Add stats about the library

This commit is contained in:
Grégoire Delattre 2017-08-14 00:20:38 +02:00
parent dec9547152
commit eb02ff2b46
7 changed files with 108 additions and 6 deletions

View File

@ -0,0 +1,57 @@
package admin
import (
"net/http"
"github.com/jmoiron/sqlx"
"github.com/sirupsen/logrus"
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
)
const (
moviesCountQuery = `SELECT COUNT(*) FROM movies;`
showsCountQuery = `SELECT COUNT(*) FROM shows;`
episodesCountQuery = `SELECT COUNT(*) FROM episodes;`
)
// GetCount gets the count from a query
func GetCount(db *sqlx.DB, query string) (int, error) {
var count int
err := db.QueryRow(query).Scan(&count)
if err != nil {
return 0, err
}
return count, nil
}
// GetStatsHandler returns the stats of the app
func GetStatsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
log := env.Log.WithFields(logrus.Fields{
"function": "admin.GetStatsHandler",
})
log.Debug("getting stats")
stats := struct {
MoviesCount int `json:"movies_count"`
ShowsCount int `json:"shows_count"`
EpisodesCount int `json:"episodes_count"`
}{}
for _, s := range []struct {
query string
ptr *int
}{
{moviesCountQuery, &stats.MoviesCount},
{showsCountQuery, &stats.ShowsCount},
{episodesCountQuery, &stats.EpisodesCount},
} {
var err error
*s.ptr, err = GetCount(env.Database, s.query)
if err != nil {
return err
}
}
return env.RenderJSON(w, stats)
}

View File

@ -7,6 +7,13 @@ export function getUsers() {
) )
} }
export function getStats() {
return request(
"ADMIN_GET_STATS",
configureAxios().get("/admins/stats")
)
}
export function updateUser(data) { export function updateUser(data) {
return request( return request(
"ADMIN_UPDATE_USER", "ADMIN_UPDATE_USER",

View File

@ -4,10 +4,12 @@ import { bindActionCreators } from "redux"
import { updateUser } from "../../actions/admins" import { updateUser } from "../../actions/admins"
import UserList from "./users" import UserList from "./users"
import Stats from "./stats"
function mapStateToProps(state) { function mapStateToProps(state) {
return { return {
users : state.adminStore.get("users"), users : state.adminStore.get("users"),
stats : state.adminStore.get("stats"),
}; };
} }
const mapDispatchToProps = (dipatch) => const mapDispatchToProps = (dipatch) =>
@ -15,10 +17,15 @@ const mapDispatchToProps = (dipatch) =>
function AdminPanel(props) { function AdminPanel(props) {
return ( return (
<UserList <div>
users={props.users} <Stats
updateUser={props.updateUser} stats={props.stats}
/> />
<UserList
users={props.users}
updateUser={props.updateUser}
/>
</div>
); );
} }

View File

@ -0,0 +1,27 @@
import React from "react"
export default function Stats(props) {
return (
<div>
<h2 className="hidden-xs">Stats</h2>
<Stat name="Movies" value={props.stats.get("movies_count")} />
<Stat name="Shows" value={props.stats.get("shows_count")} />
<Stat name="Episodes" value={props.stats.get("episodes_count")} />
</div>
);
}
function Stat(props) {
return (
<div className="col-xs-4">
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title">{props.name}</h3>
</div>
<div className="panel-body">
{props.value}
</div>
</div>
</div>
);
}

View File

@ -2,10 +2,12 @@ import { Map, List, fromJS } from "immutable"
const defaultState = Map({ const defaultState = Map({
"users": List(), "users": List(),
"stats": Map({}),
}); });
const handlers = { const handlers = {
"ADMIN_LIST_USERS_FULFILLED": (state, action) => state.set("users", fromJS(action.payload.response.data)), "ADMIN_LIST_USERS_FULFILLED": (state, action) => state.set("users", fromJS(action.payload.response.data)),
"ADMIN_GET_STATS_FULFILLED": (state, action) => state.set("stats", fromJS(action.payload.response.data)),
} }
export default (state = defaultState, action) => export default (state = defaultState, action) =>

View File

@ -12,7 +12,7 @@ import { fetchTorrents } from "./actions/torrents"
import { userLogout, getUserInfos } from "./actions/users" import { userLogout, getUserInfos } from "./actions/users"
import { fetchMovies, getMovieExploreOptions } from "./actions/movies" import { fetchMovies, getMovieExploreOptions } from "./actions/movies"
import { fetchShows, fetchShowDetails, getShowExploreOptions } from "./actions/shows" import { fetchShows, fetchShowDetails, getShowExploreOptions } from "./actions/shows"
import { getUsers } from "./actions/admins" import { getUsers, getStats } from "./actions/admins"
import store from "./store" import store from "./store"
@ -122,7 +122,7 @@ export default function getRoutes(App) {
component: UserActivation, component: UserActivation,
onEnter: function(nextState, replace, next) { onEnter: function(nextState, replace, next) {
if (!isLoggedIn()) { if (!isLoggedIn()) {
replace('/users/login'); replace("/users/login");
} }
if (isActivated()) { if (isActivated()) {
// User is already activated, redirect him to the default route // User is already activated, redirect him to the default route
@ -254,6 +254,7 @@ export default function getRoutes(App) {
onEnter: function(nextState, replace, next) { onEnter: function(nextState, replace, next) {
adminCheck(nextState, replace, next, function() { adminCheck(nextState, replace, next, function() {
store.dispatch(getUsers()); store.dispatch(getUsers());
store.dispatch(getStats());
}); });
}, },
}, },

View File

@ -61,4 +61,5 @@ func setupRoutes(env *web.Env) {
// Admin routes // Admin routes
env.Handle("/admins/users", admin.GetUsersHandler).WithRole(users.AdminRole).Methods("GET") env.Handle("/admins/users", admin.GetUsersHandler).WithRole(users.AdminRole).Methods("GET")
env.Handle("/admins/users", admin.UpdateUserHandler).WithRole(users.AdminRole).Methods("POST") env.Handle("/admins/users", admin.UpdateUserHandler).WithRole(users.AdminRole).Methods("POST")
env.Handle("/admins/stats", admin.GetStatsHandler).WithRole(users.AdminRole).Methods("GET")
} }