mailtrain/lib/models/segments.js

631 lines
20 KiB
JavaScript
Raw Normal View History

2016-04-04 12:36:30 +00:00
'use strict';
let tools = require('../tools');
let db = require('../db');
let fields = require('./fields');
module.exports.defaultColumns = [{
column: 'email',
name: 'Email address',
type: 'string'
}, {
column: 'opt_in_country',
name: 'Signup country',
type: 'string'
}, {
column: 'created',
name: 'Sign up date',
type: 'date'
}, {
column: 'latest_open',
name: 'Latest open',
type: 'date'
}, {
column: 'latest_click',
name: 'Latest click',
type: 'date'
}, {
column: 'first_name',
name: 'First name',
type: 'string'
}, {
column: 'last_name',
name: 'Last name',
type: 'string'
}];
module.exports.list = (listId, callback) => {
listId = Number(listId) || 0;
if (listId < 1) {
return callback(new Error('Missing List ID'));
}
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
let query = 'SELECT * FROM segments WHERE list=? ORDER BY name';
connection.query(query, [listId], (err, rows) => {
connection.release();
if (err) {
return callback(err);
}
let segments = (rows || []).map(tools.convertKeys);
return callback(null, segments);
});
});
};
module.exports.get = (id, callback) => {
id = Number(id) || 0;
if (id < 1) {
return callback(new Error('Missing Segment ID'));
}
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
let query = 'SELECT * FROM segments WHERE id=? LIMIT 1';
connection.query(query, [id], (err, rows) => {
if (err) {
connection.release();
return callback(err);
}
if (!rows || !rows.length) {
connection.release();
return callback(new Error('Segment not found'));
}
let segment = tools.convertKeys(rows[0]);
let query = 'SELECT * FROM segment_rules WHERE segment=? ORDER BY id ASC';
connection.query(query, [id], (err, rows) => {
connection.release();
if (err) {
return callback(err);
}
fields.list(segment.list, (err, fieldList) => {
if (err || !fieldList) {
fieldList = [];
}
segment.columns = [].concat(module.exports.defaultColumns);
fieldList.forEach(field => {
if (field.column) {
segment.columns.push({
column: field.column,
name: field.name,
type: fields.genericTypes[field.type] || 'string'
});
}
if (field.options) {
field.options.forEach(subField => {
if (subField.column) {
segment.columns.push({
column: subField.column,
name: field.name + ': ' + subField.name,
type: fields.genericTypes[subField.type] || 'string'
});
}
});
}
});
segment.rules = (rows || []).map(rule => {
rule = tools.convertKeys(rule);
if (rule.value) {
try {
rule.value = JSON.parse(rule.value);
} catch (E) {
// ignore
}
}
if (!rule.value) {
rule.value = {};
}
rule.columnType = segment.columns.filter(column => rule.column === column.column).pop() || {};
rule.name = rule.columnType.name || '';
switch (rule.columnType.type) {
case 'number':
case 'date':
case 'birthday':
if (rule.value.range) {
rule.formatted = (rule.value.start || '') + ' … ' + (rule.value.end || '');
} else {
rule.formatted = rule.value.value || '';
}
break;
case 'boolean':
rule.formatted = rule.value.value ? 'Selected' : 'Not selected';
break;
default:
rule.formatted = rule.value.value || '';
}
return rule;
});
return callback(null, segment);
});
});
});
});
};
module.exports.create = (listId, segment, callback) => {
listId = Number(listId) || 0;
if (listId < 1) {
return callback(new Error('Missing List ID'));
}
segment = tools.convertKeys(segment);
segment.name = (segment.name || '').toString().trim();
segment.type = Number(segment.type) || 0;
if (!segment.name) {
return callback(new Error('Field Name must be set'));
}
if (segment.type <= 0) {
return callback(new Error('Invalid segment rule type'));
}
let keys = ['list', 'name', 'type'];
let values = [listId, segment.name, segment.type];
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
let query = 'INSERT INTO segments (`' + keys.join('`, `') + '`) VALUES (' + values.map(() => '?').join(',') + ')';
connection.query(query, values, (err, result) => {
connection.release();
if (err) {
return callback(err);
}
return callback(null, result && result.insertId || false);
});
});
};
module.exports.update = (id, updates, callback) => {
updates = updates || {};
id = Number(id) || 0;
if (id < 1) {
return callback(new Error('Missing Segment ID'));
}
let segment = tools.convertKeys(updates);
segment.name = (segment.name || '').toString().trim();
segment.type = Number(segment.type) || 0;
if (!segment.name) {
return callback(new Error('Field Name must be set'));
}
if (segment.type <= 0) {
return callback(new Error('Invalid segment rule type'));
}
let keys = ['name', 'type'];
let values = [segment.name, segment.type];
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
values.push(id);
connection.query('UPDATE segments SET ' + keys.map(key => '`' + key + '`=?').join(', ') + ' WHERE id=? LIMIT 1', values, (err, result) => {
connection.release();
if (err) {
return callback(err);
}
return callback(null, result && result.affectedRows || false);
});
});
};
module.exports.delete = (id, callback) => {
id = Number(id) || 0;
if (id < 1) {
return callback(new Error('Missing Segment ID'));
}
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
connection.query('DELETE FROM segments WHERE id=? LIMIT 1', [id], err => {
connection.release();
if (err) {
return callback(err);
}
return callback(null, true);
});
});
};
module.exports.createRule = (segmentId, rule, callback) => {
segmentId = Number(segmentId) || 0;
if (segmentId < 1) {
return callback(new Error('Missing Segment ID'));
}
rule = tools.convertKeys(rule);
module.exports.get(segmentId, (err, segment) => {
if (err) {
return callback(err);
}
if (!segment) {
return callback(new Error('Selected segment not found'));
}
let column = segment.columns.filter(column => column.column === rule.column).pop();
if (!column) {
return callback(new Error('Invalid rule type'));
}
let value;
switch (column.type) {
case 'date':
case 'birthday':
case 'number':
if (rule.range) {
value = {
range: true,
start: rule.start,
end: rule.end
};
} else {
value = {
value: rule.value
};
}
break;
case 'boolean':
value = {
value: rule.value ? 1 : 0
};
break;
default:
value = {
value: rule.value
};
}
let keys = ['segment', 'column', 'value'];
let values = [segment.id, rule.column, JSON.stringify(value)];
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
let query = 'INSERT INTO segment_rules (`' + keys.join('`, `') + '`) VALUES (' + values.map(() => '?').join(',') + ')';
connection.query(query, values, (err, result) => {
connection.release();
if (err) {
return callback(err);
}
return callback(null, result && result.insertId || false);
});
});
});
};
module.exports.getRule = (id, callback) => {
id = Number(id) || 0;
if (id < 1) {
return callback(new Error('Missing Rule ID'));
}
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
let query = 'SELECT * FROM segment_rules WHERE id=? LIMIT 1';
connection.query(query, [id], (err, rows) => {
connection.release();
if (err) {
return callback(err);
}
if (!rows || !rows.length) {
return callback(new Error('Specified rule not found'));
}
let rule = tools.convertKeys(rows[0]);
module.exports.get(rule.segment, (err, segment) => {
if (err) {
return callback(err);
}
if (!segment) {
return callback(new Error('Specified segment not found'));
}
if (rule.value) {
try {
rule.value = JSON.parse(rule.value);
} catch (E) {
// ignore
}
}
if (!rule.value) {
rule.value = {};
}
rule.columnType = segment.columns.filter(column => rule.column === column.column).pop() || {};
rule.name = rule.columnType.name || '';
switch (rule.columnType.type) {
case 'number':
case 'date':
case 'birthday':
if (rule.value.range) {
rule.formatted = (rule.value.start || '') + ' … ' + (rule.value.end || '');
} else {
rule.formatted = rule.value.value || '';
}
break;
case 'boolean':
rule.formatted = rule.value.value ? 'Selected' : 'Not selected';
break;
default:
rule.formatted = rule.value.value || '';
}
return callback(null, rule);
});
});
});
};
module.exports.updateRule = (id, rule, callback) => {
id = Number(id) || 0;
if (id < 1) {
return callback(new Error('Missing Rule ID'));
}
rule = tools.convertKeys(rule);
module.exports.getRule(id, (err, existingRule) => {
if (err) {
return callback(err);
}
if (!existingRule) {
return callback(new Error('Selected rule not found'));
}
module.exports.get(existingRule.segment, (err, segment) => {
if (err) {
return callback(err);
}
if (!segment) {
return callback(new Error('Selected segment not found'));
}
let column = segment.columns.filter(column => column.column === existingRule.column).pop();
if (!column) {
return callback(new Error('Invalid rule type'));
}
let value;
switch (column.type) {
case 'date':
case 'birthday':
case 'number':
if (rule.range) {
value = {
range: true,
start: rule.start,
end: rule.end
};
} else {
value = {
value: rule.value
};
}
break;
case 'boolean':
value = {
value: rule.value ? 1 : 0
};
break;
default:
value = {
value: rule.value
};
}
let keys = ['value'];
let values = [JSON.stringify(value)];
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
values.push(id);
connection.query('UPDATE segment_rules SET ' + keys.map(key => '`' + key + '`=?').join(', ') + ' WHERE id=? LIMIT 1', values, (err, result) => {
connection.release();
if (err) {
return callback(err);
}
return callback(null, result && result.affectedRows || false);
});
});
});
});
};
module.exports.deleteRule = (id, callback) => {
id = Number(id) || 0;
if (id < 1) {
return callback(new Error('Missing Rule ID'));
}
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
connection.query('DELETE FROM segment_rules WHERE id=? LIMIT 1', [id], err => {
connection.release();
if (err) {
return callback(err);
}
return callback(null, true);
});
});
};
module.exports.getQuery = (id, callback) => {
module.exports.get(id, (err, segment) => {
if (err) {
return callback(err);
}
if (!segment) {
return callback(new Error('Segment not found'));
}
let query = [];
let values = [];
let getDate = (value, nextDay) => {
let parts = value.trim().split(/\D/);
let year = Number(parts.shift()) || 0;
let month = Number(parts.shift()) || 0;
let day = Number(parts.shift()) || 0;
if (!year || !month || !day) {
return false;
}
return new Date(Date.UTC(year, month - 1, day + (nextDay ? 1 : 0)));
};
segment.rules.forEach(rule => {
switch (rule.columnType.type) {
case 'string':
query.push('`' + rule.columnType.column + '` LIKE ?');
values.push(rule.value.value);
break;
case 'boolean':
query.push('`' + rule.columnType.column + '` = ?');
values.push(rule.value.value);
break;
case 'number':
if (rule.value.range) {
query.push('`' + rule.columnType.column + '` >= ?');
query.push('`' + rule.columnType.column + '` < ?');
values.push(rule.value.start);
values.push(rule.value.end);
} else {
query.push('`' + rule.columnType.column + '` = ?');
values.push(rule.value.value);
}
break;
case 'birthday':
if (rule.value.range) {
query.push('`' + rule.columnType.column + '` >= ?');
query.push('`' + rule.columnType.column + '` < ?');
values.push(getDate('2000-' + rule.value.start));
values.push(getDate('2000-' + rule.value.end, true));
} else {
query.push('`' + rule.columnType.column + '` >= ?');
query.push('`' + rule.columnType.column + '` < ?');
values.push(getDate('2000-' + rule.value.value));
values.push(getDate('2000-' + rule.value.value, true));
}
break;
case 'date':
if (rule.value.range) {
query.push('`' + rule.columnType.column + '` >= ?');
query.push('`' + rule.columnType.column + '` < ?');
values.push(getDate(rule.value.start));
values.push(getDate(rule.value.end, true));
} else {
query.push('`' + rule.columnType.column + '` >= ?');
query.push('`' + rule.columnType.column + '` < ?');
values.push(getDate(rule.value.value));
values.push(getDate(rule.value.value, true));
}
break;
}
});
return callback(null, {
where: query.join(segment.type === 1 ? ' AND ' : ' OR ') || '1',
values
});
});
};
module.exports.subscribers = (id, onlySubscribed, callback) => {
module.exports.get(id, (err, segment) => {
if (err) {
return callback(err);
}
if (!segment) {
return callback(new Error('Segment not found'));
}
module.exports.getQuery(id, (err, queryData) => {
if (err) {
return callback(err);
}
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
let query;
if (!onlySubscribed) {
query = 'SELECT COUNT(id) AS `count` FROM `subscription__' + segment.list + '` WHERE ' + queryData.where + ' LIMIT 1';
} else {
query = 'SELECT COUNT(id) AS `count` FROM `subscription__' + segment.list + '` WHERE `status`=1 ' + (queryData.where ? ' AND (' + queryData.where + ')' : '') + ' LIMIT 1';
}
connection.query(query, queryData.values, (err, rows) => {
connection.release();
2016-04-04 12:36:30 +00:00
if (err) {
return callback(err);
}
let count = rows && rows[0] && rows[0].count || 0;
return callback(null, count);
});
});
});
});
};