You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2018/08/14 19:51:42 UTC

[airavata-django-portal] branch master updated (925f4d2 -> 15dab89)

This is an automated email from the ASF dual-hosted git repository.

machristie pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git.


    from 925f4d2  Proxy isProgressing in ExperimentStatus
     new d119f53  AIRAVATA-2727 Applying list layout to Group Resource Profiles
     new 6f9a1a1  AIRAVATA-2727 Adding ids to routes so data can be fetched on reload
     new 46466fe  AIRAVATA-2727 table of compute preferences
     new 5d33839  Preserving 'next' when logging in via redirect
     new e92c7ba  AIRAVATA-2727 Compute Preference editor plus policy editor
     new b57d9da  AIRAVATA-2727 Implement update of group compute pref
     new 2556b2e  AIRAVATA-2727 Added cancel button to compute preference page
     new b9e2c8f  AIRAVATA-2727 GroupResourceProfile save and cancel buttons
     new 4fedab0  AIRAVATA-2727 Add GroupResourceProfile and comp prefs
     new 182d9e7  AIRAVATA-2727 Routes for new GroupResourceProfile, ComputePrefs
     new 2089276  AIRAVATA-2727 Removing compute preferences
     new cb1e525  AIRAVATA-2727 Deleting GroupResourceProfiles
     new 15dab89  AIRAVATA-2727 Load state from compute preference URL path params

The 13 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 django_airavata/apps/admin/package-lock.json       |   5 +
 django_airavata/apps/admin/package.json            |   7 +-
 .../build/webpack.base.conf.js                     |   7 +-
 .../BatchQueueResourcePolicy.vue                   |  83 ++--
 .../ComputePreference.vue                          | 426 ++++++++++-----------
 .../ComputePreferences.vue                         |  55 ---
 .../GroupComputeResourcePreference.vue             | 402 ++++++++-----------
 .../ComputeResourcePreferenceDashboard.vue         | 132 +++----
 .../admin/static/django_airavata_admin/src/main.js |   4 +-
 .../static/django_airavata_admin/src/router.js     |  16 +-
 .../apps/admin/templates/admin/admin_base.html     |   2 +-
 django_airavata/apps/api/serializers.py            |  44 +++
 .../api/static/django_airavata_api/js/index.js     |  13 +-
 .../js/models/GroupComputeResourcePreference.js    |   6 +-
 .../js/models/GroupResourceProfile.js              |  83 +++-
 .../django_airavata_api/js/service_config.js       |   8 +-
 .../js/services/ComputeResourceService.js          |  56 ---
 django_airavata/apps/api/views.py                  |  24 +-
 django_airavata/apps/auth/backends.py              |   5 +-
 .../auth/templates/django_airavata_auth/login.html |  10 +-
 django_airavata/apps/auth/views.py                 |   9 +-
 .../ComputationalResourceSchedulingEditor.vue      |   2 +-
 django_airavata/static/common/js/index.js          |   7 +
 .../static/common/js/layouts/ListLayout.vue        |  74 ++++
 django_airavata/static/common/scss/main.scss       |   6 +
 25 files changed, 783 insertions(+), 703 deletions(-)
 delete mode 100644 django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreferences.vue
 delete mode 100644 django_airavata/apps/api/static/django_airavata_api/js/services/ComputeResourceService.js
 create mode 100644 django_airavata/static/common/js/layouts/ListLayout.vue


[airavata-django-portal] 04/13: Preserving 'next' when logging in via redirect

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 5d338393d84c25004882424f37ba8843150f68e6
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Fri Aug 10 10:39:20 2018 -0400

    Preserving 'next' when logging in via redirect
---
 django_airavata/apps/auth/backends.py                          |  5 ++---
 .../apps/auth/templates/django_airavata_auth/login.html        | 10 ++++++++--
 django_airavata/apps/auth/views.py                             |  9 ++++++---
 3 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/django_airavata/apps/auth/backends.py b/django_airavata/apps/auth/backends.py
index 17d37f0..3d7326a 100644
--- a/django_airavata/apps/auth/backends.py
+++ b/django_airavata/apps/auth/backends.py
@@ -69,18 +69,17 @@ class KeycloakBackend(object):
 
     def _get_token_and_userinfo_redirect_flow(self, request):
         authorization_code_url = request.build_absolute_uri()
-        redirect_url = request.build_absolute_uri(
-            reverse('django_airavata_auth:callback'))
         client_id = settings.KEYCLOAK_CLIENT_ID
         client_secret = settings.KEYCLOAK_CLIENT_SECRET
         token_url = settings.KEYCLOAK_TOKEN_URL
         userinfo_url = settings.KEYCLOAK_USERINFO_URL
         verify_ssl = settings.KEYCLOAK_VERIFY_SSL
         state = request.session['OAUTH2_STATE']
+        redirect_uri = request.session['OAUTH2_REDIRECT_URI']
         logger.debug("state={}".format(state))
         oauth2_session = OAuth2Session(client_id,
                                        scope='openid',
-                                       redirect_uri=redirect_url,
+                                       redirect_uri=redirect_uri,
                                        state=state)
         if hasattr(settings, 'KEYCLOAK_CA_CERTFILE'):
             oauth2_session.verify = settings.KEYCLOAK_CA_CERTFILE
diff --git a/django_airavata/apps/auth/templates/django_airavata_auth/login.html b/django_airavata/apps/auth/templates/django_airavata_auth/login.html
index ede2663..80abbee 100644
--- a/django_airavata/apps/auth/templates/django_airavata_auth/login.html
+++ b/django_airavata/apps/auth/templates/django_airavata_auth/login.html
@@ -10,8 +10,14 @@
                 <div class="card-body">
                     <h5 class="card-title">Log in with your existing organizational login</h5>
                     {% for external in options.external %}
-                    <a href="{% url 'django_airavata_auth:redirect_login' external.idp_alias %}" class="btn btn-primary btn-block mt-3">
-                        Sign in with {{ external.name }}
+                    <a
+                    {% if next %}
+                        href="{% url 'django_airavata_auth:redirect_login' external.idp_alias %}?next={{ next | urlencode }}"
+                    {% else %}
+                        href="{% url 'django_airavata_auth:redirect_login' external.idp_alias %}"
+                    {% endif %}
+                        class="btn btn-primary btn-block mt-3">
+                            Sign in with {{ external.name }}
                     </a>
                     {% endfor %}
                 </div>
diff --git a/django_airavata/apps/auth/views.py b/django_airavata/apps/auth/views.py
index 0c7f719..39f9213 100644
--- a/django_airavata/apps/auth/views.py
+++ b/django_airavata/apps/auth/views.py
@@ -21,15 +21,18 @@ def redirect_login(request, idp_alias):
     _validate_idp_alias(idp_alias)
     client_id = settings.KEYCLOAK_CLIENT_ID
     base_authorize_url = settings.KEYCLOAK_AUTHORIZE_URL
+    redirect_uri = request.build_absolute_uri(
+        reverse('django_airavata_auth:callback'))
+    if 'next' in request.GET:
+        redirect_uri += "?next=" + quote(request.GET['next'])
     oauth2_session = OAuth2Session(
-        client_id, scope='openid',
-        redirect_uri=request.build_absolute_uri(
-            reverse('django_airavata_auth:callback')))
+        client_id, scope='openid', redirect_uri=redirect_uri)
     authorization_url, state = oauth2_session.authorization_url(
         base_authorize_url)
     authorization_url += '&kc_idp_hint=' + quote(idp_alias)
     # Store state in session for later validation (see backends.py)
     request.session['OAUTH2_STATE'] = state
+    request.session['OAUTH2_REDIRECT_URI'] = redirect_uri
     return redirect(authorization_url)
 
 


[airavata-django-portal] 09/13: AIRAVATA-2727 Add GroupResourceProfile and comp prefs

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 4fedab0ce358985edf1d9a5bb42e2de1ab55e628
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Mon Aug 13 17:12:54 2018 -0400

    AIRAVATA-2727 Add GroupResourceProfile and comp prefs
---
 .../ComputePreference.vue                          | 26 +++++----
 .../GroupComputeResourcePreference.vue             | 68 +++++++++++++++++++---
 .../api/static/django_airavata_api/js/index.js     |  6 +-
 .../js/models/GroupComputeResourcePreference.js    |  6 +-
 .../django_airavata_api/js/service_config.js       |  2 -
 .../js/services/ComputeResourceService.js          | 56 ------------------
 6 files changed, 85 insertions(+), 79 deletions(-)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
index 87c72db..d3a1491 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
@@ -34,7 +34,8 @@
           <div class="card-body">
             <h5 class="card-title">Policy</h5>
             <b-form-group label="Allowed Queues">
-              <div v-for="batchQueue in computeResource.batchQueues" :key="batchQueue.queueName">
+              <div v-for="batchQueue in computeResource.batchQueues" :key="batchQueue.queueName"
+                  v-if="localComputeResourcePolicy">
                 <b-form-checkbox :checked="localComputeResourcePolicy.allowedBatchQueues.includes(batchQueue.queueName)"
                   @input="batchQueueChecked(batchQueue, $event)">
                   {{ batchQueue.queueName }}
@@ -94,9 +95,17 @@
         // TODO: load the Group Resource Profile and get the compute preferences for this host_id
       }
       if (this.host_id) {
-        this.fetchComputeResource(this.host_id);
-      } else {
-        this.fetchComputeResources();
+        const computeResourceOperation = this.fetchComputeResource(this.host_id);
+        // If no computeResourcePolicy create a new default one that allows all queues
+        if (!this.computeResourcePolicy) {
+          computeResourceOperation.then(computeResource => {
+            const defaultComputeResourcePolicy = new models.ComputeResourcePolicy();
+            defaultComputeResourcePolicy.computeResourceId = this.host_id;
+            defaultComputeResourcePolicy.groupResourceProfileId = this.id;
+            defaultComputeResourcePolicy.allowedBatchQueues = computeResource.batchQueues.map(queue => queue.queueName);
+            this.localComputeResourcePolicy = defaultComputeResourcePolicy;
+          })
+        }
       }
     },
     data: function () {
@@ -115,9 +124,6 @@
     },
     mixins: [VModelMixin],
     methods: {
-      fetchComputeResources: function () {
-        return DjangoAiravataAPI.utils.FetchUtils.get('/api/compute-resources/all_names_list').then((value) => this.computeResources = value);
-      },
       batchQueueChecked: function(batchQueue, checked) {
         if (checked) {
           this.localComputeResourcePolicy.allowedBatchQueues.push(batchQueue.queueName);
@@ -151,8 +157,8 @@
         }
       },
       fetchComputeResource: function (id) {
-        DjangoAiravataAPI.utils.FetchUtils.get("/api/compute-resources/" + encodeURIComponent(id) + "/").then(value => {
-          this.computeResource = value;
+        return DjangoAiravataAPI.utils.FetchUtils.get("/api/compute-resources/" + encodeURIComponent(id) + "/").then(value => {
+          return this.computeResource = value;
         });
       },
       save: function() {
@@ -192,7 +198,7 @@
         }
       },
       cancel: function() {
-        this.$router.back();
+        this.$router.push({ name: 'group_resource_preference', params: {id: this.id}});
       }
     },
   }
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
index e0b182f..46b3cd3 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
@@ -42,39 +42,51 @@
             <b-button class="ml-2" variant="secondary" @click="cancel">Cancel</b-button>
         </div>
     </div>
+    <b-modal id="modal-select-compute-resource" ref="modalSelectComputeResource" title="Select Compute Resource"
+      @ok="onSelectComputeResource" :ok-disabled="modalSelectComputeResourceOkDisabled">
+      <b-form-select v-model="selectedComputeResource" :options="computeResourceOptions">
+        <template slot="first">
+          <option :value="null">Please select compute resource</option>
+        </template>
+      </b-form-select>
+    </b-modal>
   </div>
 </template>
 <script>
   import {components as comps, layouts} from 'django-airavata-common-ui'
-  import DjangoAiravataAPI from 'django-airavata-api'
+  import {models, services} from 'django-airavata-api'
 
   export default {
     name: "group-compute-resource-preference",
     props: {
       value: {
-        type: DjangoAiravataAPI.models.GroupResourceProfile,
+        type: models.GroupResourceProfile,
         default: function () {
-          return new DjangoAiravataAPI.models.GroupResourceProfile()
+          return new models.GroupResourceProfile()
         }
       },
+      id: {
+        type: String,
+      },
     },
     mounted: function () {
       if (this.value.groupResourceProfileId) {
-        DjangoAiravataAPI.services.ServiceFactory.service("SharedEntities").retrieve({lookup: this.value.groupResourceProfileId})
+        services.ServiceFactory.service("SharedEntities").retrieve({lookup: this.value.groupResourceProfileId})
           .then(sharedEntity => this.sharedEntity = sharedEntity);
-      } else if (this.$route.params.id) {
-        // TODO: switch to using props to get the id param
-        DjangoAiravataAPI.services.ServiceFactory.service("GroupResourceProfiles").retrieve({lookup: this.$route.params.id})
+      } else if (this.id) {
+        services.ServiceFactory.service("GroupResourceProfiles").retrieve({lookup: this.id})
           .then(grp => this.data = grp);
-        DjangoAiravataAPI.services.ServiceFactory.service("SharedEntities").retrieve({lookup: this.$route.params.id})
+        services.ServiceFactory.service("SharedEntities").retrieve({lookup: this.id})
           .then(sharedEntity => this.sharedEntity = sharedEntity);
       }
+      services.ComputeResourceService.namesList()
+        .then(names => this.computeResources = names);
     },
     data: function () {
       let data = this.value.clone();
       return {
         data: data,
-        service: DjangoAiravataAPI.services.ServiceFactory.service("GroupResourceProfiles"),
+        service: services.ServiceFactory.service("GroupResourceProfiles"),
         sharedEntity: null,
         computePreferencesFields: [
           {
@@ -88,6 +100,8 @@
             key: 'action',
           },
         ],
+        computeResources: null,
+        selectedComputeResource: null,
       }
     },
 
@@ -95,6 +109,24 @@
       "share-button": comps.ShareButton,
       "list-layout": layouts.ListLayout,
     },
+    computed: {
+      modalSelectComputeResourceOkDisabled: function() {
+        return this.selectedComputeResource == null;
+      },
+      computeResourceOptions: function() {
+        const currentPrefs = this.data.computePreferences ? this.data.computePreferences.map(computePreference => computePreference.computeResourceId) : [];
+        const options = this.computeResources ? this.computeResources
+          .filter(comp => !currentPrefs.includes(comp.host_id))
+          .map(comp => {
+            return {
+              value: comp.host_id,
+              text: comp.host 
+            }
+          }) : [];
+        options.sort((a, b) => a.text.toLowerCase().localeCompare(b.text.toLowerCase()));
+        return options;
+      },
+    },
     methods: {
       saveGroupResourceProfile: function () {
         var persist = null;
@@ -133,6 +165,24 @@
       },
       cancel: function() {
         this.$router.push('/group-resource-profiles');
+      },
+      createComputePreference: function() {
+        this.$refs.modalSelectComputeResource.show();
+      },
+      onSelectComputeResource: function() {
+        const computeResourcePreference = new models.GroupComputeResourcePreference();
+        const computeResourceId = this.selectedComputeResource;
+        computeResourcePreference.computeResourceId = computeResourceId;
+        this.$router.push({
+          name: 'compute_preference', params: {
+            value: computeResourcePreference,
+            id: this.data.groupResourceProfileId,
+            host_id: computeResourceId,
+            groupResourceProfile: this.data,
+            computeResourcePolicy: null,
+            batchQueueResourcePolicies: null,
+          }
+        });
       }
     },
   }
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/index.js b/django_airavata/apps/api/static/django_airavata_api/js/index.js
index 1db9ad4..bb428d1 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/index.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/index.js
@@ -3,11 +3,13 @@ import ApplicationInterfaceDefinition from './models/ApplicationInterfaceDefinit
 import ApplicationModule from './models/ApplicationModule'
 import BatchQueue from './models/BatchQueue'
 import BatchQueueResourcePolicy from './models/BatchQueueResourcePolicy'
+import ComputeResourcePolicy from './models/ComputeResourcePolicy'
 import DataType from './models/DataType'
 import Experiment from './models/Experiment'
 import ExperimentState from './models/ExperimentState'
 import FullExperiment from './models/FullExperiment'
 import Group from './models/Group'
+import GroupComputeResourcePreference from './models/GroupComputeResourcePreference'
 import GroupPermission from './models/GroupPermission'
 import GroupResourceProfile from './models/GroupResourceProfile'
 import InputDataObjectType from './models/InputDataObjectType'
@@ -45,11 +47,13 @@ exports.models = {
     ApplicationModule,
     BatchQueue,
     BatchQueueResourcePolicy,
+    ComputeResourcePolicy,
     DataType,
     Experiment,
     ExperimentState,
     FullExperiment,
     Group,
+    GroupComputeResourcePreference,
     GroupPermission,
     GroupResourceProfile,
     InputDataObjectType,
@@ -71,7 +75,7 @@ exports.services = {
     GroupService,
     GroupResourceProfileService,
     UserProfileService,
-    ComputeResourceService,
+    ComputeResourceService: ServiceFactory.service("ComputeResources"),
     CloudJobSubmissionService,
     GlobusJobSubmissionService,
     LocaJobSubmissionService,
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupComputeResourcePreference.js b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupComputeResourcePreference.js
index 3a69495..1dacbc5 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupComputeResourcePreference.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupComputeResourcePreference.js
@@ -4,7 +4,11 @@ import BaseModel from './BaseModel'
 const FIELDS = [
      'computeResourceId',
      'groupResourceProfileId',
-     'overridebyAiravata',
+     {
+         name: 'overridebyAiravata',
+         type: 'boolean',
+         default: true,
+     },
      'loginUserName',
      'preferredJobSubmissionProtocol',
      'preferredDataMovementProtocol',
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
index ae7b0cf..86074ed 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
@@ -51,12 +51,10 @@ export default {
             name: "names",
             url: "/api/compute-resources/all_names/",
             requestType: 'get',
-            modelClass: Object,
         }, {
             name: "namesList",
             url: "/api/compute-resources/all_names_list/",
             requestType: 'get',
-            modelClass: Array,
         }],
         modelClass: ApplicationDeploymentDescription,
     },
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/services/ComputeResourceService.js b/django_airavata/apps/api/static/django_airavata_api/js/services/ComputeResourceService.js
deleted file mode 100644
index 1bf029c..0000000
--- a/django_airavata/apps/api/static/django_airavata_api/js/services/ComputeResourceService.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import FetchUtils from '../utils/FetchUtils'
-
-let defaultValue = {
-    hostName: 'Hi',
-    hostAliases: [''],
-    ipAddresses: [''],
-    resourceDescription: '',
-    maxMemoryPerNode: 0,
-    batchQueues: [{
-        queueDescription: '',
-        maxRunTime: 0,
-        maxNodes: 0,
-        maxProcessors: 0,
-        maxJobsInQueue: 0,
-        maxMemory: 0,
-        cpuPerNode: 0,
-        defaultNodeCount: 0,
-        defaultCPUCount: 0,
-        defaultWalltime: 0,
-        queueSpecificMacros: '',
-        isDefaultQueue: false
-    }],
-    fileSystems: {
-        0: '',
-        1: '',
-        2: '',
-        3: '',
-        4: ''
-    },
-    jobSubmissionInterfaces: [],
-    dataMovementInterfaces: [{
-        dataMovementInterfaceId: 0,
-        dataMovementProtocol: 3,
-        priorityOrder: null
-    }],
-    gatewayUsageReporting: false,
-    gatewayUsageModuleLoadCommand: null,
-    gatewayUsageExecutable: null
-}
-
-class ComputeResourceService {
-    constructor() {
-        this.data=null
-    }
-
-    list() {
-        return FetchUtils.get('/api/compute-resources/all_names_list')
-    }
-
-     retrieve(id) {
-        this.data=null
-        return  FetchUtils.get('/api/compute-resource/' + encodeURIComponent(id) + '/')
-    }
-}
-
-export default new ComputeResourceService()
\ No newline at end of file


[airavata-django-portal] 03/13: AIRAVATA-2727 table of compute preferences

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 46466fe6d191de61ad12d072bcb54bc4f3ee46d1
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Thu Aug 9 16:46:24 2018 -0400

    AIRAVATA-2727 table of compute preferences
---
 .../GroupComputeResourcePreference.vue             | 292 +++++----------------
 .../js/models/GroupResourceProfile.js              |  28 ++
 .../django_airavata_api/js/service_config.js       |   6 +-
 3 files changed, 95 insertions(+), 231 deletions(-)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
index 32a3094..62d57e7 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
@@ -1,73 +1,56 @@
 <template>
-  <div class="new_app">
-    <div class="new_app_header">
-      <h3 style="display: inline-block">Group Resource Profile</h3>
-      <div class="new-application-tab-main">
-        <div class="entry">
-          <div class="heading">Name</div>
-          <input type="text" v-model="data.groupResourceProfileName"/>
-        </div>
-        <div class="entry">
-          <share-button ref="shareButton" v-model="sharedEntity"/>
-        </div>
+  <div>
+    <div class="row">
+      <div class="col">
+        <h1 class="h4 mb-4">Group Resource Profile</h1>
       </div>
-      <div class="new-application-tab-main">
-        <h4>Compute Preferences</h4>
-        <div>
-          <a class="list-item" v-for="computePreference,index in data.computePreferences" v-bind:key="index"
-             v-on:click="computePreferenceClickHandler(computePreference.computeResourceId)">
-            <span v-if="computePreference.groupResourceProfileId">
-              {{getComputeResourceName(computePreference.computeResourceId)}}
-            </span>
-            <span v-else class="un-saved">
-              Un Saved Compute Preference {{index}}
-            </span>
-
-            <img v-on:click.stop="data.computePreferences.splice(index,1)" src="/static/images/delete.png"/>
-          </a>
-        </div>
-        <div class="entry">
-          <button class="interface-btn" v-on:click="createComputePreferenceClickHandler()">Add Compute
-            <span>Preference</span>
-          </button>
+    </div>
+    <div class="row">
+      <div class="col">
+        <div class="card">
+          <div class="card-body">
+            <b-form-group label="Name" label-for="profile-name">
+              <b-form-input id="profile-name" type="text"
+                v-model="data.groupResourceProfileName"
+                required placeholder="Name of this Group Resource Profile">
+              </b-form-input>
+            </b-form-group>
+            <share-button ref="shareButton" v-model="sharedEntity"/>
+          </div>
         </div>
-        <tab-action-console v-bind:sectionName="'Group Resource Profile'" v-bind:save="saveGroupResourceProfile"
-                            v-bind:enableCancel="false"></tab-action-console>
       </div>
     </div>
+    <list-layout :items="data.computePreferences" title="Compute Preferences"
+      new-item-button-text="New Compute Preference"
+      @add-new-item="createComputePreferences">
+      <template slot="item-list" slot-scope="slotProps">
+
+        <b-table hover :fields="computePreferencesFields" :items="slotProps.items"
+          sort-by="computeResourceId">
+          <template slot="action" slot-scope="data">
+            <a href="#" @click.prevent="computePreferenceClickHandler(data.item.computeResourceId)">
+              Edit
+              <i class="fa fa-edit" aria-hidden="true"></i>
+            </a>
+          </template>
+        </b-table> 
+      </template>
+    </list-layout>
   </div>
 </template>
 <script>
-  const ComputePreference = () => import('./ComputePreference');
-  import TabActionConsole from '../TabActionConsole'
-  import {components as comps} from 'django-airavata-common-ui'
+  import {components as comps, layouts} from 'django-airavata-common-ui'
   import DjangoAiravataAPI from 'django-airavata-api'
 
   export default {
     name: "group-compute-resource-preference",
     props: {
       value: {
-        type: Object,
+        type: DjangoAiravataAPI.models.GroupResourceProfile,
         default: function () {
-          return {
-            computePreferences: [],
-            computeResourcePolicies: [],
-            batchQueueResourcePolicies: [],
-            groupResourceProfileName: null,
-            creationTime: null,
-            updatedTime: null,
-            groupResourceProfileId: null
-          }
+          return new DjangoAiravataAPI.models.GroupResourceProfile()
         }
       },
-      newCreation: {
-        type: Boolean,
-        default: false
-      },
-      transform: {
-        type: Boolean,
-        default: true
-      }
     },
     mounted: function () {
       if (this.value.groupResourceProfileId) {
@@ -75,141 +58,49 @@
           .then(sharedEntity => this.sharedEntity = sharedEntity);
       } else if (this.$route.params.id) {
         // TODO: switch to using props to get the id param
-        DjangoAiravataAPI.services.ServiceFactory.service("GroupResourcePreference").retrieve({lookup: this.$route.params.id})
-          .then(grp => this.data = this.transformData(grp));
+        DjangoAiravataAPI.services.ServiceFactory.service("GroupResourceProfiles").retrieve({lookup: this.$route.params.id})
+          .then(grp => this.data = grp);
         DjangoAiravataAPI.services.ServiceFactory.service("SharedEntities").retrieve({lookup: this.$route.params.id})
           .then(sharedEntity => this.sharedEntity = sharedEntity);
       }
     },
     data: function () {
-      let data = Object.assign({},this.value);
-      if (this.transform) {
-        data = this.transformData(data);
-      }
+      let data = this.value.clone();
       return {
         data: data,
-        service: DjangoAiravataAPI.services.ServiceFactory.service("GroupResourcePreference"),
+        service: DjangoAiravataAPI.services.ServiceFactory.service("GroupResourceProfiles"),
         sharedEntity: null,
-
+        computePreferencesFields: [
+          {
+            label: 'Name',
+            key: 'computeResourceId',
+            sortable: true,
+            formatter: (value) => this.getComputeResourceName(value),
+          },
+          {
+            label: 'Action',
+            key: 'action',
+          },
+        ],
       }
     },
 
     components: {
-      ComputePreference,
-      "auto-complete": comps.Autocomplete,
-      TabActionConsole,
       "share-button": comps.ShareButton,
+      "list-layout": layouts.ListLayout,
     },
     methods: {
-      transformData: function (groupResourceProfile) {
-        let computePreferences=groupResourceProfile.computePreferences;
-        console.log("Transform Compute prefernces",computePreferences.length,groupResourceProfile);
-        for (let computePreference of computePreferences) {
-          let groupResourceProfileId = computePreference.groupResourceProfileId;
-          let computeResourceId = computePreference.computeResourceId;
-          let computeResourcePolicies = []
-          console.log("Transforming   Group Resource Profile ID, Compute Resource ID", groupResourceProfileId, computeResourceId)
-          for (let computeResourcePolicy of groupResourceProfile.computeResourcePolicies) {
-            let resourcePolicyId = computeResourcePolicy.resourcePolicyId;
-            console.log("policy Group Resource Profile ID, Compute Resource ID Resource Policy", computeResourcePolicy.groupResourceProfileId, computeResourcePolicy.computeResourceId, resourcePolicyId)
-            if (groupResourceProfileId == computeResourcePolicy.groupResourceProfileId && computeResourceId == computeResourcePolicy.computeResourceId) {
-              let computeResourcePolicyTemp = computeResourcePolicy;
-              let batchQueueResourcePolicies = [];
-              for (let batchQueueResourcePolicy of groupResourceProfile.batchQueueResourcePolicies) {
-                console.log("batch policy Group Resource Profile ID, Compute Resource ID Resource Policy", batchQueueResourcePolicy.groupResourceProfileId, batchQueueResourcePolicy.computeResourceId, batchQueueResourcePolicy.resourcePolicyId)
-                if (groupResourceProfileId == batchQueueResourcePolicy.groupResourceProfileId && computeResourceId == batchQueueResourcePolicy.computeResourceId) {
-                  batchQueueResourcePolicies.push(batchQueueResourcePolicy);
-                }
-              }
-              console.log("Batch Queue Rsource Policies for", batchQueueResourcePolicies.length, computeResourcePolicy.computeResourceId);
-              computeResourcePolicyTemp.batchQueueResourcePolicies = batchQueueResourcePolicies;
-              computeResourcePolicies.push(computeResourcePolicyTemp);
-            }
-          }
-          computePreference.computeResourcePolicies = computeResourcePolicies;
-        }
-        groupResourceProfile.computePreferences=computePreferences;
-        return groupResourceProfile;
-      },
-      createComputePreferences: function () {
-        let computeResourcePreference = {
-          computeResourceId: null,
-          groupResourceProfileId: null,
-          overridebyAiravata: true,
-          loginUserName: null,
-          preferredJobSubmissionProtocol: null,
-          preferredDataMovementProtocol: null,
-          preferredBatchQueue: null,
-          scratchLocation: null,
-          allocationProjectNumber: null,
-          resourceSpecificCredentialStoreToken: null,
-          usageReportingGatewayId: null,
-          qualityOfService: null,
-          reservation: null,
-          reservationStartTime: null,
-          reservationEndTime: null,
-          sshAccountProvisiogroupSSHAccountProvisionerConfigsner: null,
-          sshAccountProvisionerAdditionalInfo: null,
-          groupSSHAccountProvisionerConfigs: [],
-          computeResourcePolicies: []
-        };
-        this.data.computePreferences.push(computeResourcePreference);
-      }
-      ,
-      createComputePreferenceClickHandler: function () {
-        this.createComputePreferences();
-        this.computePreferenceClickHandler(this.data.computePreferences.length - 1);
-      },
-      saveGroupResourceProfile: function (callback) {
-        let groupResourceProfile = Object.assign({}, this.data);
-        let computePreferences = groupResourceProfile.computePreferences;
-        let batchQueueResourcePolicies = [];
-        let computeResourcePolicies = [];
-        if (computePreferences) {
-          for (let computePreference of computePreferences) {
-            computePreference.groupResourceProfileId = groupResourceProfile.groupResourceProfileId;
-            if (!computePreference.computeResourcePolicies) {
-              console.log("Compute Resource Policies empty", computePreference);
-            }
-            for (let computeResourcePolicy of computePreference.computeResourcePolicies) {
-              if (!computeResourcePolicy.batchQueueResourcePolicies) {batchQueueResourcePolicies
-                console.log("batchQueueResourcePolicies empty", computePreference);
-              }
-              computeResourcePolicy.groupResourceProfileId=groupResourceProfile.groupResourceProfileId;
-              for (let batchQueueResourcePolicy of computeResourcePolicy.batchQueueResourcePolicies) {
-                batchQueueResourcePolicy.computeResourceId = computePreference.computeResourceId;
-                batchQueueResourcePolicy.groupResourceProfileId=groupResourceProfile.groupResourceProfileId;
-                batchQueueResourcePolicies.push(batchQueueResourcePolicy);
-              }
-              delete computeResourcePolicy.batchQueueResourcePolicies;
-              computeResourcePolicies.push(computeResourcePolicy);
-            }
-            delete computePreference.computeResourcePolicies;
-            delete computePreference.batchQueueResourcePolicies;
-          }
-        } else {
-          groupResourceProfile.computePreferences = [];
-        }
-        groupResourceProfile.computeResourcePolicies = computeResourcePolicies;
-        groupResourceProfile.batchQueueResourcePolicies = batchQueueResourcePolicies;
-        console.log("Saving..", groupResourceProfile);
+      saveGroupResourceProfile: function () {
         if (this.data.groupResourceProfileId) {
-          DjangoAiravataAPI.utils.FetchUtils.put('/api/group-resource-profiles/' + this.data.groupResourceProfileId + '/', groupResourceProfile)
-            .then(callback.failure).then((data) => {
-            console.log("Completed")
-            if (data) {
-              this.data = this.transformData(data);
-            }
-          });
+          DjangoAiravataAPI.utils.FetchUtils.put('/api/group-resource-profiles/' + this.data.groupResourceProfileId + '/', this.data)
+            .then((data) => this.data = data);
         } else {
-          this.service.create({data: groupResourceProfile}).then(callback.success, callback.failure).then((data) => {
-            console.log("Completed")
-            if (data) {
-              this.data = this.transformData(data);
-            }
-            // Save sharing settings too
-            return this.$refs.shareButton.mergeAndSave(data.groupResourceProfileId);
-          });
+          this.service.create({data: this.data})
+            .then((data) => {
+              this.data = data;
+              // Save sharing settings too
+              return this.$refs.shareButton.mergeAndSave(data.groupResourceProfileId);
+            });
         }
       },
       computePreferenceClickHandler: function (computeResourceId) {
@@ -227,62 +118,5 @@
         return (computeResourceId && computeResourceId.indexOf("_") > 0) ? computeResourceId.split("_")[0] : computeResourceId;
       },
     },
-    watch: {
-      'data.groupResourceProfileId': function (newValue) {
-        let computePreferences = this.data.computePreferences;
-        for (let computePreference of computePreferences) {
-          computePreference.groupResourceProfileId = newValue;
-          for (let groupSSHAccountProvisionerConfig of computePreference.groupSSHAccountProvisionerConfigs) {
-            groupSSHAccountProvisionerConfig.groupResourceProfileId = newValue;
-          }
-        }
-      }
-    }
-  }
-</script>
-
-<style scoped>
-  .list-item {
-    color: #007BFF;
-    border: solid 1px #007BFF;
-    background-color: white;
-    border-top: none;
-    text-align: center;
-    padding-top: 5px;
-    padding-bottom: 5px;
-    padding-left: 15px;
-    padding-right: 15px;
-    display: block;
-
-  }
-
-  .list-item:first-child {
-    border-top-left-radius: 4px;
-    border-top-right-radius: 4px;
-    border-top: solid 1px #007BFF;
-  }
-
-  .list-item:last-child {
-    border-bottom-left-radius: 4px;
-    border-bottom-right-radius: 4px;
   }
-
-  .list-item img {
-    float: right;
-  }
-
-  .list-item:hover {
-    color: white;
-    background-color: #007BFF;
-    cursor: pointer;
-  }
-
-  .list-item:hover span {
-    color: white;
-  }
-
-  .un-saved {
-    color: red;
-  }
-
-</style>
+</script>
\ No newline at end of file
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
index ab3f1de..e24045a 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
@@ -38,4 +38,32 @@ export default class GroupResourceProfile extends BaseModel {
     constructor(data = {}) {
         super(FIELDS, data);
     }
+
+    getComputeResourcePolicy(computeResourceId) {
+        return this.computeResourcePolicies.find(pol => pol.computeResourceId === computeResourceId);
+    }
+
+    setComputeResourcePolicy(computeResourcePolicy) {
+        const currentPolicy = this.getComputeResourcePolicy(computeResourcePolicy.computeResourceId);
+        if (currentPolicy) {
+            Object.assign(currentPolicy, computeResourcePolicy);
+        } else {
+            this.computeResourcePolicies.push(computeResourcePolicy);
+        }
+    }
+
+    getBatchQueueResourcePolicies(computeResourceId) {
+        return this.batchQueueResourcePolicies.filter(pol => pol.computeResourceId === computeResourceId);
+    }
+
+    setComputeResourcePolicy(batchQueueResourcePolicies) {
+        for (let newPolicy of batchQueueResourcePolicies) {
+            const currentPolicy = this.batchQueueResourcePolicies.find(pol => pol.resourcePolicyId === newPolicy.resourcePolicyId);
+            if (currentPolicy) {
+                Object.assign(currentPolicy, newPolicy);
+            } else {
+                this.batchQueueResourcePolicies.push(newPolicy);
+            }
+        }
+    }
 }
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
index 3b0d6c4..ae7b0cf 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
@@ -1,5 +1,6 @@
 import ApplicationDeploymentDescription from './models/ApplicationDeploymentDescription'
 import Group from './models/Group'
+import GroupResourceProfile from './models/GroupResourceProfile'
 import SharedEntity from './models/SharedEntity'
 import UserProfile from './models/UserProfile'
 
@@ -59,9 +60,10 @@ export default {
         }],
         modelClass: ApplicationDeploymentDescription,
     },
-    "GroupResourcePreference": {
+    "GroupResourceProfiles": {
         url: "/api/group-resource-profiles/",
-        viewSet: true
+        viewSet: true,
+        modelClass: GroupResourceProfile,
     },
     "Groups": {
         url: "/api/groups",


[airavata-django-portal] 06/13: AIRAVATA-2727 Implement update of group compute pref

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit b57d9da95db8f947660f200f99ddf18873cab9cf
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Mon Aug 13 13:05:13 2018 -0400

    AIRAVATA-2727 Implement update of group compute pref
---
 .../ComputePreference.vue                          | 135 +++++++--------------
 .../GroupComputeResourcePreference.vue             |   1 +
 django_airavata/apps/api/serializers.py            |  16 +++
 .../js/models/GroupResourceProfile.js              |  37 ++++++
 django_airavata/apps/api/views.py                  |  10 +-
 .../static/common/js/layouts/ListLayout.vue        |  10 +-
 6 files changed, 112 insertions(+), 97 deletions(-)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
index c83863c..35ad166 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
@@ -50,6 +50,11 @@
         </div>
       </div>
     </div>
+    <div class="row">
+        <div class="col d-flex justify-content-end">
+            <b-button variant="primary" @click="save">Save</b-button>
+        </div>
+    </div>
   </div>
 </template>
 
@@ -73,6 +78,9 @@
       host_id: {
         type: String,
       },
+      groupResourceProfile: {
+        type: models.GroupResourceProfile,
+      },
       computeResourcePolicy: {
         type: models.ComputeResourcePolicy
       },
@@ -98,50 +106,6 @@
         selectedComputeResourceIndex: null,
         localComputeResourcePolicy: this.computeResourcePolicy ? this.computeResourcePolicy.clone() : null,
         localBatchQueueResourcePolicies: this.batchQueueResourcePolicies ? this.batchQueueResourcePolicies.map(pol => pol.clone()) : [],
-        dataMovementProtocols: [{
-          name: "LOCAL",
-          enabled: false,
-          value: 0
-        }, {
-          name: "SCP",
-          enabled: false,
-          value: 1
-        }, {
-          name: "GridFTP",
-          enabled: false,
-          value: 2
-        }, {
-          name: "UNICORE_STORAGE_SERVICE",
-          enabled: false,
-          value: 3
-        },],
-        jobSubmissionProtocols: [
-          {
-            name: "Local",
-            enabled: false,
-            value: 0
-          },
-          {
-            name: "SSH",
-            enabled: false,
-            value: 1
-          },
-          {
-            name: "GLOBUS",
-            enabled: false,
-            value: 2
-          },
-          {
-            name: "UNICORE",
-            enabled: false,
-            value: 3
-          },
-          {
-            name: "Cloud",
-            enabled: false,
-            value: 4
-          },
-        ],
         computeResource: {
           batchQueues: [],
           jobSubmissionInterfaces: []
@@ -153,15 +117,6 @@
       fetchComputeResources: function () {
         return DjangoAiravataAPI.utils.FetchUtils.get('/api/compute-resources/all_names_list').then((value) => this.computeResources = value);
       },
-      createComputeResourcePolicy: function () {
-        return {
-          allowedBatchQueues: [],
-          batchQueueResourcePolicies: [],
-          computeResourceId: null,
-          groupResourceProfileId: null,
-          resourcePolicyId: null
-        }
-      },
       batchQueueChecked: function(batchQueue, checked) {
         if (checked) {
           this.localComputeResourcePolicy.allowedBatchQueues.push(batchQueue.queueName);
@@ -182,6 +137,9 @@
           if (existingPolicy) {
             Object.assign(existingPolicy, batchQueueResourcePolicy);
           } else {
+            // For new BatchQueueResourcePolicy instances, set the parent ids
+            batchQueueResourcePolicy.groupResourceProfileId = this.id;
+            batchQueueResourcePolicy.computeResourceId = this.host_id;
             this.localBatchQueueResourcePolicies.push(batchQueueResourcePolicy);
           }
         } else {
@@ -191,46 +149,47 @@
           }
         }
       },
-      createGroupSSHAccountProvisionerConfigs: function () {
-        this.data.groupSSHAccountProvisionerConfigs.push({
-          resourceId: this.data.computeResourceId,
-          groupResourceProfileId: this.data.groupResourceProfileId,
-          configName: null,
-          configValue: null
+      fetchComputeResource: function (id) {
+        DjangoAiravataAPI.utils.FetchUtils.get("/api/compute-resources/" + encodeURIComponent(id) + "/").then(value => {
+          this.computeResource = value;
         });
       },
-      fetchComputeResource: function (id) {
-        console.log("Fetching compute Resource", id);
-        if (id) {
-          DjangoAiravataAPI.utils.FetchUtils.get("/api/compute-resources/" + encodeURIComponent(id) + "/").then(value => {
-            console.log("Compute  Resource", value);
-            this.computeResource = value;
-            this.computeResource.jobSubmissionInterfaces.forEach((jobSubmissionInterface) => {
-              this.jobSubmissionProtocols[jobSubmissionInterface.jobSubmissionProtocol].enabled = true;
+      save: function() {
+        let groupResourceProfile = this.groupResourceProfile.clone();
+        groupResourceProfile.mergeComputeResourcePreference(this.data, this.localComputeResourcePolicy, this.localBatchQueueResourcePolicies);
+        // TODO: success and error handling are the same so we can just combine those
+        if (groupResourceProfile.groupResourceProfileId) {
+          DjangoAiravataAPI.services.ServiceFactory.service("GroupResourceProfiles").update({data: groupResourceProfile, lookup: groupResourceProfile.groupResourceProfileId})
+            .then(groupResourceProfile => {
+              // Navigate back to GroupResourceProfile with success message
+              this.$router.push({
+                name: 'group_resource_preference', params: {
+                  value: groupResourceProfile,
+                  id: groupResourceProfile.groupResourceProfileId
+                }
+              });
+            })
+            .catch(error => {
+              // TODO: handle error
+              console.log("Error occurred", error);
             });
-            this.computeResource.dataMovementInterfaces.forEach((dataMovementInterface) => {
-              this.dataMovementProtocols[dataMovementInterface.dataMovementProtocol].enabled = true;
+        } else {
+          DjangoAiravataAPI.services.ServiceFactory.service("GroupResourceProfiles").create({data: groupResourceProfile})
+            .then(groupResourceProfile => {
+              // Navigate back to GroupResourceProfile with success message
+              this.$router.push({
+                name: 'group_resource_preference', params: {
+                  value: groupResourceProfile,
+                  id: groupResourceProfile.groupResourceProfileId
+                }
+              });
+            })
+            .catch(error => {
+              // TODO: handle error
+              console.log("Error occurred", error);
             });
-          });
-        }
-      },
-      continueHandler: function () {
-        this.enablePopup = false;
-        if (this.computeResources && this.computeResources.length > 0) {
-          this.fetchComputeResource(this.computeResources[this.selectedComputeResourceIndex].host_id);
         }
       }
     },
-    watch: {}
-  }
-</script>
-
-<style scoped>
-  .batch-queue {
-    margin-top: 10px;
-  }
-
-  .popup-select {
-    height: 100%;
   }
-</style>
+</script>
\ No newline at end of file
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
index f6065c0..fd98a6e 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
@@ -112,6 +112,7 @@
             value: computeResourcePreference,
             id: this.data.groupResourceProfileId,
             host_id: computeResourceId,
+            groupResourceProfile: this.data,
             computeResourcePolicy: computeResourcePolicy,
             batchQueueResourcePolicies: batchQueueResourcePolicies,
           }
diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index 020ade4..ec8cee8 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -451,6 +451,22 @@ class GroupResourceProfileSerializer(
     updatedTime = UTCPosixTimestampDateTimeField(allow_null=True)
     userHasWriteAccess = serializers.SerializerMethodField()
 
+    def update(self, instance, validated_data):
+        result = super().update(instance, validated_data)
+        result._removed_batch_queue_resource_policies = []
+        # Find all batch queue resource policies that were removed
+        for batch_queue_resource_policy in instance.batchQueueResourcePolicies:
+            existing_batch_queue_resource_policy_for_update = next(
+                (bq for bq in result.batchQueueResourcePolicies
+                 if bq.computeResourceId ==
+                    batch_queue_resource_policy.computeResourceId
+                    and bq.queuename == batch_queue_resource_policy.queuename),
+                None)
+            if not existing_batch_queue_resource_policy_for_update:
+                result._removed_batch_queue_resource_policies.append(
+                    batch_queue_resource_policy)
+        return result
+
     def get_userHasWriteAccess(self, groupResourceProfile):
         request = self.context['request']
         return request.airavata_client.userHasAccess(
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
index e24045a..259cfcf 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
@@ -66,4 +66,41 @@ export default class GroupResourceProfile extends BaseModel {
             }
         }
     }
+
+    mergeComputeResourcePreference(computeResourcePreference, computeResourcePolicy, batchQueueResourcePolicies) {
+        // merge/add computeResourcePreference and computeResourcePolicy
+        const existingComputeResourcePreference = this.computePreferences.find(pref => pref.computeResourceId === computeResourcePreference.computeResourceId);
+        if (existingComputeResourcePreference) {
+            Object.assign(existingComputeResourcePreference, computeResourcePreference);
+        } else {
+            this.computePreferences.push(computeResourcePreference);
+        }
+        const existingComputeResourcePolicy = this.computeResourcePolicies.find(pol => pol.computeResourceId === computeResourcePolicy.computeResourceId);
+        if (existingComputeResourcePolicy) {
+            Object.assign(existingComputeResourcePolicy, computeResourcePolicy);
+        } else {
+            this.computeResourcePolicies.push(computeResourcePolicy);
+        }
+        // merge/add/remove batchQueueResourcePolicies
+        const existingBatchQueueResourcePolicies = this.batchQueueResourcePolicies.filter(pol => pol.computeResourceId === computeResourcePreference.computeResourceId);
+        for (const batchQueueResourcePolicy of batchQueueResourcePolicies) {
+            const existingBatchQueueResourcePolicy = existingBatchQueueResourcePolicies.find(pol => pol.queuename === batchQueueResourcePolicy.queuename);
+            if (existingBatchQueueResourcePolicy) {
+                Object.assign(existingBatchQueueResourcePolicy, batchQueueResourcePolicy);
+                const existingBatchQueueResourcePolicyIndex = existingBatchQueueResourcePolicies.findIndex(pol => pol.queuename === batchQueueResourcePolicy.queuename);
+                if (existingBatchQueueResourcePolicyIndex >= 0) {
+                    existingBatchQueueResourcePolicies.splice(existingBatchQueueResourcePolicyIndex, 1);
+                }
+            } else {
+                this.batchQueueResourcePolicies.push(batchQueueResourcePolicy);
+            }
+        }
+        for (const existingBatchQueueResourcePolicy of existingBatchQueueResourcePolicies) {
+            const existingBatchQueueResourcePolicyIndex = this.batchQueueResourcePolicies.findIndex(
+                pol => pol.computeResourceId === existingBatchQueueResourcePolicy.computeResourceId && pol.queuename === existingBatchQueueResourcePolicy.queuename);
+            if (existingBatchQueueResourcePolicyIndex >= 0) {
+                this.batchQueueResourcePolicies.splice(existingBatchQueueResourcePolicyIndex, 1);
+            }
+        }
+    }
 }
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index c40bf7f..2ad5ec5 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -698,7 +698,15 @@ class GroupResourceProfileViewSet(APIBackedViewSet):
         serializer.instance = group_resource_profile
 
     def perform_update(self, serializer):
-        self.request.airavata_client.updateGroupResourceProfile(self.authz_token, serializer.save())
+        grp = serializer.save()
+        for removed_batch_queue_resource_policy \
+                in grp._removed_batch_queue_resource_policies:
+            self.request.airavata_client.removeGroupBatchQueueResourcePolicy(
+                self.authz_token,
+                removed_batch_queue_resource_policy.resourcePolicyId)
+        log.debug("batch queue res policies: {}".format(grp.batchQueueResourcePolicies))
+        self.request.airavata_client.updateGroupResourceProfile(
+            self.authz_token, grp)
 
 
 class SharedEntityGroups(APIBackedViewSet):
diff --git a/django_airavata/static/common/js/layouts/ListLayout.vue b/django_airavata/static/common/js/layouts/ListLayout.vue
index 8ab1af8..a947934 100644
--- a/django_airavata/static/common/js/layouts/ListLayout.vue
+++ b/django_airavata/static/common/js/layouts/ListLayout.vue
@@ -6,7 +6,7 @@
                     <h1 class="h4 mb-4">{{ title }}</h1>
                 </slot>
             </div>
-            <div id="col-new-item" class="col">
+            <div class="col d-flex justify-content-end align-items-baseline">
                 <slot name="new-item-button">
                     <b-btn variant="primary" @click="addNewItem">
                         {{ newItemButtonText }} <i class="fa fa-plus" aria-hidden="true"></i>
@@ -71,10 +71,4 @@ export default {
         },
     },
 }
-</script>
-
-<style>
-#col-new-item {
-    text-align: right;
-}
-</style>
+</script>
\ No newline at end of file


[airavata-django-portal] 08/13: AIRAVATA-2727 GroupResourceProfile save and cancel buttons

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit b9e2c8f1f3edeaa485f407f6fae6f39b7d6f7deb
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Mon Aug 13 14:44:30 2018 -0400

    AIRAVATA-2727 GroupResourceProfile save and cancel buttons
---
 .../GroupComputeResourcePreference.vue             | 22 +++++++++++++++++-----
 django_airavata/static/common/scss/main.scss       |  5 +++++
 2 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
index fd98a6e..e0b182f 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
@@ -22,7 +22,7 @@
     </div>
     <list-layout :items="data.computePreferences" title="Compute Preferences"
       new-item-button-text="New Compute Preference"
-      @add-new-item="createComputePreferences">
+      @add-new-item="createComputePreference">
       <template slot="item-list" slot-scope="slotProps">
 
         <b-table hover :fields="computePreferencesFields" :items="slotProps.items"
@@ -36,6 +36,12 @@
         </b-table> 
       </template>
     </list-layout>
+    <div class="row">
+        <div class="col d-flex justify-content-end">
+            <b-button variant="primary" @click="saveGroupResourceProfile">Save</b-button>
+            <b-button class="ml-2" variant="secondary" @click="cancel">Cancel</b-button>
+        </div>
+    </div>
   </div>
 </template>
 <script>
@@ -91,17 +97,20 @@
     },
     methods: {
       saveGroupResourceProfile: function () {
+        var persist = null;
         if (this.data.groupResourceProfileId) {
-          DjangoAiravataAPI.utils.FetchUtils.put('/api/group-resource-profiles/' + this.data.groupResourceProfileId + '/', this.data)
-            .then((data) => this.data = data);
+          persist = this.service.update({data: this.data, lookup: this.data.groupResourceProfileId});
         } else {
-          this.service.create({data: this.data})
+          persist = this.service.create({data: this.data})
             .then((data) => {
-              this.data = data;
               // Save sharing settings too
               return this.$refs.shareButton.mergeAndSave(data.groupResourceProfileId);
             });
         }
+        // TODO: handle errors
+        persist.then(data => {
+          this.$router.push('/group-resource-profiles');
+        });
       },
       computePreferenceClickHandler: function (computeResourceId) {
         let computeResourcePreference = this.data.computePreferences.find(pref => pref.computeResourceId === computeResourceId);
@@ -122,6 +131,9 @@
         // TODO: load compute resources to get the real name
         return (computeResourceId && computeResourceId.indexOf("_") > 0) ? computeResourceId.split("_")[0] : computeResourceId;
       },
+      cancel: function() {
+        this.$router.push('/group-resource-profiles');
+      }
     },
   }
 </script>
\ No newline at end of file
diff --git a/django_airavata/static/common/scss/main.scss b/django_airavata/static/common/scss/main.scss
index 95a2cc9..76b9684 100644
--- a/django_airavata/static/common/scss/main.scss
+++ b/django_airavata/static/common/scss/main.scss
@@ -110,6 +110,11 @@ body {
     max-width: 1200px;
 }
 
+/* Add extra padding at the bottom to accommodate for the scrolling under the fixed width header */
+.main-content > :last-child {
+    padding-bottom: 70px;
+}
+
 .sidebar {
     flex-basis: 200px;
     background-color: #ffffff;


[airavata-django-portal] 01/13: AIRAVATA-2727 Applying list layout to Group Resource Profiles

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit d119f53e8cb9ff070ef8d4d63cd2ee850ad69c59
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Wed Aug 8 10:51:45 2018 -0400

    AIRAVATA-2727 Applying list layout to Group Resource Profiles
---
 django_airavata/apps/admin/package-lock.json       |   5 +
 django_airavata/apps/admin/package.json            |   7 +-
 .../build/webpack.base.conf.js                     |   7 +-
 .../ComputeResourcePreferenceDashboard.vue         | 109 +++++++--------------
 .../apps/admin/templates/admin/admin_base.html     |   2 +-
 django_airavata/apps/api/serializers.py            |   7 ++
 .../js/models/GroupResourceProfile.js              |   1 +
 django_airavata/static/common/js/index.js          |   7 ++
 .../static/common/js/layouts/ListLayout.vue        |  80 +++++++++++++++
 django_airavata/static/common/scss/main.scss       |   1 +
 10 files changed, 145 insertions(+), 81 deletions(-)

diff --git a/django_airavata/apps/admin/package-lock.json b/django_airavata/apps/admin/package-lock.json
index c4af74d..e934244 100644
--- a/django_airavata/apps/admin/package-lock.json
+++ b/django_airavata/apps/admin/package-lock.json
@@ -15025,6 +15025,11 @@
         }
       }
     },
+    "moment": {
+      "version": "2.22.2",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
+      "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
+    },
     "move-concurrently": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
diff --git a/django_airavata/apps/admin/package.json b/django_airavata/apps/admin/package.json
index 901e88a..ad5b6f7 100644
--- a/django_airavata/apps/admin/package.json
+++ b/django_airavata/apps/admin/package.json
@@ -11,14 +11,15 @@
     "watch": "cross-env WEBPACK_WATCH=true node static/django_airavata_admin/build/build.js"
   },
   "dependencies": {
+    "bootstrap": "^4.0.0",
+    "bootstrap-vue": "^1.5.1",
     "django-airavata-api": "file:../api",
     "django-airavata-common-ui": "file:../../static/common",
+    "moment": "^2.22.2",
     "vue": "^2.3.3",
     "vue-resource": "^1.3.4",
     "vue-router": "^2.7.0",
-    "vuex": "^2.4.0",
-    "bootstrap": "^4.0.0",
-    "bootstrap-vue": "^1.5.1"
+    "vuex": "^2.4.0"
   },
   "devDependencies": {
     "autoprefixer": "^7.1.3",
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/build/webpack.base.conf.js b/django_airavata/apps/admin/static/django_airavata_admin/build/webpack.base.conf.js
index 48bccd2..2aa78bb 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/build/webpack.base.conf.js
+++ b/django_airavata/apps/admin/static/django_airavata_admin/build/webpack.base.conf.js
@@ -1,4 +1,5 @@
 var path = require('path')
+var webpack = require('webpack')
 var utils = require('./utils')
 var config = require('../config')
 var vueLoaderConfig = require('./vue-loader.conf')
@@ -64,5 +65,9 @@ module.exports = {
         }
       }
     ]
-  }
+  },
+  plugins: [
+      // Exclude all but the 'en' locale from moment's locales
+      new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /^en$/),
+  ],
 }
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
index e593cf4..4c547fd 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
@@ -1,36 +1,48 @@
 <template>
-  <div class="new_app">
-    <div class="new_app_header">
-      <h4 style="display: inline-block">Group Resource Profiles</h4>
-      <label v-on:click="newGroupResourcePreference()">New Group Resource Profile <span>+</span></label>
-    </div>
-    <div class="applications">
-      <h6 style="color: #666666;">Group Resource Profile</h6>
-      <div class="container-fluid">
-        <div class="row">
-          <application-card v-for="groupResourceProfile in groupResourceProfiles"
-                            v-bind:app-module="transform(groupResourceProfile)"
-                            v-bind:key="groupResourceProfile.groupResourceProfileId"
-                            v-on:app-selected="clickHandler(groupResourceProfile)">
-          </application-card>
-        </div>
-      </div>
-    </div>
-  </div>
+  <list-layout @add-new-item="newGroupResourcePreference" :items="groupResourceProfiles"
+      title="Group Resource Profiles" new-item-button-text="New Group Resource Profile">
+    <template slot="item-list" slot-scope="slotProps">
+
+      <b-table striped hover :fields="fields" :items="slotProps.items">
+        <template slot="action" slot-scope="data">
+          <a href="#" @click.prevent="clickHandler(data.item)" v-if="data.item.userHasWriteAccess">
+            Edit
+            <i class="fa fa-edit" aria-hidden="true"></i>
+          </a>
+        </template>
+      </b-table> 
+    </template>
+  </list-layout>
 </template>
 
 <script>
-  import {components as comps} from 'django-airavata-common-ui'
+  import {layouts} from 'django-airavata-common-ui'
   import {services} from 'django-airavata-api'
+  import moment from 'moment'
 
   export default {
     name: "compute-resource-preference",
     components: {
-      'application-card': comps.ApplicationCard,
+      'list-layout': layouts.ListLayout,
     },
     data: function () {
       return {
         groupResourceProfiles: [],
+        fields: [
+          {
+            label: 'Name',
+            key: 'groupResourceProfileName',
+          },
+          {
+            label: 'Updated',
+            key: 'updatedTime',
+            formatter: (value) => moment(new Date(value)).fromNow(),
+          },
+          {
+            label: 'Action',
+            key: 'action',
+          },
+        ],
       }
     },
     methods: {
@@ -48,18 +60,6 @@
           }
         })
       },
-      transform: function (preference) {
-        let tags=["Created On " + new Date(preference.creationTime).toDateString()];
-        if(preference.creationTime !==preference.updatedTime){
-          tags.push("Updated On " + new Date(preference.updatedTime).toDateString());
-        }
-        return {
-          appModuleName: preference.groupResourceProfileName,
-          tags: tags,
-          appModuleVersion: null,
-          appModuleDescription: null
-        }
-      },
       loadGroupResourceProfiles: function () {
         services.GroupResourceProfileService.list()
           .then(groupResourceProfiles => {
@@ -71,47 +71,4 @@
       this.loadGroupResourceProfiles();
     }
   }
-</script>
-
-<style scoped>
-  .new_app {
-    margin: 45px;
-    width: 100%;
-    background-color: white;
-  }
-
-  .new_app_header {
-    width: 100%;
-    display: inline;
-  }
-
-  .new_app_header > label {
-    background-color: #2e73bc;
-    color: white;
-    border: solid #2e73bc 1px;
-    border-radius: 3px;
-    float: right;
-    padding-right: 15px;
-    padding-left: 15px;
-    padding-bottom: 8px;
-    padding-top: 3px;
-    text-align: center;
-  }
-
-  .new_app_header label:hover {
-    cursor: pointer;
-  }
-
-  .new_app_header label span {
-    font-weight: 900;
-    font-size: 25px;
-  }
-
-  .applications {
-    margin-top: 50px;
-  }
-
-  .ssh, .generate input {
-    text-align: center;
-  }
-</style>
+</script>
\ No newline at end of file
diff --git a/django_airavata/apps/admin/templates/admin/admin_base.html b/django_airavata/apps/admin/templates/admin/admin_base.html
index 96dec35..97e5c66 100644
--- a/django_airavata/apps/admin/templates/admin/admin_base.html
+++ b/django_airavata/apps/admin/templates/admin/admin_base.html
@@ -15,7 +15,7 @@
         <a href="{% url 'django_airavata_admin:credential_store' %}" class="c-nav__item {% if request.active_nav_item == 'credential_store' %}is-active{% endif %}" data-toggle=tooltip data-placement=right title="Credential Store">
             <i class="fa fa-lock"></i> <span class=sr-only>Credential Store</span>
         </a>
-        <a href="{% url 'django_airavata_admin:group_resource_profile' %}" class="c-nav__item {% if request.active_nav_item == 'group_resource_profile' %}is-active{% endif %}" data-toggle=tooltip data-placement=right title="Group Resouce Profile">
+        <a href="{% url 'django_airavata_admin:group_resource_profile' %}" class="c-nav__item {% if request.active_nav_item == 'group_resource_profile' %}is-active{% endif %}" data-toggle=tooltip data-placement=right title="Group Resource Profile">
             <i class="fa fa-server"></i> <span class=sr-only>Group Resource Profile</span>
         </a>
 {% endblock %}
diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index 88de8a8..020ade4 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -449,6 +449,13 @@ class GroupResourceProfileSerializer(
     url = FullyEncodedHyperlinkedIdentityField(view_name='django_airavata_api:group-resource-profile-detail', lookup_field='groupResourceProfileId', lookup_url_kwarg='group_resource_profile_id')
     creationTime = UTCPosixTimestampDateTimeField(allow_null=True)
     updatedTime = UTCPosixTimestampDateTimeField(allow_null=True)
+    userHasWriteAccess = serializers.SerializerMethodField()
+
+    def get_userHasWriteAccess(self, groupResourceProfile):
+        request = self.context['request']
+        return request.airavata_client.userHasAccess(
+            request.authz_token, groupResourceProfile.groupResourceProfileId,
+            ResourcePermissionType.WRITE)
 
 
 class SharedGroups(serializers.Serializer):
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
index 1f66a38..ab3f1de 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
@@ -30,6 +30,7 @@ const FIELDS = [
          name: 'updatedTime',
          type: 'date',
      },
+     'userHasWriteAccess', // true if current user has write access
 ];
 
 export default class GroupResourceProfile extends BaseModel {
diff --git a/django_airavata/static/common/js/index.js b/django_airavata/static/common/js/index.js
index cb87249..9c9c22f 100644
--- a/django_airavata/static/common/js/index.js
+++ b/django_airavata/static/common/js/index.js
@@ -3,6 +3,9 @@ import Autocomplete from './components/Autocomplete.vue'
 import AutocompleteTextInput from './components/AutocompleteTextInput.vue'
 import Pager from './components/Pager.vue'
 import ShareButton from './components/ShareButton.vue'
+
+import ListLayout from './layouts/ListLayout.vue'
+
 import * as utils from './utils'
 
 exports.components = {
@@ -13,4 +16,8 @@ exports.components = {
     ShareButton,
 }
 
+exports.layouts = {
+    ListLayout,
+}
+
 exports.utils = utils;
diff --git a/django_airavata/static/common/js/layouts/ListLayout.vue b/django_airavata/static/common/js/layouts/ListLayout.vue
new file mode 100644
index 0000000..8ab1af8
--- /dev/null
+++ b/django_airavata/static/common/js/layouts/ListLayout.vue
@@ -0,0 +1,80 @@
+<template>
+    <div>
+        <div class="row">
+            <div class="col">
+                <slot name="title">
+                    <h1 class="h4 mb-4">{{ title }}</h1>
+                </slot>
+            </div>
+            <div id="col-new-item" class="col">
+                <slot name="new-item-button">
+                    <b-btn variant="primary" @click="addNewItem">
+                        {{ newItemButtonText }} <i class="fa fa-plus" aria-hidden="true"></i>
+                    </b-btn>
+                </slot>
+            </div>
+        </div>
+        <div class="row">
+            <div class="col">
+                <div class="card">
+                    <div class="card-body">
+                        <slot name="item-list" :items="itemsList">Item List goes here</slot>
+                        <pager v-if="itemsPaginator" :paginator="itemsPaginator"
+                        next="nextItems" v-on:previous="previousItems"></pager>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+
+import { utils } from 'django-airavata-api'
+import Pager from '../components/Pager.vue'
+
+export default {
+    props: {
+        items: Array,
+        itemsPaginator: utils.PaginationIterator,
+        title: {
+            type: String,
+            default: "Items"
+        },
+        newItemButtonText: {
+            type: String,
+            default: "New Item",
+        },
+    },
+    name: 'list-layout',
+    data () {
+        return {
+        }
+    },
+    components: {
+        'pager': Pager,
+    },
+    methods: {
+        nextItems: function(event) {
+            this.itemsPaginator.next();
+        },
+        previousItems: function(event) {
+            this.itemsPaginator.previous();
+        },
+        addNewItem: function() {
+            this.$emit('add-new-item');
+        },
+    },
+    computed: {
+        itemsList: function() {
+            return this.itemsPaginator ? this.itemsPaginator.results : this.items;
+        },
+    },
+}
+</script>
+
+<style>
+#col-new-item {
+    text-align: right;
+}
+</style>
diff --git a/django_airavata/static/common/scss/main.scss b/django_airavata/static/common/scss/main.scss
index 5ca0024..95a2cc9 100644
--- a/django_airavata/static/common/scss/main.scss
+++ b/django_airavata/static/common/scss/main.scss
@@ -107,6 +107,7 @@ body {
     flex-grow: 1;
     padding: 1rem;
     overflow-y: scroll;
+    max-width: 1200px;
 }
 
 .sidebar {


[airavata-django-portal] 02/13: AIRAVATA-2727 Adding ids to routes so data can be fetched on reload

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 6f9a1a109d08cfeef02a95d7db2f515468bbfcf6
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Wed Aug 8 12:56:37 2018 -0400

    AIRAVATA-2727 Adding ids to routes so data can be fetched on reload
---
 .../group_resource_preferences/ComputePreference.vue  | 15 ++++++++++++---
 .../GroupComputeResourcePreference.vue                | 19 +++++++++++++------
 .../dashboards/ComputeResourcePreferenceDashboard.vue |  3 ++-
 .../admin/static/django_airavata_admin/src/main.js    |  4 +++-
 .../admin/static/django_airavata_admin/src/router.js  |  8 ++++----
 5 files changed, 34 insertions(+), 15 deletions(-)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
index 5fd7cba..f3fca9f 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
@@ -125,11 +125,20 @@
       newCreation: {
         type: Boolean,
         default: false
+      },
+      id: {
+        type: String,
+      },
+      host_id: {
+        type: String,
       }
     },
     mounted: function () {
-      if (this.value && this.value.computeResourceId) {
-        this.fetchComputeResource(this.value.computeResourceId);
+      if (!this.value && this.id && this.host_id) {
+        // TODO: load the Group Resource Profile and get the compute preferences for this host_id
+      }
+      if (this.host_id) {
+        this.fetchComputeResource(this.host_id);
       } else {
         this.fetchComputeResources();
       }
@@ -141,7 +150,7 @@
         data.computeResourcePolicies = [];
         data.computeResourcePolicies.push(this.createComputeResourcePolicy());
       }
-      if(!data.computeResourceId){
+      if(!this.host_id){
         enablePopup = true;
       }
       return {
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
index eb3b2ad..32a3094 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
@@ -15,7 +15,7 @@
         <h4>Compute Preferences</h4>
         <div>
           <a class="list-item" v-for="computePreference,index in data.computePreferences" v-bind:key="index"
-             v-on:click="computePreferenceClickHandler(index)">
+             v-on:click="computePreferenceClickHandler(computePreference.computeResourceId)">
             <span v-if="computePreference.groupResourceProfileId">
               {{getComputeResourceName(computePreference.computeResourceId)}}
             </span>
@@ -73,6 +73,12 @@
       if (this.value.groupResourceProfileId) {
         DjangoAiravataAPI.services.ServiceFactory.service("SharedEntities").retrieve({lookup: this.value.groupResourceProfileId})
           .then(sharedEntity => this.sharedEntity = sharedEntity);
+      } else if (this.$route.params.id) {
+        // TODO: switch to using props to get the id param
+        DjangoAiravataAPI.services.ServiceFactory.service("GroupResourcePreference").retrieve({lookup: this.$route.params.id})
+          .then(grp => this.data = this.transformData(grp));
+        DjangoAiravataAPI.services.ServiceFactory.service("SharedEntities").retrieve({lookup: this.$route.params.id})
+          .then(sharedEntity => this.sharedEntity = sharedEntity);
       }
     },
     data: function () {
@@ -206,12 +212,13 @@
           });
         }
       },
-      computePreferenceClickHandler: function (index) {
+      computePreferenceClickHandler: function (computeResourceId) {
+        let computeResourcePreference = this.data.computePreferences.find(pref => pref.computeResourceId === computeResourceId);
         this.$router.push({
-          name: 'compute_preferences', params: {
-            value: this.data,
-            index: index,
-            newCreation: this.newCreation
+          name: 'compute_preference', params: {
+            value: computeResourcePreference,
+            id: this.data.groupResourceProfileId,
+            host_id: computeResourceId,
           }
         });
       },
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
index 4c547fd..87896c8 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
@@ -49,7 +49,8 @@
       clickHandler: function (groupResourceProfile) {
         this.$router.push({
           name: 'group_resource_preference', params: {
-            value: groupResourceProfile
+            value: groupResourceProfile,
+            id: groupResourceProfile.groupResourceProfileId
           }
         });
       },
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/main.js b/django_airavata/apps/admin/static/django_airavata_admin/src/main.js
index ff18980..fa8a27b 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/main.js
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/main.js
@@ -43,7 +43,9 @@ export function initializeApacheAiravataDashboard(dashboardName) {
       ComputeResourcePreferenceDashboard
     },
     mounted:function () {
-      this.$router.push({name:dashboardName})
+      if (this.$router.currentRoute.path === '/') {
+        this.$router.push({ name: dashboardName })
+      }
     }
 
   })
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/router.js b/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
index a47d90d..6d54012 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
@@ -5,7 +5,7 @@ import ApplicationDetails from './components/admin/ApplicationDetails.vue'
 import ApplicationInterface from './components/admin/ApplicationInterface.vue'
 import ApplicationDeployments from './components/admin/ApplicationDeployments.vue'
 import GroupComputeResourcePreference from './components/admin/group_resource_preferences/GroupComputeResourcePreference'
-import ComputePreferences from './components/admin/group_resource_preferences/ComputePreferences'
+import ComputePreference from './components/admin/group_resource_preferences/ComputePreference'
 import ComputeResourcePreferenceDashboard from './components/dashboards/ComputeResourcePreferenceDashboard'
 import CredentialStoreDashboard from './components/dashboards/CredentialStoreDashboard'
 import VueRouter from 'vue-router'
@@ -34,15 +34,15 @@ const routes = [
   {path: '/admin', component: AdminDashboard, name: "admin_dashboard"},
   {path: '/experiments', component: ExperimentsDashboard, name: 'experiments_dashboard'},
   {
-    path: '/group/resource/profile', component: GroupComputeResourcePreference, name: 'group_resource_preference',
+    path: '/group-resource-profiles/:id', component: GroupComputeResourcePreference, name: 'group_resource_preference',
     props: true
   },
   {
-    path: '/group/resource/compute/preferences', component: ComputePreferences, name: 'compute_preferences',
+    path: '/group-resource-profiles/:id/compute-preferences/:host_id', component: ComputePreference, name: 'compute_preference',
     props: true
   },
   {
-    path: '/dashboards/group/resource/profiles',
+    path: '/group-resource-profiles',
     component: ComputeResourcePreferenceDashboard,
     name: 'group_resource_preference_dashboard',
   },


[airavata-django-portal] 12/13: AIRAVATA-2727 Deleting GroupResourceProfiles

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit cb1e52560c09dba8d2ec5afc2b3f3c2ed1877431
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Tue Aug 14 14:37:25 2018 -0400

    AIRAVATA-2727 Deleting GroupResourceProfiles
---
 .../GroupComputeResourcePreference.vue                   | 16 ++++++++++++++++
 .../dashboards/ComputeResourcePreferenceDashboard.vue    | 14 ++++++++++++++
 .../apps/api/static/django_airavata_api/js/index.js      |  3 +--
 django_airavata/apps/api/views.py                        |  4 ++++
 .../experiment/ComputationalResourceSchedulingEditor.vue |  2 +-
 5 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
index 9fe4cc9..849bb6d 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
@@ -43,6 +43,7 @@
     <div class="row">
         <div class="col d-flex justify-content-end">
             <b-button variant="primary" @click="saveGroupResourceProfile">Save</b-button>
+            <b-button v-if="id" class="ml-2" variant="danger" @click="removeGroupResourceProfile">Delete</b-button>
             <b-button class="ml-2" variant="secondary" @click="cancel">Cancel</b-button>
         </div>
     </div>
@@ -193,6 +194,21 @@
             // TODO: handle error
             console.log("Error occurred", error);
           });
+      },
+      removeGroupResourceProfile: function() {
+        if (this.id) {
+          this.service.delete({lookup: this.id})
+            .then(() => {
+              this.$router.push('/group-resource-profiles');
+            })
+            .catch(error => {
+              // TODO: handle error
+              console.log("Error occurred", error);
+            });
+        } else {
+          // Nothing to delete so just treat like a cancel
+          this.cancel();
+        }
       }
     },
   }
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
index 8fc5470..bddae9a 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
@@ -9,6 +9,10 @@
             Edit
             <i class="fa fa-edit" aria-hidden="true"></i>
           </a>
+          <a href="#" class="text-danger" @click.prevent="removeGroupResourceProfile(data.item)" v-if="data.item.userHasWriteAccess">
+            Delete
+            <i class="fa fa-trash" aria-hidden="true"></i>
+          </a>
         </template>
       </b-table> 
     </template>
@@ -65,6 +69,16 @@
             this.groupResourceProfiles = groupResourceProfiles;
           });
       },
+      removeGroupResourceProfile: function(groupResourceProfile) {
+
+        services.GroupResourceProfileService.delete({lookup: groupResourceProfile.groupResourceProfileId})
+          .then(() => services.GroupResourceProfileService.list())
+          .then(groupResourceProfiles => this.groupResourceProfiles = groupResourceProfiles)
+          .catch(error => {
+            // TODO: handle error
+            console.log("Error occurred", error);
+          });
+      }
     },
     mounted: function () {
       this.loadGroupResourceProfiles();
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/index.js b/django_airavata/apps/api/static/django_airavata_api/js/index.js
index bb428d1..49f08c1 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/index.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/index.js
@@ -29,7 +29,6 @@ import ProjectService from './services/ProjectService'
 import GroupService from './services/GroupService'
 import GroupResourceProfileService from './services/GroupResourceProfileService'
 import UserProfileService from './services/UserProfileService'
-import ComputeResourceService from './services/ComputeResourceService'
 import CloudJobSubmissionService from './services/CloudJobSubmissionService'
 import GlobusJobSubmissionService from './services/GlobusJobSubmissionService'
 import LocaJobSubmissionService from './services/LocaJobSubmissionService'
@@ -73,7 +72,7 @@ exports.services = {
     FullExperimentService,
     ProjectService,
     GroupService,
-    GroupResourceProfileService,
+    GroupResourceProfileService: ServiceFactory.service("GroupResourceProfiles"),
     UserProfileService,
     ComputeResourceService: ServiceFactory.service("ComputeResources"),
     CloudJobSubmissionService,
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index 3650c40..a2f473c 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -718,6 +718,10 @@ class GroupResourceProfileViewSet(APIBackedViewSet):
         self.request.airavata_client.updateGroupResourceProfile(
             self.authz_token, grp)
 
+    def perform_destroy(self, instance):
+        self.request.airavata_client.removeGroupResourceProfile(
+            self.authz_token, instance.groupResourceProfileId)
+
 
 class SharedEntityGroups(APIBackedViewSet):
     serializer_class = serializers.SharedGroups
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ComputationalResourceSchedulingEditor.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ComputationalResourceSchedulingEditor.vue
index e58d8e5..e8bca09 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ComputationalResourceSchedulingEditor.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/ComputationalResourceSchedulingEditor.vue
@@ -139,7 +139,7 @@ export default {
         },
         loadGroupResourceProfile: function() {
             this.loadingCount++;
-            services.GroupResourceProfileService.get(this.groupResourceProfileId)
+            services.GroupResourceProfileService.retrieve({lookup: this.groupResourceProfileId})
                 .then(groupResourceProfile => {
                     this.selectedGroupResourceProfileData = groupResourceProfile;
                 })


[airavata-django-portal] 13/13: AIRAVATA-2727 Load state from compute preference URL path params

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 15dab8947e78fbae691659464643024028dc2047
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Tue Aug 14 15:29:21 2018 -0400

    AIRAVATA-2727 Load state from compute preference URL path params
---
 .../ComputePreference.vue                          | 56 +++++++++++++---------
 .../js/models/GroupResourceProfile.js              |  4 ++
 2 files changed, 38 insertions(+), 22 deletions(-)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
index 917a999..86a08de 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
@@ -67,7 +67,7 @@
   import VModelMixin from '../../commons/vmodel_mixin'
   import BatchQueueResourcePolicy from './BatchQueueResourcePolicy.vue'
 
-  import {models} from 'django-airavata-api'
+  import {models, services} from 'django-airavata-api'
 
   export default {
     name: "compute-preference",
@@ -80,6 +80,7 @@
       },
       host_id: {
         type: String,
+        required: true,
       },
       groupResourceProfile: {
         type: models.GroupResourceProfile,
@@ -92,29 +93,31 @@
       }
     },
     mounted: function () {
+      const computeResourcePromise = this.fetchComputeResource(this.host_id);
       if (!this.value && this.id && this.host_id) {
-        // TODO: load the Group Resource Profile and get the compute preferences for this host_id
-      }
-      if (this.host_id) {
-        const computeResourceOperation = this.fetchComputeResource(this.host_id);
-        // If no computeResourcePolicy create a new default one that allows all queues
-        if (!this.computeResourcePolicy) {
-          computeResourceOperation.then(computeResource => {
-            const defaultComputeResourcePolicy = new models.ComputeResourcePolicy();
-            defaultComputeResourcePolicy.computeResourceId = this.host_id;
-            defaultComputeResourcePolicy.groupResourceProfileId = this.id;
-            defaultComputeResourcePolicy.allowedBatchQueues = computeResource.batchQueues.map(queue => queue.queueName);
-            this.localComputeResourcePolicy = defaultComputeResourcePolicy;
-          })
-        }
+        services.GroupResourceProfileService.retrieve({lookup: this.id})
+          .then(groupResourceProfile => {
+            this.localGroupResourceProfile = groupResourceProfile;
+            const computeResourcePreference = groupResourceProfile.getComputePreference(this.host_id);
+            if (computeResourcePreference) {
+              this.data = computeResourcePreference;
+            }
+            const computeResourcePolicy = groupResourceProfile.getComputeResourcePolicy(this.host_id);
+            if (computeResourcePolicy) {
+              this.localComputeResourcePolicy = computeResourcePolicy;
+            } else {
+              this.createDefaultComputeResourcePolicy(computeResourcePromise);
+            }
+            this.localBatchQueueResourcePolicies = groupResourceProfile.getBatchQueueResourcePolicies(this.host_id);
+          });
+      } else if (!this.computeResourcePolicy) {
+        this.createDefaultComputeResourcePolicy(computeResourcePromise);
       }
     },
     data: function () {
       return {
-        data: this.value.clone(),
-        selected: null,
-        computeResources: [],
-        selectedComputeResourceIndex: null,
+        data: this.value ? this.value.clone() : new models.GroupComputeResourcePreference({computeResourceId: this.host_id}),
+        localGroupResourceProfile: this.groupResourceProfile ? this.groupResourceProfile.clone() : null,
         localComputeResourcePolicy: this.computeResourcePolicy ? this.computeResourcePolicy.clone() : null,
         localBatchQueueResourcePolicies: this.batchQueueResourcePolicies ? this.batchQueueResourcePolicies.map(pol => pol.clone()) : [],
         computeResource: {
@@ -163,7 +166,7 @@
         });
       },
       save: function() {
-        let groupResourceProfile = this.groupResourceProfile.clone();
+        let groupResourceProfile = this.localGroupResourceProfile.clone();
         groupResourceProfile.mergeComputeResourcePreference(this.data, this.localComputeResourcePolicy, this.localBatchQueueResourcePolicies);
         // TODO: success and error handling are the same so we can just combine those
         if (this.id) {
@@ -200,7 +203,7 @@
       },
       remove: function() {
 
-        let groupResourceProfile = this.groupResourceProfile.clone();
+        let groupResourceProfile = this.localGroupResourceProfile.clone();
         const removedChildren = groupResourceProfile.removeComputeResource(this.host_id);
         if (removedChildren) {
           DjangoAiravataAPI.services.ServiceFactory.service("GroupResourceProfiles").update({data: groupResourceProfile, lookup: this.id})
@@ -226,8 +229,17 @@
         if (this.id) {
           this.$router.push({ name: 'group_resource_preference', params: {id: this.id}});
         } else {
-          this.$router.push({ name: 'new_group_resource_preference', params: {value: this.groupResourceProfile}});
+          this.$router.push({ name: 'new_group_resource_preference', params: {value: this.localGroupResourceProfile}});
         }
+      },
+      createDefaultComputeResourcePolicy: function(computeResourcePromise) {
+        computeResourcePromise.then(computeResource => {
+          const defaultComputeResourcePolicy = new models.ComputeResourcePolicy();
+          defaultComputeResourcePolicy.computeResourceId = this.host_id;
+          defaultComputeResourcePolicy.groupResourceProfileId = this.id;
+          defaultComputeResourcePolicy.allowedBatchQueues = computeResource.batchQueues.map(queue => queue.queueName);
+          this.localComputeResourcePolicy = defaultComputeResourcePolicy;
+        })
       }
     },
     beforeRouteEnter: function(to, from, next) {
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
index 8904504..59645e3 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
@@ -42,6 +42,10 @@ export default class GroupResourceProfile extends BaseModel {
         super(FIELDS, data);
     }
 
+    getComputePreference(computeResourceId) {
+        return this.computePreferences.find(pref => pref.computeResourceId === computeResourceId);
+    }
+
     getComputeResourcePolicy(computeResourceId) {
         return this.computeResourcePolicies.find(pol => pol.computeResourceId === computeResourceId);
     }


[airavata-django-portal] 11/13: AIRAVATA-2727 Removing compute preferences

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 20892765c7257c464492ad0b838ddb5c61cdcbf4
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Tue Aug 14 12:34:46 2018 -0400

    AIRAVATA-2727 Removing compute preferences
---
 .../ComputePreference.vue                          | 25 +++++++++++++++++++
 .../GroupComputeResourcePreference.vue             | 15 ++++++++++++
 django_airavata/apps/api/serializers.py            | 27 ++++++++++++++++++---
 .../js/models/GroupResourceProfile.js              | 28 ++++++++++++++++++++++
 django_airavata/apps/api/views.py                  | 12 +++++++++-
 5 files changed, 103 insertions(+), 4 deletions(-)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
index 5d8edda..917a999 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
@@ -54,6 +54,7 @@
     <div class="row">
         <div class="col d-flex justify-content-end">
             <b-button variant="primary" @click="save">Save</b-button>
+            <b-button class="ml-2" variant="danger" @click="remove">Delete</b-button>
             <b-button class="ml-2" variant="secondary" @click="cancel">Cancel</b-button>
         </div>
     </div>
@@ -197,6 +198,30 @@
             });
         }
       },
+      remove: function() {
+
+        let groupResourceProfile = this.groupResourceProfile.clone();
+        const removedChildren = groupResourceProfile.removeComputeResource(this.host_id);
+        if (removedChildren) {
+          DjangoAiravataAPI.services.ServiceFactory.service("GroupResourceProfiles").update({data: groupResourceProfile, lookup: this.id})
+            .then(groupResourceProfile => {
+              // Navigate back to GroupResourceProfile with success message
+              this.$router.push({
+                name: 'group_resource_preference', params: {
+                  value: groupResourceProfile,
+                  id: this.id
+                }
+              });
+            })
+            .catch(error => {
+              // TODO: handle error
+              console.log("Error occurred", error);
+            });
+        } else {
+          // Since nothing was removed, just handle this like a cancel
+          this.cancel();
+        }
+      },
       cancel: function() {
         if (this.id) {
           this.$router.push({ name: 'group_resource_preference', params: {id: this.id}});
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
index e62dc28..9fe4cc9 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
@@ -32,6 +32,10 @@
               Edit
               <i class="fa fa-edit" aria-hidden="true"></i>
             </a>
+            <a href="#" class="text-danger" @click.prevent="removeComputePreference(data.item.computeResourceId)">
+              Delete
+              <i class="fa fa-trash" aria-hidden="true"></i>
+            </a>
           </template>
         </b-table> 
       </template>
@@ -178,6 +182,17 @@
             batchQueueResourcePolicies: batchQueueResourcePolicies,
           }
         });
+      },
+      removeComputePreference: function(computeResourceId) {
+
+        let groupResourceProfile = this.data.clone();
+        groupResourceProfile.removeComputeResource(computeResourceId);
+        this.service.update({data: groupResourceProfile, lookup: this.id})
+          .then(groupResourceProfile => this.data = groupResourceProfile)
+          .catch(error => {
+            // TODO: handle error
+            console.log("Error occurred", error);
+          });
       }
     },
   }
diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index ec8cee8..3ccd9d8 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -453,14 +453,35 @@ class GroupResourceProfileSerializer(
 
     def update(self, instance, validated_data):
         result = super().update(instance, validated_data)
+        result._removed_compute_resource_preferences = []
+        result._removed_compute_resource_policies = []
         result._removed_batch_queue_resource_policies = []
+        # Find all compute resource preferences that were removed
+        for compute_resource_preference in instance.computePreferences:
+            existing_compute_resource_preference = next(
+                (pref for pref in result.computePreferences
+                 if pref.computeResourceId ==
+                    compute_resource_preference.computeResourceId),
+                None)
+            if not existing_compute_resource_preference:
+                result._removed_compute_resource_preferences.append(
+                    compute_resource_preference)
+        # Find all compute resource policies that were removed
+        for compute_resource_policy in instance.computeResourcePolicies:
+            existing_compute_resource_policy = next(
+                (pol for pol in result.computeResourcePolicies
+                 if pol.resourcePolicyId ==
+                    compute_resource_policy.resourcePolicyId),
+                None)
+            if not existing_compute_resource_policy:
+                result._removed_compute_resource_policies.append(
+                    compute_resource_policy)
         # Find all batch queue resource policies that were removed
         for batch_queue_resource_policy in instance.batchQueueResourcePolicies:
             existing_batch_queue_resource_policy_for_update = next(
                 (bq for bq in result.batchQueueResourcePolicies
-                 if bq.computeResourceId ==
-                    batch_queue_resource_policy.computeResourceId
-                    and bq.queuename == batch_queue_resource_policy.queuename),
+                 if bq.resourcePolicyId ==
+                    batch_queue_resource_policy.resourcePolicyId),
                 None)
             if not existing_batch_queue_resource_policy_for_update:
                 result._removed_batch_queue_resource_policies.append(
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
index 34d664e..8904504 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
@@ -86,4 +86,32 @@ export default class GroupResourceProfile extends BaseModel {
             }
         }
     }
+
+    /**
+     * Remove compute resource preference, compute resource policy and batch queue policies.
+     * @param {string} computeResourceId 
+     * @returns {boolean} true if this GroupResourceProfile was changed
+     */
+    removeComputeResource(computeResourceId) {
+
+        let removedChildren = false;
+        const existingComputeResourcePreferenceIndex = this.computePreferences.findIndex(pref => pref.computeResourceId === computeResourceId);
+        if (existingComputeResourcePreferenceIndex >= 0) {
+            this.computePreferences.splice(existingComputeResourcePreferenceIndex, 1);
+            removedChildren = true;
+        }
+        const existingComputeResourcePolicyIndex = this.computeResourcePolicies.findIndex(pol => pol.computeResourceId === computeResourceId);
+        if (existingComputeResourcePolicyIndex >= 0) {
+            this.computeResourcePolicies.splice(existingComputeResourcePolicyIndex, 1);
+            removedChildren = true;
+        }
+        const existingBatchQueueResourcePolicies = this.batchQueueResourcePolicies.filter(pol => pol.computeResourceId === computeResourceId);
+        for (const existingBatchQueueResourcePolicy of existingBatchQueueResourcePolicies) {
+            const existingBatchQueueResourcePolicyIndex = this.batchQueueResourcePolicies.indexOf(existingBatchQueueResourcePolicy);
+            this.batchQueueResourcePolicies.splice(existingBatchQueueResourcePolicyIndex, 1);
+            removedChildren = true;
+        }
+
+        return removedChildren;
+    }
 }
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index 2ad5ec5..3650c40 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -699,12 +699,22 @@ class GroupResourceProfileViewSet(APIBackedViewSet):
 
     def perform_update(self, serializer):
         grp = serializer.save()
+        for removed_compute_resource_preference \
+                in grp._removed_compute_resource_preferences:
+            self.request.airavata_client.removeGroupComputePrefs(
+                self.authz_token,
+                removed_compute_resource_preference.computeResourceId,
+                removed_compute_resource_preference.groupResourceProfileId)
+        for removed_compute_resource_policy \
+                in grp._removed_compute_resource_policies:
+            self.request.airavata_client.removeGroupComputeResourcePolicy(
+                self.authz_token,
+                removed_compute_resource_policy.resourcePolicyId)
         for removed_batch_queue_resource_policy \
                 in grp._removed_batch_queue_resource_policies:
             self.request.airavata_client.removeGroupBatchQueueResourcePolicy(
                 self.authz_token,
                 removed_batch_queue_resource_policy.resourcePolicyId)
-        log.debug("batch queue res policies: {}".format(grp.batchQueueResourcePolicies))
         self.request.airavata_client.updateGroupResourceProfile(
             self.authz_token, grp)
 


[airavata-django-portal] 10/13: AIRAVATA-2727 Routes for new GroupResourceProfile, ComputePrefs

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 182d9e74024cf5085e1db3415fcbca3236a48c1b
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Tue Aug 14 11:07:07 2018 -0400

    AIRAVATA-2727 Routes for new GroupResourceProfile, ComputePrefs
---
 .../ComputePreference.vue                          | 22 +++++++--
 .../ComputePreferences.vue                         | 55 ----------------------
 .../GroupComputeResourcePreference.vue             | 43 ++++++++---------
 .../ComputeResourcePreferenceDashboard.vue         |  6 +--
 .../static/django_airavata_admin/src/router.js     |  8 ++++
 .../js/models/GroupResourceProfile.js              | 25 ++--------
 6 files changed, 51 insertions(+), 108 deletions(-)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
index d3a1491..5d8edda 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
@@ -165,14 +165,14 @@
         let groupResourceProfile = this.groupResourceProfile.clone();
         groupResourceProfile.mergeComputeResourcePreference(this.data, this.localComputeResourcePolicy, this.localBatchQueueResourcePolicies);
         // TODO: success and error handling are the same so we can just combine those
-        if (groupResourceProfile.groupResourceProfileId) {
-          DjangoAiravataAPI.services.ServiceFactory.service("GroupResourceProfiles").update({data: groupResourceProfile, lookup: groupResourceProfile.groupResourceProfileId})
+        if (this.id) {
+          DjangoAiravataAPI.services.ServiceFactory.service("GroupResourceProfiles").update({data: groupResourceProfile, lookup: this.id})
             .then(groupResourceProfile => {
               // Navigate back to GroupResourceProfile with success message
               this.$router.push({
                 name: 'group_resource_preference', params: {
                   value: groupResourceProfile,
-                  id: groupResourceProfile.groupResourceProfileId
+                  id: this.id
                 }
               });
             })
@@ -198,8 +198,22 @@
         }
       },
       cancel: function() {
-        this.$router.push({ name: 'group_resource_preference', params: {id: this.id}});
+        if (this.id) {
+          this.$router.push({ name: 'group_resource_preference', params: {id: this.id}});
+        } else {
+          this.$router.push({ name: 'new_group_resource_preference', params: {value: this.groupResourceProfile}});
+        }
       }
     },
+    beforeRouteEnter: function(to, from, next) {
+      // If we don't have the Group Resource Profile id or instance, then the
+      // Group Resource Profile wasn't created and we need to just go back to
+      // the dashboard
+      if (!to.params.id && !to.params.groupResourceProfile) {
+        next({name: 'group_resource_preference_dashboard'});
+      } else {
+        next();
+      }
+    }
   }
 </script>
\ No newline at end of file
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreferences.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreferences.vue
deleted file mode 100644
index f18e5bf..0000000
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreferences.vue
+++ /dev/null
@@ -1,55 +0,0 @@
-<template>
-  <array-component-view v-bind:homeAction="createHomeAction()" v-bind:maxIndex="data.computePreferences.length-1"
-                        v-model="currentIndex">
-      <compute-preference v-model="data.computePreferences[currentIndex]" v-bind:key="currentIndex" v-bind:newCreation="data.computePreferences[currentIndex].groupResourceProfileId == null"></compute-preference>
-  </array-component-view>
-</template>
-
-<script>
-  import ArrayComponentView from '../../commons/ArrayComponentView'
-  import ComputePreference from './ComputePreference';
-  import VModelMixin from '../../commons/vmodel_mixin'
-
-  export default {
-    name: "compute-preferences",
-    components: {
-      ArrayComponentView,
-      ComputePreference
-    },
-    mixins: [VModelMixin],
-    props: {
-      index: {
-        type: Number,
-        default: 0
-      },
-      newCreation:{
-        type:Boolean,
-        default:false
-      }
-    },
-    data: function () {
-      return {
-        currentIndex: this.index
-      }
-    },
-    methods: {
-      createMovableAction: function () {
-        return (index) => {
-          this.currentIndex = index
-        }
-      },
-      createHomeAction: function () {
-        return () => this.$router.push({
-          name: 'group_resource_preference', params: {
-            value: this.data,
-            transform:false
-          }
-        })
-      }
-    },
-  }
-</script>
-
-<style scoped>
-
-</style>
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
index 46b3cd3..e62dc28 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
@@ -70,12 +70,11 @@
       },
     },
     mounted: function () {
-      if (this.value.groupResourceProfileId) {
-        services.ServiceFactory.service("SharedEntities").retrieve({lookup: this.value.groupResourceProfileId})
-          .then(sharedEntity => this.sharedEntity = sharedEntity);
-      } else if (this.id) {
-        services.ServiceFactory.service("GroupResourceProfiles").retrieve({lookup: this.id})
-          .then(grp => this.data = grp);
+      if (this.id) {
+        if (!this.value.groupResourceProfileId) {
+          services.ServiceFactory.service("GroupResourceProfiles").retrieve({lookup: this.id})
+            .then(grp => this.data = grp);
+        }
         services.ServiceFactory.service("SharedEntities").retrieve({lookup: this.id})
           .then(sharedEntity => this.sharedEntity = sharedEntity);
       }
@@ -130,13 +129,14 @@
     methods: {
       saveGroupResourceProfile: function () {
         var persist = null;
-        if (this.data.groupResourceProfileId) {
-          persist = this.service.update({data: this.data, lookup: this.data.groupResourceProfileId});
+        if (this.id) {
+          persist = this.service.update({data: this.data, lookup: this.id});
         } else {
           persist = this.service.create({data: this.data})
             .then((data) => {
               // Save sharing settings too
-              return this.$refs.shareButton.mergeAndSave(data.groupResourceProfileId);
+              const groupResourceProfileId = data.groupResourceProfileId;
+              return this.$refs.shareButton.mergeAndSave(groupResourceProfileId);
             });
         }
         // TODO: handle errors
@@ -148,16 +148,7 @@
         let computeResourcePreference = this.data.computePreferences.find(pref => pref.computeResourceId === computeResourceId);
         const computeResourcePolicy = this.data.getComputeResourcePolicy(computeResourceId);
         const batchQueueResourcePolicies = this.data.getBatchQueueResourcePolicies(computeResourceId);
-        this.$router.push({
-          name: 'compute_preference', params: {
-            value: computeResourcePreference,
-            id: this.data.groupResourceProfileId,
-            host_id: computeResourceId,
-            groupResourceProfile: this.data,
-            computeResourcePolicy: computeResourcePolicy,
-            batchQueueResourcePolicies: batchQueueResourcePolicies,
-          }
-        });
+        this.navigateToComputeResourcePreference(computeResourcePreference, computeResourcePolicy, batchQueueResourcePolicies);
       },
       getComputeResourceName: function (computeResourceId) {
         // TODO: load compute resources to get the real name
@@ -173,14 +164,18 @@
         const computeResourcePreference = new models.GroupComputeResourcePreference();
         const computeResourceId = this.selectedComputeResource;
         computeResourcePreference.computeResourceId = computeResourceId;
+        this.navigateToComputeResourcePreference(computeResourcePreference);
+      },
+      navigateToComputeResourcePreference: function(computeResourcePreference, computeResourcePolicy=null, batchQueueResourcePolicies=null) {
+        const routeName = (this.id) ? 'compute_preference' : 'compute_preference_for_new_group_resource_profile';
         this.$router.push({
-          name: 'compute_preference', params: {
+          name: routeName, params: {
             value: computeResourcePreference,
-            id: this.data.groupResourceProfileId,
-            host_id: computeResourceId,
+            id: this.id,
+            host_id: computeResourcePreference.computeResourceId,
             groupResourceProfile: this.data,
-            computeResourcePolicy: null,
-            batchQueueResourcePolicies: null,
+            computeResourcePolicy: computeResourcePolicy,
+            batchQueueResourcePolicies: batchQueueResourcePolicies,
           }
         });
       }
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
index 87896c8..8fc5470 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
@@ -56,10 +56,8 @@
       },
       newGroupResourcePreference: function () {
         this.$router.push({
-          name: 'group_resource_preference', params: {
-            newCreation: true
-          }
-        })
+          name: 'new_group_resource_preference'
+        });
       },
       loadGroupResourceProfiles: function () {
         services.GroupResourceProfileService.list()
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/router.js b/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
index 6d54012..4f883d5 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
@@ -34,10 +34,18 @@ const routes = [
   {path: '/admin', component: AdminDashboard, name: "admin_dashboard"},
   {path: '/experiments', component: ExperimentsDashboard, name: 'experiments_dashboard'},
   {
+    path: '/group-resource-profiles/new', component: GroupComputeResourcePreference, name: 'new_group_resource_preference',
+    props: true
+  },
+  {
     path: '/group-resource-profiles/:id', component: GroupComputeResourcePreference, name: 'group_resource_preference',
     props: true
   },
   {
+    path: '/group-resource-profiles/new/compute-preferences/:host_id', component: ComputePreference, name: 'compute_preference_for_new_group_resource_profile',
+    props: true
+  },
+  {
     path: '/group-resource-profiles/:id/compute-preferences/:host_id', component: ComputePreference, name: 'compute_preference',
     props: true
   },
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
index 259cfcf..34d664e 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
@@ -11,16 +11,19 @@ const FIELDS = [
          name: 'computePreferences',
          type: GroupComputeResourcePreference,
          list: true,
+         default: BaseModel.defaultNewInstance(Array),
      },
      {
          name: 'computeResourcePolicies',
          type: ComputeResourcePolicy,
          list: true,
+         default: BaseModel.defaultNewInstance(Array),
      },
      {
          name: 'batchQueueResourcePolicies',
          type: BatchQueueResourcePolicy,
-         list: true
+         list: true,
+         default: BaseModel.defaultNewInstance(Array),
      },
      {
          name: 'creationTime',
@@ -43,30 +46,10 @@ export default class GroupResourceProfile extends BaseModel {
         return this.computeResourcePolicies.find(pol => pol.computeResourceId === computeResourceId);
     }
 
-    setComputeResourcePolicy(computeResourcePolicy) {
-        const currentPolicy = this.getComputeResourcePolicy(computeResourcePolicy.computeResourceId);
-        if (currentPolicy) {
-            Object.assign(currentPolicy, computeResourcePolicy);
-        } else {
-            this.computeResourcePolicies.push(computeResourcePolicy);
-        }
-    }
-
     getBatchQueueResourcePolicies(computeResourceId) {
         return this.batchQueueResourcePolicies.filter(pol => pol.computeResourceId === computeResourceId);
     }
 
-    setComputeResourcePolicy(batchQueueResourcePolicies) {
-        for (let newPolicy of batchQueueResourcePolicies) {
-            const currentPolicy = this.batchQueueResourcePolicies.find(pol => pol.resourcePolicyId === newPolicy.resourcePolicyId);
-            if (currentPolicy) {
-                Object.assign(currentPolicy, newPolicy);
-            } else {
-                this.batchQueueResourcePolicies.push(newPolicy);
-            }
-        }
-    }
-
     mergeComputeResourcePreference(computeResourcePreference, computeResourcePolicy, batchQueueResourcePolicies) {
         // merge/add computeResourcePreference and computeResourcePolicy
         const existingComputeResourcePreference = this.computePreferences.find(pref => pref.computeResourceId === computeResourcePreference.computeResourceId);


[airavata-django-portal] 07/13: AIRAVATA-2727 Added cancel button to compute preference page

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 2556b2eddca92e7567517b723b983a9321e8b006
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Mon Aug 13 13:15:22 2018 -0400

    AIRAVATA-2727 Added cancel button to compute preference page
---
 .../components/admin/group_resource_preferences/ComputePreference.vue | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
index 35ad166..87c72db 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
@@ -53,6 +53,7 @@
     <div class="row">
         <div class="col d-flex justify-content-end">
             <b-button variant="primary" @click="save">Save</b-button>
+            <b-button class="ml-2" variant="secondary" @click="cancel">Cancel</b-button>
         </div>
     </div>
   </div>
@@ -189,6 +190,9 @@
               console.log("Error occurred", error);
             });
         }
+      },
+      cancel: function() {
+        this.$router.back();
       }
     },
   }


[airavata-django-portal] 05/13: AIRAVATA-2727 Compute Preference editor plus policy editor

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit e92c7baa160d2dc8c180a9471003d9b4e9c66ce7
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Fri Aug 10 17:04:35 2018 -0400

    AIRAVATA-2727 Compute Preference editor plus policy editor
---
 .../BatchQueueResourcePolicy.vue                   |  83 +++++---
 .../ComputePreference.vue                          | 215 +++++++++------------
 .../GroupComputeResourcePreference.vue             |   4 +
 .../api/static/django_airavata_api/js/index.js     |   4 +
 4 files changed, 149 insertions(+), 157 deletions(-)

diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/BatchQueueResourcePolicy.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/BatchQueueResourcePolicy.vue
index 3928dae..a40aeb4 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/BatchQueueResourcePolicy.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/BatchQueueResourcePolicy.vue
@@ -1,41 +1,64 @@
 <template>
-  <div class="batch-queue-resource-policy">
-    <div class="entry">
-      <div class="heading">Maximum Allowed Nodes</div>
-      <input v-model="data.maxAllowedNodes" type="number"/>
+  <div class="row">
+    <div class="col">
+      <b-form-group label="Maximum Allowed Nodes" label-for="max-allowed-nodes">
+        <b-form-input id="max-allowed-nodes" type="number"
+          v-model="data.maxAllowedNodes" @input="policyUpdated"
+          min="1" :max="batchQueue.maxNodes"
+          :placeholder="'Max Nodes: ' + batchQueue.maxNodes">
+        </b-form-input>
+      </b-form-group>
     </div>
-    <div class="entry">
-      <div class="heading">Maximum Allowed Cores</div>
-      <input v-model="data.maxAllowedCores" type="number"/>
+    <div class="col">
+      <b-form-group label="Maximum Allowed Cores" label-for="max-allowed-cores">
+        <b-form-input id="max-allowed-cores" type="number"
+          v-model="data.maxAllowedCores" @input="policyUpdated"
+          min="1" :max="batchQueue.maxProcessors"
+          :placeholder="'Max Cores: ' + batchQueue.maxProcessors">
+        </b-form-input>
+      </b-form-group>
     </div>
-    <div class="entry">
-      <div class="heading">Maximum Allowed Wall Time</div>
-      <input v-model="data.maxAllowedWalltime" type="number"/>
+    <div class="col">
+      <b-form-group label="Maximum Allowed Wall Time" label-for="max-allowed-walltime">
+        <b-form-input id="max-allowed-walltime" type="number"
+          v-model="data.maxAllowedWalltime" @input="policyUpdated"
+          min="1" :max="batchQueue.maxRunTime"
+          :placeholder="'Max Wall Time: ' + batchQueue.maxRunTime">
+        </b-form-input>
+      </b-form-group>
     </div>
-  </div>
 </template>
 
 <script>
-  import VModelMixin from '../../commons/vmodel_mixin'
+  import { models } from 'django-airavata-api'
 
   export default {
     name: "batch-queue-resource-policy",
-    mixins: [VModelMixin]
+    props: {
+      value: {
+        required: false,
+        type: models.BatchQueueResourcePolicy
+      },
+      batchQueue: {
+        required: true,
+        type: models.BatchQueue,
+      },
+    },
+    data: function() {
+      const localValue = this.value ? this.value.clone() : new models.BatchQueueResourcePolicy();
+      localValue.queuename = this.batchQueue.queueName;
+      return {
+        data: localValue,
+      }
+    },
+    methods: {
+      policyUpdated: function() {
+        if (this.data.maxAllowedNodes || this.data.maxAllowedCores || this.data.maxAllowedWalltime) {
+          this.$emit('input', this.data);
+        } else {
+          this.$emit('input', null);
+        }
+      }
+    }
   }
-</script>
-
-<style scoped>
-  .batch-queue-resource-policy {
-    border: solid #dddddd 1px;
-    border-radius: 4px;
-    padding: 31px;
-    width: 80%;
-    font-size: small;
-    margin-left: 25px;
-    margin-bottom: 30px;
-  }
-
-  .batch-queue-resource-policy input {
-    height: 30px;
-  }
-</style>
+</script>
\ No newline at end of file
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
index f3fca9f..c83863c 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
@@ -1,136 +1,83 @@
 <template>
-  <transition name="fade">
-    <div class="new_app">
-      <div class="new_app_header">
-        <h3 style="display: inline-block">Compute Preference</h3>
-        <div class="new-application-tab-main">
-          <popup-component v-model="enablePopup" v-bind:enableClose="false">
-            <div class="entry">
-              <div class="heading">Select Compute Resource</div>
-              <select v-model="selectedComputeResourceIndex" size="4" class="popup-select">
-                <option v-bind:value="index" v-for="computeResource,index in computeResources">{{computeResource.host}}
-                </option>
-              </select>
-            </div>
-            <div class="entry">
-              <input class="vbtn vbtn-default" type="button" value="Continue" v-on:click="continueHandler"/>
-            </div>
-          </popup-component>
-          <div class="entry">
-            <div class="heading">Login User Name</div>
-            <input v-model="data.loginUserName" type="text"/>
-          </div>
-          <div class="entry">
-            <div class="heading">Preferred Batch Queue</div>
-            <select v-model="data.preferredBatchQueue">
-              <option v-bind:value="batchQueue.queueName" v-for="batchQueue,index in computeResource.batchQueues"
-                      v-bind:key="index">{{batchQueue.queueName}}
-              </option>
-            </select>
-          </div>
-          <div class="entry">
-            <div class="heading">Scratch Location</div>
-            <input v-model="data.scratchLocation" type="text"/>
-          </div>
-          <div class="entry">
-            <div class="heading">Allocation Project Number</div>
-            <input v-model="data.allocationProjectNumber" type="text"/>
-          </div>
-          <div class="entry">
-            <div class="heading">Resource Specific Credential Store Token</div>
-            <input v-model="data.resourceSpecificCredentialStoreToken" type="text"/>
-          </div>
-          <div class="entry">
-            <div class="heading">Usage Reporting Gateway ID</div>
-            <input v-model="data.usageReportingGatewayId" type="text"/>
-          </div>
-          <div class="entry">
-            <div class="heading">Quality of Service</div>
-            <input v-model="data.qualityOfService" type="text"/>
-          </div>
-          <div class="entry">
-            <div class="heading">Application argument</div>
-            <input v-model="data.reservation" type="text"/>
-          </div>
-          <div class="entry">
-            <div class="heading">SSH Account Provision Group / SSH Account Provisioner</div>
-            <input v-model="data.sshAccountProvisiogroupSSHAccountProvisionerConfigsner" type="text"/>
-          </div>
-          <div class="entry">
-            <div class="heading">SSH Account Provisioner Additional Info</div>
-            <input v-model="data.sshAccountProvisionerAdditionalInfo" type="text"/>
-          </div>
-          <div class="entry">
-            <div class="heading">Preferred Data Movement Protocol</div>
-            <select v-model="data.preferredDataMovementProtocol">
-              <option v-bind:value="dataMovementProtocol.value" v-for="dataMovementProtocol in dataMovementProtocols"
-                      v-if="dataMovementProtocol.enabled" v-bind:key="index">{{dataMovementProtocol.name}}
-              </option>
-            </select>
-          </div>
-          <div class="entry">
-            <div class="heading">Preferred Job Submission Protocol</div>
-            <select v-model="data.preferredJobSubmissionProtocol">
-              <option v-bind:value="jobSubmissionProtocol.value" v-for="jobSubmissionProtocol in jobSubmissionProtocols"
-                      v-if="jobSubmissionProtocol.enabled" v-bind:key="index">{{jobSubmissionProtocol.name}}
-              </option>
-            </select>
+  <div>
+    <div class="row">
+      <div class="col">
+        <h1 class="h4 mb-4">Compute Preference</h1>
+      </div>
+    </div>
+    <div class="row">
+      <div class="col">
+        <div class="card">
+          <div class="card-body">
+            <b-form-group label="Login Username" label-for="login-username">
+              <b-form-input id="login-username" type="text"
+                v-model="data.loginUserName">
+              </b-form-input>
+            </b-form-group>
+            <b-form-group label="Allocation Project Number" label-for="allocation-number">
+              <b-form-input id="allocation-number" type="text"
+                v-model="data.allocationProjectNumber">
+              </b-form-input>
+            </b-form-group>
+            <b-form-group label="Scratch Location" label-for="scratch-location">
+              <b-form-input id="scratch-location" type="text"
+                v-model="data.scratchLocation">
+              </b-form-input>
+            </b-form-group>
           </div>
-          <div class="new-application-tab-main">
-            <div class="deployment-entry">
-              <div class="heading">Group SSH Account Provisioner Configs</div>
-              <div class="name_value"
-                   v-for="groupSSHAccountProvisionerConfig,index in data.groupSSHAccountProvisionerConfigs"
-                   v-bind:key="index">
-                <input type="text" placeholder="Name"
-                       v-model="data.groupSSHAccountProvisionerConfigs[index].configName"/>
-                <input type="text" placeholder="Value"
-                       v-model="data.groupSSHAccountProvisionerConfigs[index].configValue"/>
+        </div>
+      </div>
+    </div>
+    <div class="row">
+      <div class="col">
+        <div class="card">
+          <div class="card-body">
+            <h5 class="card-title">Policy</h5>
+            <b-form-group label="Allowed Queues">
+              <div v-for="batchQueue in computeResource.batchQueues" :key="batchQueue.queueName">
+                <b-form-checkbox :checked="localComputeResourcePolicy.allowedBatchQueues.includes(batchQueue.queueName)"
+                  @input="batchQueueChecked(batchQueue, $event)">
+                  {{ batchQueue.queueName }}
+                </b-form-checkbox>
+                <batch-queue-resource-policy
+                  v-if="localComputeResourcePolicy.allowedBatchQueues.includes(batchQueue.queueName)"
+                  :batch-queue="batchQueue"
+                  :value="localBatchQueueResourcePolicies.find(pol => pol.queuename === batchQueue.queueName)"
+                  @input="updatedBatchQueueResourcePolicy(batchQueue, $event)"/>
               </div>
-              <input type="button" class="deployment btn" value="Add Config"
-                     v-on:click="createGroupSSHAccountProvisionerConfigs()"/>
-            </div>
-          </div>
-          <div class="sub-section-1">
-            <h4>Compute Resource Policies</h4>
-            <tab-sub-section v-for="computeResourcePolicy,index in data.computeResourcePolicies" v-bind:key="index"
-                             v-bind:enableDeletion="false" v-bind:section-name="'Compute Resource Policy'">
-              <compute-resource-policy v-model="data.computeResourcePolicies[index]" v-bind:computeResource="computeResource"></compute-resource-policy>
-            </tab-sub-section>
+            </b-form-group>
           </div>
         </div>
       </div>
     </div>
-  </transition>
+  </div>
 </template>
 
 <script>
 
-  import BooleanRadioButton from '../BooleanRadioButton'
-  import TabSubSection from '../../tabs/TabSubSection'
   import DjangoAiravataAPI from 'django-airavata-api'
-  import PopupComponent from '../../commons/PopupComponent'
-  import ComputeResourcePolicy from "./ComputeResourcePolicy";
   import VModelMixin from '../../commons/vmodel_mixin'
+  import BatchQueueResourcePolicy from './BatchQueueResourcePolicy.vue'
+
+  import {models} from 'django-airavata-api'
 
   export default {
     name: "compute-preference",
     components: {
-      PopupComponent,
-      ComputeResourcePolicy,
-      BooleanRadioButton,
-      TabSubSection,
+      BatchQueueResourcePolicy,
     },
     props: {
-      newCreation: {
-        type: Boolean,
-        default: false
-      },
       id: {
         type: String,
       },
       host_id: {
         type: String,
+      },
+      computeResourcePolicy: {
+        type: models.ComputeResourcePolicy
+      },
+      batchQueueResourcePolicies: {
+        type: Array
       }
     },
     mounted: function () {
@@ -144,21 +91,13 @@
       }
     },
     data: function () {
-      let data = this.value;
-      let enablePopup = false;
-      if (!data.computeResourcePolicies || data.computeResourcePolicies.length == 0) {
-        data.computeResourcePolicies = [];
-        data.computeResourcePolicies.push(this.createComputeResourcePolicy());
-      }
-      if(!this.host_id){
-        enablePopup = true;
-      }
       return {
-        enablePopup: enablePopup,
-        data: data,
+        data: this.value.clone(),
         selected: null,
         computeResources: [],
         selectedComputeResourceIndex: null,
+        localComputeResourcePolicy: this.computeResourcePolicy ? this.computeResourcePolicy.clone() : null,
+        localBatchQueueResourcePolicies: this.batchQueueResourcePolicies ? this.batchQueueResourcePolicies.map(pol => pol.clone()) : [],
         dataMovementProtocols: [{
           name: "LOCAL",
           enabled: false,
@@ -211,9 +150,6 @@
     },
     mixins: [VModelMixin],
     methods: {
-      boolValueHandler: function (id, value) {
-        this.data.overridebyAiravata = value
-      },
       fetchComputeResources: function () {
         return DjangoAiravataAPI.utils.FetchUtils.get('/api/compute-resources/all_names_list').then((value) => this.computeResources = value);
       },
@@ -226,6 +162,35 @@
           resourcePolicyId: null
         }
       },
+      batchQueueChecked: function(batchQueue, checked) {
+        if (checked) {
+          this.localComputeResourcePolicy.allowedBatchQueues.push(batchQueue.queueName);
+        } else {
+          const queueIndex = this.localComputeResourcePolicy.allowedBatchQueues.indexOf(batchQueue.queueName);
+          this.localComputeResourcePolicy.allowedBatchQueues.splice(queueIndex, 1);
+          // Remove batchQueueResourcePolicy if it exists
+          const policyIndex = this.localBatchQueueResourcePolicies.findIndex(pol => pol.queuename === batchQueue.queueName);
+          if (policyIndex >= 0) {
+            this.localBatchQueueResourcePolicies.splice(policyIndex, 1);
+          }
+        }
+      },
+      updatedBatchQueueResourcePolicy: function(batchQueue, batchQueueResourcePolicy) {
+        const queueName = batchQueue.queueName;
+        if (batchQueueResourcePolicy) {
+          const existingPolicy = this.localBatchQueueResourcePolicies.find(pol => pol.queuename === queueName);
+          if (existingPolicy) {
+            Object.assign(existingPolicy, batchQueueResourcePolicy);
+          } else {
+            this.localBatchQueueResourcePolicies.push(batchQueueResourcePolicy);
+          }
+        } else {
+          const existingPolicyIndex = this.localBatchQueueResourcePolicies.findIndex(pol => pol.queuename === queueName);
+          if (existingPolicyIndex >= 0) {
+            this.localBatchQueueResourcePolicies.splice(existingPolicyIndex, 1);
+          }
+        }
+      },
       createGroupSSHAccountProvisionerConfigs: function () {
         this.data.groupSSHAccountProvisionerConfigs.push({
           resourceId: this.data.computeResourceId,
@@ -246,10 +211,6 @@
             this.computeResource.dataMovementInterfaces.forEach((dataMovementInterface) => {
               this.dataMovementProtocols[dataMovementInterface.dataMovementProtocol].enabled = true;
             });
-            this.data.computeResourceId = value.computeResourceId;
-            this.data.computeResourcePolicies.forEach((value) => {
-              value.computeResourceId = this.data.computeResourceId;
-            })
           });
         }
       },
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
index 62d57e7..f6065c0 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
@@ -105,11 +105,15 @@
       },
       computePreferenceClickHandler: function (computeResourceId) {
         let computeResourcePreference = this.data.computePreferences.find(pref => pref.computeResourceId === computeResourceId);
+        const computeResourcePolicy = this.data.getComputeResourcePolicy(computeResourceId);
+        const batchQueueResourcePolicies = this.data.getBatchQueueResourcePolicies(computeResourceId);
         this.$router.push({
           name: 'compute_preference', params: {
             value: computeResourcePreference,
             id: this.data.groupResourceProfileId,
             host_id: computeResourceId,
+            computeResourcePolicy: computeResourcePolicy,
+            batchQueueResourcePolicies: batchQueueResourcePolicies,
           }
         });
       },
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/index.js b/django_airavata/apps/api/static/django_airavata_api/js/index.js
index 12c643b..1db9ad4 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/index.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/index.js
@@ -1,6 +1,8 @@
 
 import ApplicationInterfaceDefinition from './models/ApplicationInterfaceDefinition'
 import ApplicationModule from './models/ApplicationModule'
+import BatchQueue from './models/BatchQueue'
+import BatchQueueResourcePolicy from './models/BatchQueueResourcePolicy'
 import DataType from './models/DataType'
 import Experiment from './models/Experiment'
 import ExperimentState from './models/ExperimentState'
@@ -41,6 +43,8 @@ import PaginationIterator from './utils/PaginationIterator'
 exports.models = {
     ApplicationInterfaceDefinition,
     ApplicationModule,
+    BatchQueue,
+    BatchQueueResourcePolicy,
     DataType,
     Experiment,
     ExperimentState,