Commit de503827 by jhrabal

radegast

parent 6a7a102c
package com.jh.radegast.api; package com.jh.radegast.api;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.List; import java.util.List;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -11,6 +10,7 @@ import org.springframework.stereotype.Controller; ...@@ -11,6 +10,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import com.jh.common.web.list.DefaultSorting; import com.jh.common.web.list.DefaultSorting;
...@@ -28,7 +28,6 @@ public class RatingApiController { ...@@ -28,7 +28,6 @@ public class RatingApiController {
@Autowired @Autowired
private RatingService service; private RatingService service;
//paged endpoint
@RequestMapping(path = "ratings", method = RequestMethod.GET) @RequestMapping(path = "ratings", method = RequestMethod.GET)
@DefaultSorting(field = "key", trend = SortTrend.DESCENDING) @DefaultSorting(field = "key", trend = SortTrend.DESCENDING)
public ResponseEntity<List<Rating>> filterRatings(/*Date from, Date to, */PagingInfo pagingInfo) { public ResponseEntity<List<Rating>> filterRatings(/*Date from, Date to, */PagingInfo pagingInfo) {
...@@ -42,6 +41,9 @@ public class RatingApiController { ...@@ -42,6 +41,9 @@ public class RatingApiController {
service.saveBulk(ratings); service.saveBulk(ratings);
} }
//stats endpoint @RequestMapping(path = "ratings/stats", method = RequestMethod.GET)
public @ResponseBody RatingsStats ratingsStats(/*Date from, Date to */) {
return service.calculateStats(new RatingsFilter());
}
} }
package com.jh.radegast.api; package com.jh.radegast.api;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.LinkedHashMap;
public class RatingsStats { public class RatingsStats {
private Long ratings;
private BigDecimal average;
private HashMap<Long, Long> values;
public RatingsStats() { public RatingsStats() {
this.values = new LinkedHashMap<>();
ratings = 0L;
}
public RatingsStats(Long ratings, BigDecimal average, HashMap<Long, Long> values) {
super();
this.ratings = ratings;
this.values = values;
this.average = average;
}
public Long getRatings() {
return ratings;
}
public void setRatings(Long ratings) {
this.ratings = ratings;
}
public HashMap<Long, Long> getValues() {
return values;
}
public void setValues(HashMap<Long, Long> values) {
this.values = values;
}
public BigDecimal getAverage() {
return average;
}
public void setAverage(BigDecimal average) {
this.average = average;
} }
} }
package com.jh.radegast.model;
public class RatingStat {
private Long rating;
private Long count;
public RatingStat() {
}
public RatingStat(Long rating, Long count) {
super();
this.rating = rating;
this.count = count;
}
public Long getRating() {
return rating;
}
public void setRating(Long rating) {
this.rating = rating;
}
public Long getCount() {
return count;
}
public void setCount(Long count) {
this.count = count;
}
}
package com.jh.radegast.rating; package com.jh.radegast.rating;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
...@@ -8,6 +9,7 @@ import java.util.Set; ...@@ -8,6 +9,7 @@ import java.util.Set;
import org.hibernate.Criteria; import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions; import org.hibernate.criterion.Restrictions;
import org.hibernate.query.NativeQuery;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import com.jh.common.jpa.AbstractHibernateRepository; import com.jh.common.jpa.AbstractHibernateRepository;
...@@ -15,6 +17,7 @@ import com.jh.common.web.list.Page; ...@@ -15,6 +17,7 @@ import com.jh.common.web.list.Page;
import com.jh.common.web.list.PagingInfo; import com.jh.common.web.list.PagingInfo;
import com.jh.radegast.api.RatingsFilter; import com.jh.radegast.api.RatingsFilter;
import com.jh.radegast.model.Rating; import com.jh.radegast.model.Rating;
import com.jh.radegast.model.RatingStat;
@Repository @Repository
...@@ -67,4 +70,38 @@ public class RatingRepository extends AbstractHibernateRepository { ...@@ -67,4 +70,38 @@ public class RatingRepository extends AbstractHibernateRepository {
} }
public List<RatingStat> stats(RatingsFilter filter) {
StringBuilder sql = new StringBuilder("select rating, count(*) from rating where 1=1");
if (filter != null) {
if (filter.getFrom() != null) {
sql.append(" and key >= :from ");
}
if (filter.getTo() != null) {
sql.append(" and key <= :to ");
}
}
sql.append(" group by rating");
NativeQuery query = getSession().createNativeQuery(sql.toString());
if (filter != null) {
if (filter.getFrom() != null) {
query.setParameter("from", String.valueOf(filter.getFrom().getTime()));
}
if (filter.getTo() != null) {
query.setParameter("to", String.valueOf(filter.getTo().getTime()));
}
}
List<RatingStat> stats = new ArrayList<>();
List<Object[]> rows = query.list();
if (rows != null) {
for (Object[] row : rows) {
stats.add(new RatingStat(((Number) row[0]).longValue(), ((Number) row[1]).longValue()));
}
}
return stats;
}
} }
package com.jh.radegast.rating; package com.jh.radegast.rating;
import java.math.BigDecimal;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.StringJoiner; import java.util.StringJoiner;
...@@ -12,10 +15,13 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -12,10 +15,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import com.jh.common.utils.Utils;
import com.jh.common.web.list.Page; import com.jh.common.web.list.Page;
import com.jh.common.web.list.PagingInfo; import com.jh.common.web.list.PagingInfo;
import com.jh.radegast.api.RatingsFilter; import com.jh.radegast.api.RatingsFilter;
import com.jh.radegast.api.RatingsStats;
import com.jh.radegast.model.Rating; import com.jh.radegast.model.Rating;
import com.jh.radegast.model.RatingStat;
@Service @Service
public class RatingService { public class RatingService {
...@@ -69,4 +75,32 @@ public class RatingService { ...@@ -69,4 +75,32 @@ public class RatingService {
return repo.filter(ratingsFilter); return repo.filter(ratingsFilter);
} }
@Transactional
public RatingsStats calculateStats(RatingsFilter filter) {
Long total = 0L;
Long value = 0L;
BigDecimal average = BigDecimal.ZERO;
HashMap<Long, Long> ratings = new LinkedHashMap<>();
for (int i = 10; i > 0; i--) {
ratings.put(Long.valueOf(i), 0L);
}
List<RatingStat> list = repo.stats(filter);
if (list != null) {
for (RatingStat rs : list) {
ratings.put(rs.getRating(), rs.getCount());
total += rs.getCount();
value += rs.getRating() * rs.getCount();
}
}
if (total > 0) {
average = Utils.divide(new BigDecimal(value), new BigDecimal(total), 2);
}
return new RatingsStats(total, average, ratings);
}
} }
...@@ -9,10 +9,7 @@ class UserInfo extends Component { ...@@ -9,10 +9,7 @@ class UserInfo extends Component {
render() { render() {
let { user, name } = this.props; let { user, name } = this.props;
if (!user) { if (!user) {
user = ( window.cfg && window.cfg.user ) || {}; user = ( window.cfg && window.cfg.principal ) || {};
}
if (!name) {
name = window.cfg && window.cfg.unit && window.cfg.unit.name;
} }
let img = user.avatar || require("./user-placeholder.png"); let img = user.avatar || require("./user-placeholder.png");
...@@ -21,11 +18,7 @@ class UserInfo extends Component { ...@@ -21,11 +18,7 @@ class UserInfo extends Component {
<div className="user-placeholder"> <div className="user-placeholder">
<img src={ img } /> <img src={ img } />
<br/> <br/>
<span className="user-name">{ name || (user.firstName + " " + user.lastName) }</span> <span className="user-name">{ user && (user.firstName + " " + user.lastName) }</span>
<br/>
<span>{ user.email || "" }</span>
<br/>
<span>{ user.position || "" }</span>
</div> </div>
); );
......
...@@ -14,7 +14,6 @@ export default () => new Promise((resolve) => { ...@@ -14,7 +14,6 @@ export default () => new Promise((resolve) => {
global._transitions = true; //FIXME global._transitions = true; //FIXME
//set colors //set colors
let chartColors = ["4ec3e0", "1287ed", "929ba3"]; //, "678096", "5beeff"]; let chartColors = ["4ec3e0", "1287ed", "929ba3"]; //, "678096", "5beeff"];
//let pal = palette(chartColors, 17); //let pal = palette(chartColors, 17);
......
...@@ -22,8 +22,8 @@ export default injectIntl((props) => { ...@@ -22,8 +22,8 @@ export default injectIntl((props) => {
let menuItems = [ let menuItems = [
{ {
id: 'home', id: 'stats',
route: '/home', route: '/stats',
title: intl.formatMessage(messages.homeMenu), title: intl.formatMessage(messages.homeMenu),
icon: icons.reports icon: icons.reports
}, { }, {
......
...@@ -127,6 +127,10 @@ ...@@ -127,6 +127,10 @@
color: #009A00; color: #009A00;
} }
.list-row-data .main-value.neutral-amount {
color: #f5ab00;
}
.list-row-data .main-value.minus-amount { .list-row-data .main-value.minus-amount {
color: #FF0000; color: #FF0000;
} }
......
...@@ -18,31 +18,19 @@ let initialState = { ...@@ -18,31 +18,19 @@ let initialState = {
status: 'pending', status: 'pending',
fresh: true, fresh: true,
paging: { paging: {
pageSize: 7 pageSize: 15
}, },
data: [] data: []
}, },
summary: { stats: {
status: 'pending', status: 'pending',
},
cashflow: {
status: 'pending',
fresh: true,
data: {
empty: true
}
},
statuses: {
status: 'pending',
fresh: true,
data: { data: {
income: {}, total: 0,
outcome: {}, values: {
data: {},
empty: true
} }
} }
},
}; };
let reducer = createReducer(initialState); let reducer = createReducer(initialState);
...@@ -55,97 +43,41 @@ let api = reduxList("home/dashboard", { ...@@ -55,97 +43,41 @@ let api = reduxList("home/dashboard", {
}); });
//export actions //export actions
export const fetchDashboardRecordsAction = api.actions.fetchList; export const fetchRatingsAction = api.actions.fetchList;
//TODO //TODO
export const selector = settingsSelector((state, props) => { export const selector = settingsSelector((state, props) => {
let home = safe(state.get("home")); let home = safe(state.get("home"));
let periodParams = safe(state.getIn(["params", "period"]));
let list = api.selector(state, props); let list = api.selector(state, props);
return { ...home, periodParams }; return home;
}); });
export function fetchSummary(unitId, from, to, dateType) { export function fetchStatsAction(unitId, from, to, dateType) {
return {
type: 'home/FETCH_SUMMARY',
payload: backendRequest('/api/' + unitId + '/home/dashboard', 'GET', { params: { from, to } })
};
};
reducer.handleAction('home/FETCH_SUMMARY_PENDING', (state, action) =>
state.setIn(["summary", "status"], "pending")
);
reducer.handleAction('home/FETCH_SUMMARY_FULFILLED', (state, action) =>
state
.setIn(["summary", "status"], "success")
.setIn(["summary", "data"], action.payload.body)
.deleteIn(["summary", "error"])
);
reducer.handleAction('home/FETCH_SUMMARY_REJECTED', (state, action) =>
state.setIn(["summary", "status"], "failed").deleteIn(["summary", "data"]).setIn(["summary", "error"], true)
);
export function fetchCashflow(unitId, from, to, dateType) {
return { return {
type: 'home/FETCH_CASHFLOW', type: 'home/FETCH_STATS',
payload: backendRequest('/api/' + unitId + '/home/cashflow', 'GET', { params: { from, to, dateType } }) payload: backendRequest('/api/ratings/stats', 'GET', { params: { from, to } })
}; };
}; };
reducer.handleAction('home/FETCH_CASHFLOW_PENDING', (state, action) => reducer.handleAction('home/FETCH_STATS_PENDING', (state, action) =>
state.setIn(["cashflow", "status"], "pending") state.setIn(["stats", "status"], "pending")
); );
reducer.handleAction('home/FETCH_CASHFLOW_FULFILLED', (state, action) => reducer.handleAction('home/FETCH_STATS_FULFILLED', (state, action) =>
state state
.withMutations(s => { .setIn(["stats", "status"], "success")
let b = action.payload.body; .setIn(["stats", "data"], action.payload.body)
s = s.setIn(["cashflow", "data"], b.data); .deleteIn(["stats", "error"])
s = s.setIn(["cashflow", "labels"], b.labels);
s = s.setIn(["cashflow", "params"], b.params);
s = s.setIn(["cashflow", "currency"], b.currency);
s = s.setIn(["cashflow", "currencySymbol"], b.currencySymbol);
s = s.deleteIn(["cashflow", "error"]);
return s;
})
.setIn(["cashflow", "status"], "success")
); );
reducer.handleAction('home/FETCH_CASHFLOW_REJECTED', (state, action) => reducer.handleAction('home/FETCH_STATS_REJECTED', (state, action) =>
state.setIn(["cashflow", "status"], "failed").setIn(["cashflow", "error"], true) state.setIn(["stats", "status"], "failed").deleteIn(["stats", "data"]).setIn(["stats", "error"], true)
); );
export function fetchStatuses(unitId, from, to, dateType) {
return {
type: 'home/FETCH_STATUSES',
payload: backendRequest('/api/' + unitId + '/home/statuses', 'GET', { params: { from, to, dateType } })
};
};
reducer.handleAction('home/FETCH_STATUSES_PENDING', (state, action) =>
state.setIn(["statuses", "status"], "pending")
);
reducer.handleAction('home/FETCH_STATUSES_FULFILLED', (state, action) =>
state
.withMutations(s => {
s = s.set("statuses", fromJS(action.payload.body));
s = s.setIn(["statuses", "status"], "success");
s = s.deleteIn(["statuses", "error"]);
return s;
})
);
reducer.handleAction('home/FETCH_STATUSES_REJECTED', (state, action) =>
state.setIn(["statuses", "status"], "failed").setIn(["statuses", "error"], true)
);
//export reducer //export reducer
export default reducer.export(); export default reducer.export();
//TODO transform summary response
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment