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 ) => {
2016-04-05 12:29:42 +00:00
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 ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ;