diff --git a/client/coral-admin/src/containers/Dashboard/ActivityWidget.js b/client/coral-admin/src/containers/Dashboard/ActivityWidget.js
new file mode 100644
index 000000000..a6e293b82
--- /dev/null
+++ b/client/coral-admin/src/containers/Dashboard/ActivityWidget.js
@@ -0,0 +1,49 @@
+import React, {PropTypes} from 'react';
+import {Link} from 'react-router';
+import styles from './Widget.css';
+import I18n from 'coral-framework/modules/i18n/i18n';
+import translations from 'coral-admin/src/translations';
+
+const lang = new I18n(translations);
+
+const ActivityWidget = ({assets}) => {
+ return (
+
@@ -39,4 +38,14 @@ const FlagWidget = (props) => {
);
};
+FlagWidget.propTypes = {
+ assets: PropTypes.arrayOf(PropTypes.shape({
+ id: PropTypes.string,
+ url: PropTypes.string,
+ action_summaries: PropTypes.array,
+ author: PropTypes.string,
+ created_at: PropTypes.string
+ })).isRequired
+};
+
export default FlagWidget;
diff --git a/client/coral-admin/src/containers/Dashboard/LikeWidget.js b/client/coral-admin/src/containers/Dashboard/LikeWidget.js
index 436fbcaf3..b878c281b 100644
--- a/client/coral-admin/src/containers/Dashboard/LikeWidget.js
+++ b/client/coral-admin/src/containers/Dashboard/LikeWidget.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {PropTypes} from 'react';
import {Link} from 'react-router';
import styles from './Widget.css';
import I18n from 'coral-framework/modules/i18n/i18n';
@@ -6,9 +6,7 @@ import translations from 'coral-admin/src/translations';
const lang = new I18n(translations);
-const LikeWidget = (props) => {
-
- const {assets} = props;
+const LikeWidget = ({assets}) => {
return (
@@ -40,4 +38,14 @@ const LikeWidget = (props) => {
);
};
+LikeWidget.propTypes = {
+ assets: PropTypes.arrayOf(PropTypes.shape({
+ id: PropTypes.string,
+ url: PropTypes.string,
+ action_summaries: PropTypes.array,
+ author: PropTypes.string,
+ created_at: PropTypes.string
+ })).isRequired
+};
+
export default LikeWidget;
diff --git a/client/coral-admin/src/containers/Dashboard/Widget.css b/client/coral-admin/src/containers/Dashboard/Widget.css
index 9912ac4e4..b4515786f 100644
--- a/client/coral-admin/src/containers/Dashboard/Widget.css
+++ b/client/coral-admin/src/containers/Dashboard/Widget.css
@@ -23,7 +23,6 @@
padding-left: 10px;
font-size: 1.5rem;
font-weight: bold;
- background-color: #e0e0e0;
}
.widgetTable {
diff --git a/client/coral-admin/src/graphql/fragments/assetMetricsView.graphql b/client/coral-admin/src/graphql/fragments/assetMetricsView.graphql
index 37335aeaa..c77fbc32b 100644
--- a/client/coral-admin/src/graphql/fragments/assetMetricsView.graphql
+++ b/client/coral-admin/src/graphql/fragments/assetMetricsView.graphql
@@ -4,6 +4,7 @@ fragment metrics on Asset {
url
author
created_at
+ commentCount
action_summaries {
type: __typename
actionCount
diff --git a/client/coral-admin/src/graphql/queries/metricsQuery.graphql b/client/coral-admin/src/graphql/queries/metricsQuery.graphql
index 42a9fb70e..f0dff8965 100644
--- a/client/coral-admin/src/graphql/queries/metricsQuery.graphql
+++ b/client/coral-admin/src/graphql/queries/metricsQuery.graphql
@@ -7,4 +7,7 @@ query Metrics ($from: Date!, $to: Date!) {
assetsByLike: assetMetrics(from: $from, to: $to, sort: LIKE) {
...metrics
}
+ assetsByActivity: assetMetrics(from: $from, to: $to, sort: ACTIVITY) {
+ ...metrics
+ }
}
diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json
index 7e0f13989..25c7900bc 100644
--- a/client/coral-admin/src/translations.json
+++ b/client/coral-admin/src/translations.json
@@ -136,6 +136,7 @@
"no_flags": "There have been no flags in the last 5 minutes! Hooray!",
"no_likes": "There have been no likes in the last 5 minutes. All quiet.",
"flags": "Flags",
+ "no_activity": "There haven't been any comments anywhere in the last five minutes.",
"comment_count": "comments"
},
"streams": {
@@ -283,6 +284,7 @@
"no_flags": "¡Nadie ha marcado nada en los últimos 5 minutos! ¡Bravo!",
"no_likes": "A nadie le ha gustado algún comentario en los últimos 5 minutos. Todo tranquilo.",
"flags": "Marcados",
+ "no_activity": "No hubo comentarios en los ultimos 5 minutos",
"comment_count": "comentarios"
},
"streams": {
diff --git a/graph/loaders/metrics.js b/graph/loaders/metrics.js
index a6fe5afe4..a0408ed4d 100644
--- a/graph/loaders/metrics.js
+++ b/graph/loaders/metrics.js
@@ -3,6 +3,53 @@ const DataLoader = require('dataloader');
const {objectCacheKeyFn} = require('./util');
const ActionModel = require('../../models/action');
+const CommentModel = require('../../models/comment');
+
+/**
+ * Returns the assets which have had comments made within the last time period.
+ */
+const getAssetActivityMetrics = ({loaders: {Assets}}, {from, to, limit}) => {
+ let assetMetrics = [];
+
+ return CommentModel.aggregate([
+ {$match: {
+ parent_id: null,
+ created_at: {
+ $gt: from,
+ $lt: to
+ }
+ }},
+ {$group: {
+ _id: '$asset_id',
+ commentCount: {
+ $sum: 1
+ }
+ }},
+ {$project: {
+ _id: false,
+ asset_id: '$_id',
+ commentCount: '$commentCount'
+ }},
+ {$sort: {
+ commentCount: -1
+ }},
+ {$limit: limit}
+ ])
+ .then((results) => {
+ assetMetrics = results;
+
+ return Assets.getByID.loadMany(results.map((result) => result.asset_id));
+ })
+ .then((assets) => assets.map((asset, i) => {
+
+ // We're leveraging the fact that the comments returned by the aggregation
+ // query are in the request order that we just made, it's what the
+ // Assets.getByID loader does.
+ asset.commentCount = assetMetrics[i].commentCount;
+
+ return asset;
+ }));
+};
/**
* Returns a list of assets with action metadata included on the models.
@@ -208,10 +255,11 @@ module.exports = (context) => ({
cacheKeyFn: objectCacheKeyFn('from', 'to')
}),
Assets: {
- get: ({from, to, sort, limit}) => getAssetMetrics(context, {from, to, sort, limit})
+ get: ({from, to, sort, limit}) => getAssetMetrics(context, {from, to, sort, limit}),
+ getActivity: ({from, to, limit}) => getAssetActivityMetrics(context, {from, to, limit}),
},
Comments: {
- get: ({from, to, sort, limit}) => getCommentMetrics(context, {from, to, sort, limit})
+ get: ({from, to, sort, limit}) => getCommentMetrics(context, {from, to, sort, limit}),
}
}
});
diff --git a/graph/resolvers/root_query.js b/graph/resolvers/root_query.js
index d577ddddb..ce11fcae9 100644
--- a/graph/resolvers/root_query.js
+++ b/graph/resolvers/root_query.js
@@ -63,6 +63,10 @@ const RootQuery = {
return null;
}
+ if (sort === 'ACTIVITY') {
+ return Assets.getActivity({from, to, limit});
+ }
+
return Assets.get({from, to, sort, limit});
},
diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql
index 9655354fb..306a6f22a 100644
--- a/graph/typeDefs.graphql
+++ b/graph/typeDefs.graphql
@@ -496,6 +496,22 @@ enum USER_STATUS {
APPROVED
}
+# Metrics for the assets.
+enum ASSET_METRICS_SORT {
+
+ # Represents a LikeAction.
+ LIKE
+
+ # Represents a FlagAction.
+ FLAG
+
+ # Represents a don't agree action.
+ DONTAGREE
+
+ # Represents activity.
+ ACTIVITY
+}
+
type RootQuery {
# Site wide settings and defaults.
@@ -526,7 +542,7 @@ type RootQuery {
# Asset metrics related to user actions are saturated into the assets
# returned.
- assetMetrics(from: Date!, to: Date!, sort: ACTION_TYPE!, limit: Int = 10): [Asset!]
+ assetMetrics(from: Date!, to: Date!, sort: ASSET_METRICS_SORT!, limit: Int = 10): [Asset!]
# Comment metrics related to user actions are saturated into the comments
# returned.