diff --git a/client/coral-admin/src/components/FlagWidget.js b/client/coral-admin/src/components/FlagWidget.js
index be167de8a..324b76c10 100644
--- a/client/coral-admin/src/components/FlagWidget.js
+++ b/client/coral-admin/src/components/FlagWidget.js
@@ -1,7 +1,6 @@
import React, {PropTypes} from 'react';
-import styles from './FlagWidget.css';
-const FlagWidget = props => {
+const FlagWidget = () => {
return (
diff --git a/graph/loaders/index.js b/graph/loaders/index.js
index 536e40fa9..5b1894b65 100644
--- a/graph/loaders/index.js
+++ b/graph/loaders/index.js
@@ -3,6 +3,7 @@ const _ = require('lodash');
const Actions = require('./actions');
const Assets = require('./assets');
const Comments = require('./comments');
+const Metrics = require('./metrics');
const Settings = require('./settings');
const Users = require('./users');
@@ -18,6 +19,7 @@ module.exports = (context) => {
Actions,
Assets,
Comments,
+ Metrics,
Settings,
Users
].map((loaders) => {
diff --git a/graph/loaders/metrics.js b/graph/loaders/metrics.js
new file mode 100644
index 000000000..a827ddeea
--- /dev/null
+++ b/graph/loaders/metrics.js
@@ -0,0 +1,137 @@
+const _ = require('lodash');
+const CommentModel = require('../../models/comment');
+const AssetModel = require('../../models/asset');
+const ActionModel = require('../../models/action');
+
+const getMetrics = (context, {from, to}) => {
+
+ let commentMetrics = {};
+ let assetMetrics = [];
+
+ return ActionModel.aggregate([
+
+ // Find all actions that were created in the time range.
+ {$match: {
+ action_type: 'FLAG',
+ item_type: 'COMMENTS',
+ created_at: {
+ $gt: from,
+ $lt: to
+ }
+ }},
+
+ // Count all those items.
+ {$group: {
+ _id: '$item_id',
+ count: {
+ $sum: 1
+ }
+ }},
+
+ // Project the count to a better field.
+ {$project: {
+ item_id: '$_id',
+ count: '$count'
+ }}
+ ]).then((actionSummaries) => {
+
+ // Collect all the action summaries into a dictionary.
+ actionSummaries.forEach((actionSummary) => {
+ commentMetrics[actionSummary.item_id] = actionSummary.count;
+ });
+
+ // Collect just the comment id's.
+ let commentIDs = actionSummaries.map((as) => as.item_id);
+
+ // Find those comments.
+ return CommentModel.aggregate([
+
+ // Get only those comments.
+ {$match: {
+ id: {
+ $in: commentIDs
+ }
+ }},
+
+ // Group by their asset id and push in the comment id.
+ {$group: {
+ _id: {
+ asset_id: '$asset_id'
+ },
+ ids: {
+ $addToSet: '$id'
+ }
+ }},
+
+ // Project that data only as better fields.
+ {$project: {
+ asset_id: '$_id.asset_id',
+ ids: '$ids'
+ }}
+ ]);
+ })
+ .then((commentResults) => {
+
+ // Compute all the action summaries for the assets based on the time slice
+ // that you requested.
+ commentResults.forEach((result) => {
+ let actionCount = 0;
+
+ result.ids.forEach((id) => {
+ actionCount += commentMetrics[id];
+ });
+
+ assetMetrics.push({
+ id: result.asset_id,
+ actionCount,
+ actionableItemCount: result.ids.length
+ });
+ });
+
+ // Sort the assets by flag count.
+ assetMetrics.sort((a, b) => {
+ return b.flags - a.flags;
+ });
+
+ // Only keep the top 10.
+ assetMetrics = assetMetrics.slice(0, 10);
+
+ // Determine the assets that we need to return.
+ return AssetModel.find({
+ id: {
+ $in: assetMetrics.map((asset) => asset.id)
+ }
+ });
+ })
+ .then((assets) => {
+
+ // Join up the assets that are returned by their id.
+ let groupedAssets = _.groupBy(assets, 'id');
+
+ // Return from the sorted asset metrics and return their assetes.
+ return assetMetrics.map(({id, actionCount, actionableItemCount}) => {
+ if (id in groupedAssets) {
+ let asset = groupedAssets[id][0];
+
+ let flagAssetActionSummary = {
+ action_type: 'FLAG',
+ actionCount,
+ actionableItemCount
+ };
+
+ // Add the action summaries to the asset.
+ asset.action_summaries = [flagAssetActionSummary];
+
+ return asset;
+ }
+
+ return null;
+ }).filter((asset) => asset != null);
+ });
+};
+
+module.exports = (context) => ({
+ Metrics: {
+ get: ({from, to}) => getMetrics(context, {from, to})
+ }
+});
diff --git a/graph/resolvers/asset_action_summary.js b/graph/resolvers/asset_action_summary.js
new file mode 100644
index 000000000..99483f9bb
--- /dev/null
+++ b/graph/resolvers/asset_action_summary.js
@@ -0,0 +1,10 @@
+const AssetActionSummary = {
+ __resolveType({action_type}) {
+ switch (action_type) {
+ case 'FLAG':
+ return 'FlagAssetActionSummary';
+ }
+ }
+};
+
+module.exports = AssetActionSummary;
diff --git a/graph/resolvers/index.js b/graph/resolvers/index.js
index 84dd10fdc..65461dc76 100644
--- a/graph/resolvers/index.js
+++ b/graph/resolvers/index.js
@@ -1,5 +1,6 @@
const ActionSummary = require('./action_summary');
const Action = require('./action');
+const AssetActionSummary = require('./asset_action_summary');
const Asset = require('./asset');
const Comment = require('./comment');
const Date = require('./date');
@@ -17,6 +18,7 @@ const ValidationUserError = require('./validation_user_error');
module.exports = {
ActionSummary,
Action,
+ AssetActionSummary,
Asset,
Comment,
Date,
diff --git a/graph/resolvers/root_query.js b/graph/resolvers/root_query.js
index ff81e4c0f..432ad126f 100644
--- a/graph/resolvers/root_query.js
+++ b/graph/resolvers/root_query.js
@@ -39,12 +39,12 @@ const RootQuery = {
return Comments.getByQuery(query);
},
- metric(_, args, {user, loaders: {Assets}}) {
+ metrics(_, {from, to}, {user, loaders: {Metrics}}) {
if (user == null || !user.hasRoles('ADMIN')) {
return null;
}
- return Assets.getForMetrics();
+ return Metrics.get({from, to});
},
// This returns the current user, ensure that if we aren't logged in, we
diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql
index 0ba307046..5d9262612 100644
--- a/graph/typeDefs.graphql
+++ b/graph/typeDefs.graphql
@@ -198,16 +198,6 @@ interface AssetActionSummary {
actionableItemCount: Int
}
-# A summary of counts related to all the Likes on an Asset.
-type LikeAssetActionSummary implements AssetActionSummary {
-
- # Number of actions associated with actionable types on this this Asset.
- actionCount: Int
-
- # Number of unique actionable types that are referenced by the actions.
- actionableItemCount: Int
-}
-
# A summary of counts related to all the Flags on an Asset.
type FlagAssetActionSummary implements AssetActionSummary {
@@ -343,15 +333,12 @@ type Asset {
# The date that the asset was closed at.
closedAt: Date
- # The date that the asset was created.
- created_at: Date
-
# Summary of all Actions against all entities associated with the Asset.
# (likes, flags, etc.)
action_summaries: [AssetActionSummary]
- # Unique users that have commented on this Asset.
- authorCount: Int
+ # The date that the asset was created.
+ created_at: Date
}
################################################################################
@@ -386,7 +373,7 @@ type ValidationUserError implements UserError {
}
################################################################################
-## Queries
+## Queries;
################################################################################
# Establishes the ordering of the content by their created_at time stamp.
@@ -424,8 +411,8 @@ type RootQuery {
# The currently logged in user based on the request.
me: User
- # metrics
- metric: [Asset]
+ # Metrics related to user actions are saturated into the assets returned.
+ metrics(from: Date!, to: Date!): [Asset]
}
################################################################################