From 539955720a0da745a2da5a1233cb1f53e1f146bd Mon Sep 17 00:00:00 2001
From: cgeek <cem.moreau@gmail.com>
Date: Tue, 21 May 2019 18:29:20 +0200
Subject: [PATCH] [enh] Data > History of member view

---
 back/webmin/graphql/resolvers/BigResolver.ts  |  7 ++
 .../graphql/types/HistoryOfMemberType.ts      |  9 +++
 back/webmin/queries/gql-data.ts               | 70 +++++++++++++++++++
 src/lib/services/webmin.service.ts            | 19 +++++
 src/views/Home.vue                            |  4 +-
 src/views/home/Data.vue                       | 47 +++++++++++++
 src/views/home/data/HistoryOfMember.vue       | 43 ++++++++++++
 src/vue-modules/register-router.ts            | 17 ++++-
 8 files changed, 211 insertions(+), 5 deletions(-)
 create mode 100644 back/webmin/graphql/types/HistoryOfMemberType.ts
 create mode 100644 back/webmin/queries/gql-data.ts
 create mode 100644 src/views/home/Data.vue
 create mode 100644 src/views/home/data/HistoryOfMember.vue

diff --git a/back/webmin/graphql/resolvers/BigResolver.ts b/back/webmin/graphql/resolvers/BigResolver.ts
index 1ccc04b..8ae3685 100644
--- a/back/webmin/graphql/resolvers/BigResolver.ts
+++ b/back/webmin/graphql/resolvers/BigResolver.ts
@@ -14,6 +14,8 @@ import {getConf, testAndSaveConf} from '../../queries/gql-conf'
 import {Arg} from 'type-graphql/dist/decorators/Arg'
 import {getForks, getMainChain} from '../../queries/gql-forks'
 import {ChainType} from '../types/ChainType'
+import {HistoryOfMemberType} from '../types/HistoryOfMemberType'
+import {getHistoryOfMember} from '../../queries/gql-data'
 
 @Resolver()
 export class BigResolver {
@@ -83,6 +85,11 @@ export class BigResolver {
     return getMainChain(ApplicationContext.server, start, end)
   }
 
+  @Query(type => HistoryOfMemberType)
+  historyOfMember(@Arg("pub", type => String) pub: string): Promise<HistoryOfMemberType> {
+    return getHistoryOfMember(ApplicationContext.server, pub)
+  }
+
   @Query(type => ChainType)
   getForks(@Arg("start", type => Int) start: number, @Arg("end", type => Int) end: number): Promise<ChainType> {
     return getForks(ApplicationContext.server, start, end)
diff --git a/back/webmin/graphql/types/HistoryOfMemberType.ts b/back/webmin/graphql/types/HistoryOfMemberType.ts
new file mode 100644
index 0000000..5c6a14d
--- /dev/null
+++ b/back/webmin/graphql/types/HistoryOfMemberType.ts
@@ -0,0 +1,9 @@
+import {Field, Int, ObjectType} from 'type-graphql'
+import {BlockTransactionType} from './BlockTransactionType'
+import {BlockType} from './BlockType'
+
+@ObjectType()
+export class HistoryOfMemberType {
+
+  @Field(type => [String]) stories: string[]
+}
diff --git a/back/webmin/queries/gql-data.ts b/back/webmin/queries/gql-data.ts
new file mode 100644
index 0000000..7b5c3eb
--- /dev/null
+++ b/back/webmin/queries/gql-data.ts
@@ -0,0 +1,70 @@
+import {Server} from 'duniter/server'
+import {HistoryOfMemberType} from '../graphql/types/HistoryOfMemberType'
+import {CertificationDTO} from 'duniter/app/lib/dto/CertificationDTO'
+import {DBBlock} from 'duniter/app/lib/db/DBBlock'
+
+export async function getHistoryOfMember(server: Server, pub: string): Promise<HistoryOfMemberType> {
+  const stories = []
+  const start = Date.now()
+  const pubRegexp = new RegExp(pub)
+  const blocks = await Promise.all((await server.dal.blockDAL.findWithIdentities())
+    .concat(await server.dal.blockDAL.findWithCertifications())
+    .concat(await server.dal.blockDAL.findWithJoiners())
+    .concat(await server.dal.blockDAL.findWithActives())
+    .concat(await server.dal.blockDAL.findWithLeavers())
+    .concat(await server.dal.blockDAL.findWithExcluded())
+    .concat(await server.dal.blockDAL.findWithRevoked())
+    .map(async bNumber => (await server.dal.blockDAL.getBlock(bNumber)) as DBBlock))
+
+  blocks.forEach(b => {
+
+    const fNumber = String(b.number).padStart(6, '0')
+
+    b.identities
+      .filter(i => i.match(pubRegexp))
+      .forEach(i => stories.push(`b#${fNumber}: IDENTITY`))
+
+    const certifies = b.certifications
+      .map(CertificationDTO.fromInline)
+      .filter(c => c.from === pub)
+
+    if (certifies.length) {
+      stories.push(`b#${fNumber}: CERTIFIES ${certifies.length} member(s)`)
+    }
+
+    const certified = b.certifications
+      .map(CertificationDTO.fromInline)
+      .filter(c => c.to === pub)
+
+    if (certified.length) {
+      stories.push(`b#${fNumber}: CERTIFIED BY ${certified.length} member(s)`)
+    }
+
+    b.joiners
+      .filter(i => i.match(pubRegexp))
+      .forEach(i => stories.push(
+        `b#${fNumber}: JOIN`)
+      )
+
+    b.actives
+      .filter(i => i.match(pubRegexp))
+      .forEach(i => stories.push(
+        `b#${fNumber}: RENEW`)
+      )
+
+    b.leavers
+      .filter(i => i.match(pubRegexp))
+      .forEach(i => stories.push(
+        `b#${fNumber}: LEAVE`)
+      )
+
+    b.revoked
+      .filter(i => i.match(pubRegexp))
+      .forEach(i => stories.push(
+        `b#${fNumber}: REVOKED`)
+      )
+
+  })
+  stories.push(`scanned all the blocs in ${Date.now() - start}ms`)
+  return { stories }
+}
\ No newline at end of file
diff --git a/src/lib/services/webmin.service.ts b/src/lib/services/webmin.service.ts
index 149fb98..66f301f 100644
--- a/src/lib/services/webmin.service.ts
+++ b/src/lib/services/webmin.service.ts
@@ -1,6 +1,7 @@
 import gql from 'graphql-tag'
 import ApolloClient from 'apollo-client';
 import {DBBlock, NodeState, WS2PConnectionInfos, WS2PHead} from '../../../common/types'
+import {HistoryOfMemberType} from '../../../back/webmin/graphql/types/HistoryOfMemberType'
 import {ChainType} from '../../../back/webmin/graphql/types/ChainType'
 
 export class WebminService {
@@ -350,6 +351,24 @@ export class WebminService {
     return res.data.getMainChain
   }
 
+  async historyOfMember(pub: string) {
+    const res = await this.getApollo()
+    .watchQuery<{ historyOfMember: HistoryOfMemberType }>({
+      query: gql`
+          query ($pub: String!){
+              historyOfMember(pub: $pub) {
+                  stories
+              }
+          }
+      `,
+      variables: {
+        pub
+      },
+    })
+    .result()
+    return res.data.historyOfMember
+  }
+
   async getForks(start: number, end: number): Promise<ChainType> {
     const res = await this.getApollo()
     .watchQuery<{ getForks: ChainType }>({
diff --git a/src/views/Home.vue b/src/views/Home.vue
index 571ed50..5b96ad1 100644
--- a/src/views/Home.vue
+++ b/src/views/Home.vue
@@ -22,8 +22,8 @@
 
         <b-navbar-nav class="mr-auto">
           <b-nav-item>
-            <router-link to="home/forkview">
-              <button class="btn"><font-awesome-icon icon="cube"/></button>
+            <router-link to="/home/data">
+              <button class="btn"><font-awesome-icon icon="search"/></button>
             </router-link>
           </b-nav-item>
         </b-navbar-nav>
diff --git a/src/views/home/Data.vue b/src/views/home/Data.vue
new file mode 100644
index 0000000..b0ec967
--- /dev/null
+++ b/src/views/home/Data.vue
@@ -0,0 +1,47 @@
+<style lang="css" scoped>
+</style>
+
+<template>
+
+  <div class="container-fluid m-0 p-0">
+
+    <div class="row mt-2">
+      <div class="col-2">
+        <div class="nav flex-column nav-pills">
+
+          <router-link :to="menu.route" v-for="menu in menus" :key="menu.route">
+            <span class="nav-link" :class="{ 'active': currentRoute === menu.route }">{{ $t(menu.name )}}</span>
+          </router-link>
+        </div>
+      </div>
+
+      <div class="col-10">
+
+        <router-view></router-view>
+
+      </div>
+
+    </div>
+
+  </div>
+
+</template>
+
+<script lang="ts">
+  import {Component, Vue} from 'vue-property-decorator';
+
+  @Component({
+    components: {},
+  })
+  export default class extends Vue {
+
+    menus = [
+      { route: '/home/data', name: 'data.menu.forks' },
+      { route: '/home/data/history-of-member', name: 'data.menu.history.of.member' },
+    ]
+
+    get currentRoute() {
+      return this.$route.path
+    }
+  }
+</script>
diff --git a/src/views/home/data/HistoryOfMember.vue b/src/views/home/data/HistoryOfMember.vue
new file mode 100644
index 0000000..7a25310
--- /dev/null
+++ b/src/views/home/data/HistoryOfMember.vue
@@ -0,0 +1,43 @@
+<style lang="css" scoped>
+
+  .col-12.graph {
+    overflow: auto !important;
+  }
+
+  ul {
+    font-family: Consolas, "Courier New", Courier, monospace;
+  }
+</style>
+
+<template>
+
+  <div class="container-fluid">
+    <div class="row">
+      <div class="col-12">
+        <h3>History</h3>
+        <ul>
+          <li v-for="(s) in stories">{{ s }}</li>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+</template>
+
+<script lang="ts">
+  import {Component, Vue} from 'vue-property-decorator';
+  import ForkGraph from '@/components/ForkGraph.vue';
+
+  @Component({
+    components: {ForkGraph},
+  })
+  export default class extends Vue {
+
+    stories: string[] = []
+
+    async beforeMount() {
+      this.stories = (await this.$webmin.historyOfMember('2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ')).stories
+    }
+
+  }
+</script>
diff --git a/src/vue-modules/register-router.ts b/src/vue-modules/register-router.ts
index cb13b31..abca5b0 100644
--- a/src/vue-modules/register-router.ts
+++ b/src/vue-modules/register-router.ts
@@ -10,6 +10,8 @@ import Conf from '@/views/home/parameters/Conf.vue'
 import Network from '@/views/home/parameters/Network.vue'
 import Forkview from '@/views/home/Forkview.vue'
 import Language from '@/views/home/parameters/Language.vue'
+import Data from '@/views/home/Data.vue'
+import HistoryOfMember from '@/views/home/data/HistoryOfMember.vue'
 
 Vue.use(Router)
 
@@ -36,9 +38,18 @@ export default new Router({
         name: 'overview',
         component: Overview
       },{
-        path: 'forkview',
-        name: 'forkview',
-        component: Forkview
+        path: 'data',
+        name: 'data',
+        component: Data,
+        children: [{
+          path: '',
+          name: 'forkview',
+          component: Forkview,
+        },{
+          path: 'history-of-member',
+          name: 'history',
+          component: HistoryOfMember,
+        }]
       },{
         path: 'parameters',
         name: 'parameters',
-- 
GitLab