From 9ab342bc2f593b58a2616ed9b61826dadf82fa68 Mon Sep 17 00:00:00 2001
From: robban64 <carl@schonfelder.se>
Date: Wed, 21 Apr 2021 10:55:55 +0200
Subject: [PATCH 1/3] add: statistics route

---
 client/src/actions/statistics.ts              | 15 ++++++++++++
 client/src/actions/types.ts                   |  1 +
 client/src/pages/admin/AdminPage.tsx          |  2 ++
 .../components/NumberOfCompetitions.tsx       | 21 +++++++---------
 .../dashboard/components/NumberOfRegions.tsx  |  2 +-
 .../dashboard/components/NumberOfUsers.tsx    |  2 +-
 client/src/reducers/allReducers.ts            |  2 ++
 client/src/reducers/statisticsReducer.ts      | 24 +++++++++++++++++++
 server/app/apis/misc.py                       | 13 +++++++++-
 server/app/apis/statistics.py                 | 21 ++++++++++++++++
 10 files changed, 87 insertions(+), 16 deletions(-)
 create mode 100644 client/src/actions/statistics.ts
 create mode 100644 client/src/reducers/statisticsReducer.ts
 create mode 100644 server/app/apis/statistics.py

diff --git a/client/src/actions/statistics.ts b/client/src/actions/statistics.ts
new file mode 100644
index 00000000..2444c4cf
--- /dev/null
+++ b/client/src/actions/statistics.ts
@@ -0,0 +1,15 @@
+import axios from 'axios'
+import { AppDispatch } from './../store'
+import Types from './types'
+
+export const getStatistics = () => async (dispatch: AppDispatch) => {
+  await axios
+    .get('/misc/statistics')
+    .then((res) => {
+      dispatch({
+        type: Types.SET_STATISTICS,
+        payload: res.data,
+      })
+    })
+    .catch((err) => console.log(err))
+}
diff --git a/client/src/actions/types.ts b/client/src/actions/types.ts
index a417f6af..189bf16c 100644
--- a/client/src/actions/types.ts
+++ b/client/src/actions/types.ts
@@ -29,4 +29,5 @@ export default {
   SET_CITIES_TOTAL: 'SET_CITIES_TOTAL',
   SET_CITIES_COUNT: 'SET_CITIES_COUNT',
   SET_TYPES: 'SET_TYPES',
+  SET_STATISTICS: 'SET_STATISTICS',
 }
diff --git a/client/src/pages/admin/AdminPage.tsx b/client/src/pages/admin/AdminPage.tsx
index 30cfe656..1b543083 100644
--- a/client/src/pages/admin/AdminPage.tsx
+++ b/client/src/pages/admin/AdminPage.tsx
@@ -20,6 +20,7 @@ import React, { useEffect } from 'react'
 import { Link, Route, Switch, useRouteMatch } from 'react-router-dom'
 import { getCities } from '../../actions/cities'
 import { getRoles } from '../../actions/roles'
+import { getStatistics } from '../../actions/statistics'
 import { getTypes } from '../../actions/typesAction'
 import { logoutUser } from '../../actions/user'
 import { useAppDispatch, useAppSelector } from '../../hooks'
@@ -71,6 +72,7 @@ const AdminView: React.FC = () => {
     dispatch(getCities())
     dispatch(getRoles())
     dispatch(getTypes())
+    dispatch(getStatistics())
   }, [])
 
   const menuAdminItems = [
diff --git a/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx b/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx
index 87d8272a..eb667ebd 100644
--- a/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx
+++ b/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx
@@ -1,24 +1,19 @@
 import { Box, Typography } from '@material-ui/core'
-import React, { useEffect } from 'react'
-import { getCompetitions } from '../../../../actions/competitions'
-import { useAppDispatch, useAppSelector } from '../../../../hooks'
+import React from 'react'
+import { useAppSelector } from '../../../../hooks'
 
 const NumberOfCompetitions: React.FC = () => {
-  const competitions = useAppSelector((state) => state.competitions.competitions)
-  const dispatch = useAppDispatch()
+  const competitions = useAppSelector((state) => state.statistics.competitions)
 
   const handleCount = () => {
-    if (competitions.length >= 1000000) {
-      ;<div>{competitions.length / 1000000 + 'M'}</div>
-    } else if (competitions.length >= 1000) {
-      ;<div>{competitions.length / 1000 + 'K'}</div>
+    if (competitions >= 1000000) {
+      ;<div>{competitions / 1000000 + 'M'}</div>
+    } else if (competitions >= 1000) {
+      ;<div>{competitions / 1000 + 'K'}</div>
     }
-    return <div>{competitions.length}</div>
+    return <div>{competitions}</div>
   }
 
-  useEffect(() => {
-    dispatch(getCompetitions())
-  }, [])
   return (
     <div>
       <Box width="100%" height="100%">
diff --git a/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx b/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx
index 360b3663..f3195f4c 100644
--- a/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx
+++ b/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx
@@ -4,7 +4,7 @@ import { getCities } from '../../../../actions/cities'
 import { useAppDispatch, useAppSelector } from '../../../../hooks'
 
 const NumberOfRegions: React.FC = () => {
-  const regions = useAppSelector((state) => state.cities.total)
+  const regions = useAppSelector((state) => state.statistics.regions)
   const dispatch = useAppDispatch()
 
   const handleCount = () => {
diff --git a/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx b/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx
index af0f9767..0e75cf1b 100644
--- a/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx
+++ b/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx
@@ -4,7 +4,7 @@ import { getSearchUsers } from '../../../../actions/searchUser'
 import { useAppDispatch, useAppSelector } from '../../../../hooks'
 
 const NumberOfUsers: React.FC = () => {
-  const usersTotal = useAppSelector((state) => state.searchUsers.total)
+  const usersTotal = useAppSelector((state) => state.statistics.users)
   const dispatch = useAppDispatch()
 
   const handleCount = () => {
diff --git a/client/src/reducers/allReducers.ts b/client/src/reducers/allReducers.ts
index 2aee4697..c23384c2 100644
--- a/client/src/reducers/allReducers.ts
+++ b/client/src/reducers/allReducers.ts
@@ -7,6 +7,7 @@ import editorReducer from './editorReducer'
 import presentationReducer from './presentationReducer'
 import rolesReducer from './rolesReducer'
 import searchUserReducer from './searchUserReducer'
+import statisticsReducer from './statisticsReducer'
 import typesReducer from './typesReducer'
 import uiReducer from './uiReducer'
 import userReducer from './userReducer'
@@ -22,5 +23,6 @@ const allReducers = combineReducers({
   roles: rolesReducer,
   searchUsers: searchUserReducer,
   types: typesReducer,
+  statistics: statisticsReducer,
 })
 export default allReducers
diff --git a/client/src/reducers/statisticsReducer.ts b/client/src/reducers/statisticsReducer.ts
new file mode 100644
index 00000000..78a06e11
--- /dev/null
+++ b/client/src/reducers/statisticsReducer.ts
@@ -0,0 +1,24 @@
+import { AnyAction } from 'redux'
+import Types from '../actions/types'
+
+interface StatisticsState {
+  users: number
+  competitions: number
+  regions: number
+}
+
+const initialState: StatisticsState = {
+  users: 0,
+  competitions: 0,
+  regions: 0,
+}
+
+export default function (state = initialState, action: AnyAction) {
+  switch (action.type) {
+    case Types.SET_STATISTICS:
+      state = action.payload as StatisticsState
+      return state
+    default:
+      return state
+  }
+}
diff --git a/server/app/apis/misc.py b/server/app/apis/misc.py
index 5364a5e4..8b3bd5e5 100644
--- a/server/app/apis/misc.py
+++ b/server/app/apis/misc.py
@@ -1,7 +1,8 @@
 import app.database.controller as dbc
 from app.apis import check_jwt, item_response, list_response
+from app.core import http_codes
 from app.core.dto import MiscDTO
-from app.database.models import City, ComponentType, MediaType, QuestionType, Role, ViewType
+from app.database.models import City, Competition, ComponentType, MediaType, QuestionType, Role, User, ViewType
 from flask_jwt_extended import jwt_required
 from flask_restx import Resource, reqparse
 
@@ -72,3 +73,13 @@ class Cities(Resource):
         dbc.delete.default(item)
         items = dbc.get.all(City)
         return list_response(city_schema.dump(items))
+
+
+@api.route("/statistics")
+class Statistics(Resource):
+    @check_jwt(editor=True)
+    def get(self):
+        user_count = User.query.count()
+        competition_count = Competition.query.count()
+        region_count = City.query.count()
+        return {"users": user_count, "competitions": competition_count, "regions": region_count}, http_codes.OK
diff --git a/server/app/apis/statistics.py b/server/app/apis/statistics.py
new file mode 100644
index 00000000..85992521
--- /dev/null
+++ b/server/app/apis/statistics.py
@@ -0,0 +1,21 @@
+import app.core.http_codes as codes
+import app.database.controller as dbc
+from app.apis import check_jwt, item_response, list_response
+from app.core.dto import UserDTO
+from app.core.parsers import user_parser, user_search_parser
+from app.database.models import City, Competition, User
+from flask import request
+from flask_jwt_extended import get_jwt_identity, jwt_required
+from flask_restx import Namespace, Resource
+
+api = Namespace("statistics")
+
+
+@api.route("/")
+class Statistics(Resource):
+    @check_jwt(editor=True)
+    def get(self):
+        user_count = User.query.count()
+        competition_count = Competition.query.count()
+        region_count = City.query.count()
+        return {"users": user_count, "competitions": competition_count, "regions": region_count}, codes.OK
-- 
GitLab


From de716e9d030c146ff35b4ecdc63252802332e1c5 Mon Sep 17 00:00:00 2001
From: robban64 <carl@schonfelder.se>
Date: Wed, 21 Apr 2021 11:04:13 +0200
Subject: [PATCH 2/3] fix: file changes

---
 server/app/apis/statistics.py | 21 ---------------------
 1 file changed, 21 deletions(-)
 delete mode 100644 server/app/apis/statistics.py

diff --git a/server/app/apis/statistics.py b/server/app/apis/statistics.py
deleted file mode 100644
index 85992521..00000000
--- a/server/app/apis/statistics.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import app.core.http_codes as codes
-import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
-from app.core.dto import UserDTO
-from app.core.parsers import user_parser, user_search_parser
-from app.database.models import City, Competition, User
-from flask import request
-from flask_jwt_extended import get_jwt_identity, jwt_required
-from flask_restx import Namespace, Resource
-
-api = Namespace("statistics")
-
-
-@api.route("/")
-class Statistics(Resource):
-    @check_jwt(editor=True)
-    def get(self):
-        user_count = User.query.count()
-        competition_count = Competition.query.count()
-        region_count = City.query.count()
-        return {"users": user_count, "competitions": competition_count, "regions": region_count}, codes.OK
-- 
GitLab


From 64f1f4442744f2aa720c048e786ba43c4176cd29 Mon Sep 17 00:00:00 2001
From: robban64 <carl@schonfelder.se>
Date: Fri, 23 Apr 2021 09:13:19 +0200
Subject: [PATCH 3/3] add: api/ string to getStatistics

---
 client/src/actions/statistics.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/client/src/actions/statistics.ts b/client/src/actions/statistics.ts
index 2444c4cf..a32dce35 100644
--- a/client/src/actions/statistics.ts
+++ b/client/src/actions/statistics.ts
@@ -4,7 +4,7 @@ import Types from './types'
 
 export const getStatistics = () => async (dispatch: AppDispatch) => {
   await axios
-    .get('/misc/statistics')
+    .get('/api/misc/statistics')
     .then((res) => {
       dispatch({
         type: Types.SET_STATISTICS,
-- 
GitLab