Initial import
This commit is contained in:
commit
54fa30701e
278 changed files with 37868 additions and 0 deletions
630
lib/models/segments.js
Normal file
630
lib/models/segments.js
Normal file
|
@ -0,0 +1,630 @@
|
|||
'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) => {
|
||||
connection.release();
|
||||
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) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
let count = rows && rows[0] && rows[0].count || 0;
|
||||
return callback(null, count);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue