diff --git a/client/src/Home.js b/client/src/Home.js
index 71b03e2e..75e6e511 100644
--- a/client/src/Home.js
+++ b/client/src/Home.js
@@ -20,7 +20,7 @@ export default class List extends Component {
return (
{t('Mailtrain 2 beta')}
-
{t('Build') + ' 2019-08-31-1200'}
+
{t('Build') + ' 2019-09-05-1547'}
);
}
diff --git a/server/models/campaigns.js b/server/models/campaigns.js
index 2607d42b..594a31bb 100644
--- a/server/models/campaigns.js
+++ b/server/models/campaigns.js
@@ -769,11 +769,9 @@ async function changeStatusByCampaignCidAndSubscriptionIdTx(tx, context, campaig
])
.first();
- if (!message) {
- throw new Error('Invalid campaign.');
+ if (message) { // If a test is send before the campaign is sent, the corresponding entry does not exists in campaign_messages. We ignore such situations as the subscriber gets unsubscribed anyway. We just don't account it to the campaign.
+ await _changeStatusByMessageTx(tx, context, message, campaignMessageStatus);
}
-
- await _changeStatusByMessageTx(tx, context, message, campaignMessageStatus);
}
diff --git a/server/models/reports.js b/server/models/reports.js
index 0f2bde6c..0f720b53 100644
--- a/server/models/reports.js
+++ b/server/models/reports.js
@@ -236,6 +236,8 @@ async function _getCampaignStatistics(campaign, select, joins, unionQryFn, listQ
commonFieldsMapping[`${prefix}:link`] = alias + '.link';
commonFieldsMapping[`${prefix}:country`] = alias + '.country';
commonFieldsMapping[`${prefix}:deviceType`] = alias + '.device_type';
+ commonFieldsMapping[`${prefix}:ip`] = alias + '.ip';
+ commonFieldsMapping[`${prefix}:created`] = alias + '.created';
knexJoinFns.push((qry, cpgListId) => qry.leftJoin('campaign_links AS ' + alias, getConds(alias, cpgListId)));
diff --git a/server/routes/quick-reports.js b/server/routes/quick-reports.js
index d816697d..b49975f7 100644
--- a/server/routes/quick-reports.js
+++ b/server/routes/quick-reports.js
@@ -10,6 +10,7 @@ const {castToInteger} = require('../lib/helpers');
const {SubscriptionStatus} = require('../../shared/lists');
const knex = require('../lib/knex');
const {LinkId} = require('../models/links');
+const moment = require('moment');
const router = require('../lib/router-async').create();
@@ -23,7 +24,7 @@ router.getAsync('/open-and-click-counts/:campaignId', passport.loggedIn, async (
const results = await reports.getCampaignStatisticsStream(
campaign,
- ['subscription:email', 'open_tracker:count', 'click_tracker:count', 'open_tracker:country', ...Object.keys(listFields)],
+ ['subscription:email', 'open_tracker:count', 'click_tracker:count', 'open_tracker:country', 'open_tracker:created', 'open_tracker:deviceType', ...Object.keys(listFields)],
[
{type: 'links', prefix: 'open_tracker', onConditions: {link: knex.raw('?', [LinkId.OPEN])} },
{type: 'links', prefix: 'click_tracker', onConditions: {link: knex.raw('?', [LinkId.GENERAL_CLICK])} }
@@ -48,11 +49,16 @@ router.getAsync('/open-and-click-counts/:campaignId', passport.loggedIn, async (
{ key: 'open_tracker:count', header: 'Open count' },
{ key: 'click_tracker:count', header: 'Click count' },
{ key: 'open_tracker:country', header: 'Country (first open)' },
+ { key: 'open_tracker:created', header: 'Date/time (first open)' },
+ { key: 'open_tracker:deviceType', header: 'Device type (first open)' },
...Object.keys(listFields).map(key => ({key, header: listFields[key].key}))
],
delimiter: ','
},
- async (row, encoding) => row
+ async (row, encoding) => ({
+ ...row,
+ 'open_tracker:created': moment(row['open_tracker:created']).toISOString()
+ })
);
});