From 3aac16a7e82d4d093234519c7ecc5af37d79d64a Mon Sep 17 00:00:00 2001
From: Sonia Zorba <sonia.zorba@inaf.it>
Date: Mon, 24 Aug 2020 20:12:16 +0200
Subject: [PATCH] Completed download CSV status feature

---
 gms-ui/src/components/GroupsBreadcrumb.vue    | 18 ++++++++++++---
 gms-ui/src/main.js                            |  4 ++--
 .../ia2/gms/controller/GroupsController.java  | 22 +++++++++++++++++++
 .../ia2/gms/manager/GroupStatusManager.java   | 15 ++++++++++---
 4 files changed, 51 insertions(+), 8 deletions(-)

diff --git a/gms-ui/src/components/GroupsBreadcrumb.vue b/gms-ui/src/components/GroupsBreadcrumb.vue
index 283c847..f1e6f6c 100644
--- a/gms-ui/src/components/GroupsBreadcrumb.vue
+++ b/gms-ui/src/components/GroupsBreadcrumb.vue
@@ -1,11 +1,14 @@
 <template>
-<nav aria-label="breadcrumb">
+<nav aria-label="breadcrumb" id="groups-breadcrumbs">
   <ol class="breadcrumb">
     <li class="breadcrumb-item" v-for="group in groups" v-bind:class="{ active: group.active }" v-bind:key="group.groupId">
       <a href="#" v-on:click="changeBreadcrumb(group.groupId)" v-if="!group.active">{{group.groupName}}</a>
       <span v-if="group.active">{{group.groupName}}</span>
     </li>
   </ol>
+  <a :href="'group/status/' + currentGroup.groupId" :download="currentGroup.groupName + '.csv'" id="csv-status-download" title="Download CSV">
+    <font-awesome-icon icon="download"></font-awesome-icon>
+  </a>
 </nav>
 </template>
 
@@ -35,7 +38,9 @@ export default {
   computed: mapState({
     model: state => state.model,
     input: state => state.input,
-    groups: state => buildItems(state.model.breadcrumbs)
+    groups: state => buildItems(state.model.breadcrumbs),
+    isAdmin: state => state.model.permission === 'ADMIN',
+    currentGroup: state => state.model.breadcrumbs[state.model.breadcrumbs.length - 1]
   }),
   methods: {
     changeBreadcrumb: function(groupId) {
@@ -55,7 +60,14 @@ export default {
 </script>
 
 <style scoped>
-nav {
+#groups-breadcrumbs {
   margin-top: 10px;
+  position: relative;
+}
+
+#csv-status-download {
+  position: absolute;
+  right: 18px;
+  top: 12px;
 }
 </style>
diff --git a/gms-ui/src/main.js b/gms-ui/src/main.js
index 255ab43..2a64d2f 100644
--- a/gms-ui/src/main.js
+++ b/gms-ui/src/main.js
@@ -5,14 +5,14 @@ import store from './store.js'
 import './plugins/bootstrap-vue'
 import App from './App.vue'
 import { library } from '@fortawesome/fontawesome-svg-core'
-import { faTrash, faEdit, faSpinner, faFolder, faUser, faSave } from '@fortawesome/free-solid-svg-icons'
+import { faTrash, faEdit, faSpinner, faFolder, faUser, faSave, faDownload } from '@fortawesome/free-solid-svg-icons'
 import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
 
 import VueRouter from 'vue-router'
 Vue.use(VueRouter)
 import router from './router.js';
 
-library.add(faTrash, faEdit, faSpinner, faFolder, faUser, faSave);
+library.add(faTrash, faEdit, faSpinner, faFolder, faUser, faSave, faDownload);
 
 Vue.component('font-awesome-icon', FontAwesomeIcon);
 
diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java
index 8b1f13d..fe9137e 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java
@@ -1,6 +1,8 @@
 package it.inaf.ia2.gms.controller;
 
+import com.opencsv.CSVWriter;
 import it.inaf.ia2.gms.authn.SessionData;
+import it.inaf.ia2.gms.manager.GroupStatusManager;
 import it.inaf.ia2.gms.manager.GroupsManager;
 import it.inaf.ia2.gms.model.request.AddGroupRequest;
 import it.inaf.ia2.gms.model.GroupNode;
@@ -12,6 +14,9 @@ import it.inaf.ia2.gms.model.request.RenameGroupRequest;
 import it.inaf.ia2.gms.model.request.SearchFilterRequest;
 import it.inaf.ia2.gms.persistence.model.GroupEntity;
 import it.inaf.ia2.gms.service.GroupsTreeBuilder;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
@@ -40,6 +45,9 @@ public class GroupsController {
     @Autowired
     private GroupsTabResponseBuilder groupsTabResponseBuilder;
 
+    @Autowired
+    private GroupStatusManager groupStatusManager;
+
     @GetMapping(value = "/groups", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<?> getGroupsTab(@Valid GroupsRequest request) {
         if (request.isOnlyPanel()) {
@@ -85,6 +93,20 @@ public class GroupsController {
         return ResponseEntity.ok(groupsPanel);
     }
 
+    @GetMapping(value = "/group/status/{groupId}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
+    public void downloadStatus(@PathVariable("groupId") String groupId, HttpServletResponse response) throws Exception {
+
+        try (OutputStream out = response.getOutputStream();
+                CSVWriter writer = new CSVWriter(new OutputStreamWriter(out))) {
+
+            writer.writeNext(new String[]{"program", "email"});
+
+            for (String[] row : groupStatusManager.generateStatus(groupId)) {
+                writer.writeNext(row);
+            }
+        }
+    }
+
     private <T extends PaginatedModelRequest & SearchFilterRequest> PaginatedData<GroupNode> getGroupsPanel(GroupEntity parentGroup, T request) {
         return groupsTreeBuilder.listSubGroups(parentGroup, request, session.getUserId());
     }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/manager/GroupStatusManager.java b/gms/src/main/java/it/inaf/ia2/gms/manager/GroupStatusManager.java
index e6d2b38..07cbb42 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/manager/GroupStatusManager.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/manager/GroupStatusManager.java
@@ -9,7 +9,9 @@ import it.inaf.ia2.gms.persistence.model.GroupEntity;
 import it.inaf.ia2.gms.persistence.model.MembershipEntity;
 import it.inaf.ia2.gms.rap.RapClient;
 import it.inaf.ia2.gms.service.GroupNameService;
+import it.inaf.ia2.gms.service.GroupsService;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -27,6 +29,9 @@ public class GroupStatusManager extends UserAwareComponent {
     @Autowired
     private PermissionsManager permissionsManager;
 
+    @Autowired
+    private GroupsService groupsService;
+
     @Autowired
     private GroupsDAO groupsDAO;
 
@@ -39,7 +44,9 @@ public class GroupStatusManager extends UserAwareComponent {
     @Autowired
     private RapClient rapClient;
 
-    public List<Object[]> generateStatus(GroupEntity parentGroup) {
+    public List<String[]> generateStatus(String groupId) {
+
+        GroupEntity parentGroup = groupsService.getGroupById(groupId);
 
         Permission groupPermission = permissionsManager.getCurrentUserPermission(parentGroup);
 
@@ -72,7 +79,7 @@ public class GroupStatusManager extends UserAwareComponent {
             usersMap.put(user.getId(), user.getPrimaryEmail());
         }
 
-        List<Object[]> rows = new ArrayList<>();
+        List<String[]> rows = new ArrayList<>();
 
         for (int i = 0; i < groups.size(); i++) {
             GroupEntity group = groups.get(i);
@@ -85,12 +92,14 @@ public class GroupStatusManager extends UserAwareComponent {
                         LOG.warn("Unable to retrieve information about user " + userId);
                         continue;
                     }
-                    Object[] row = new Object[]{groupName, email};
+                    String[] row = new String[]{groupName, email};
                     rows.add(row);
                 }
             }
         }
 
+        Collections.sort(rows, (r1, r2) -> (r1[0]).compareTo(r2[0]));
+
         return rows;
     }
 }
-- 
GitLab