Skip to content
This repository was archived by the owner on Sep 4, 2020. It is now read-only.

Commit 5d34e1b

Browse files
feat(#3): push notifications if potential candidates are outside reach
1 parent ffb503d commit 5d34e1b

File tree

11 files changed

+264
-119
lines changed

11 files changed

+264
-119
lines changed

config/locales/de.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"ACTIVATED": "Genial! FLUSTER PLUS steht nun zu deiner Verfügung, viel Spass!"
2020
},
2121
"ITEMS": {
22-
"NEW_ITEMS": "Hoi {{who}}, einige neue Inserate passen zu deinem Profil 🔍👍😃"
22+
"NEW_ITEMS": "Hey {{who}}, einige neue Inserate passen zu deinem Profil 🔍👍😃",
23+
"NEW_CANDIDATES": "Hey {{who}}, erweitert die Sichtbarkeit deiner Anzeige, um mehr Kandidaten zu erreichen"
2324
}
2425
}

config/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"ACTIVATED": "Awesome! You upgrade to FLUSTER PLUS is done, enjoy!"
2020
},
2121
"ITEMS": {
22-
"NEW_ITEMS": "Hey {{who}}, some new posts are matching your profile 🔍👍😃"
22+
"NEW_ITEMS": "Hey {{who}}, some new posts are matching your profile 🔍👍😃",
23+
"NEW_CANDIDATES": "Hey {{who}}, extend the visibility of your ad to reach more candidates"
2324
}
2425
}

config/locales/fr.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"ACTIVATED": "Fantastique! Tu disposes dès à présent de FLUSTER PLUS!"
2020
},
2121
"ITEMS": {
22-
"NEW_ITEMS": "Hey {{who}}, des nouvelles annonces correspondent à ton profil 🔍👍😃"
22+
"NEW_ITEMS": "Hey {{who}}, des nouvelles annonces correspondent à ton profil 🔍👍😃",
23+
"NEW_CANDIDATES": "Hey {{who}}, étends la visibilité de ton annonce pour atteindre plus de candidats"
2324
}
2425
}

config/locales/it.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"ACTIVATED": "Eccezionale! FLUSTER PLUS è ora disponibile a tua disposizione"
2020
},
2121
"ITEMS": {
22-
"NEW_ITEMS": "Ciao {{who}}, nuove camere e appartamenti corrispondoni alla tuo profilo 🔍👍😃"
22+
"NEW_ITEMS": "Ciao {{who}}, nuove camere e appartamenti corrispondoni alla tuo profilo 🔍👍😃",
23+
"NEW_CANDIDATES": "Hey {{who}}, espandi la visibilità del tuo annuncio per raggiungere più candidati"
2324
}
2425
}

cronjobs/cronjobs.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const cron = require('cron');
33
const pushNotifications = require('./push-notifications');
44
const pushChatMessages = require('./push-chat-messages');
55
const pushNewItems = require('./push-new-items');
6+
const pushNewCandidates = require('./push-new-candidates');
67

78
const emailAdminNewItems = require('./email-admin-new-items');
89

@@ -24,7 +25,7 @@ const pushChatMessagesJob = new cron.CronJob('0 */5 7-23 * * *', function() {
2425
'Europe/Paris'
2526
);
2627

27-
const pushNewItemsJob = new cron.CronJob('00 26 7-23 * * *', function() {
28+
const pushNewItemsJob = new cron.CronJob('0 26 7-23 * * *', function() {
2829
pushNewItems.pushNewItems();
2930
}, function () {
3031
/* This function is executed when the job stops */
@@ -40,4 +41,13 @@ const emailAdminNewItemsJob = new cron.CronJob('0 */2 7-23 * * *', function() {
4041
},
4142
true, /* Start the job right now */
4243
'Europe/Paris'
43-
);
44+
);
45+
46+
const pushNewCandidatesJob = new cron.CronJob('0 0 19 0-6/2 * *', function() {
47+
pushNewCandidates.pushNewCandidates();
48+
}, function () {
49+
/* This function is executed when the job stops */
50+
},
51+
true, /* Start the job right now */
52+
'Europe/Paris'
53+
);
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
const Device = require('../model/device');
2+
3+
const logger = require('log4js').getLogger('peterparker');
4+
5+
const utils = require('../../controllers/utils/utils');
6+
7+
const constants = require('../../config/constants');
8+
9+
const CandidatesHelper = require('../../controllers/candidates/candidatesHelper');
10+
11+
const pushSender = require('./push-sender');
12+
13+
const i18n = require("i18n");
14+
15+
const Q = require('q');
16+
17+
module.exports = {
18+
findCandidates: findCandidates,
19+
sendPushNotification: sendPushNotification
20+
};
21+
22+
function findCandidates(item, ageMin, ageMax) {
23+
const deferred = Q.defer();
24+
25+
const candidatesHelper = new CandidatesHelper();
26+
27+
let query = {
28+
longitude: item.address.location.coordinates[0],
29+
latitude: item.address.location.coordinates[1],
30+
type: item.attributes.type,
31+
furnished: item.attributes.furnished,
32+
ageMin: ageMin,
33+
ageMax: ageMax,
34+
gender: item.userLimitations.gender
35+
};
36+
37+
if (utils.isNotNull(item.attributes.rooms) && item.attributes.rooms !== 0) {
38+
query["rooms"] = '' + item.attributes.rooms;
39+
}
40+
41+
if (utils.isNotNull(item.attributes.price.gross) && item.attributes.price.gross !== 0) {
42+
query["price"] = '' + item.attributes.price.gross;
43+
}
44+
45+
if (utils.isNotNull(item.attributes.disabledFriendly)) {
46+
query["disabledFriendly"] = '' + item.attributes.disabledFriendly;
47+
}
48+
49+
if (utils.isNotNull(item.attributes.petsAllowed)) {
50+
query["petsAllowed"] = '' + item.attributes.petsAllowed;
51+
}
52+
53+
if (utils.isNotNull(item.attributes.availability.begin)) {
54+
query["availablebegin"] = '' + item.attributes.availability.begin;
55+
}
56+
57+
if (utils.isNotNull(item.attributes.availability.end)) {
58+
query["availableend"] = '' + item.attributes.availability.end;
59+
}
60+
61+
let likes = _.map(item.likes, function (doc) {
62+
return doc.user;
63+
});
64+
65+
const dislikes = _.map(item.dislikes, function (doc) {
66+
return doc.user;
67+
});
68+
69+
let userIds = new Array();
70+
userIds.push(item.user);
71+
72+
if (utils.isNotEmpty(likes)) {
73+
userIds = userIds.concat(likes);
74+
}
75+
76+
if (utils.isNotEmpty(dislikes)) {
77+
userIds = userIds.concat(dislikes);
78+
}
79+
80+
candidatesHelper.findCandidates(userIds, 0, constants.MAX_ITEM_USERS, query).then(function (users) {
81+
deferred.resolve(users);
82+
}, function (err) {
83+
deferred.reject(new Error(err));
84+
});
85+
86+
return deferred.promise;
87+
}
88+
89+
function sendPushNotification(user, labelKey) {
90+
Device.findDevice(user._id).then(function (device) {
91+
if (utils.isNotNull(device) && !utils.isStringEmpty(device.tokenId)) {
92+
processNofication(user, device, labelKey);
93+
}
94+
}, function (error) {
95+
logger.info('error', 'Error while looking for device informations.');
96+
});
97+
}
98+
99+
function processNofication(user, device, labelKey) {
100+
101+
if (utils.isNotNull(user.userParams) && utils.isNotNull(user.userParams.appSettings) && user.userParams.appSettings.pushNotifications) {
102+
103+
const msgText = getPushNotificationText(user, device, labelKey);
104+
105+
pushSender.pushNotification(msgText, device).then(function (data) {
106+
// Coolio all right here
107+
}, function (error) {
108+
logger.info('error', 'Error while pushing the notification to the client. ' + JSON.stringify(error));
109+
});
110+
} else {
111+
// Means user don't want to receive push notifications
112+
}
113+
}
114+
115+
function getPushNotificationText(user, device, labelKey) {
116+
117+
const language = !utils.isStringEmpty(device.language) ? device.language : 'en';
118+
119+
return i18n.__({phrase: labelKey, locale: language}, {who: user.facebook.firstName});
120+
}

cronjobs/push-sender.js renamed to cronjobs/helpers/push-sender.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
var utils = require('../controllers/utils/utils');
2-
var constants = require('../config/constants');
1+
var utils = require('../../controllers/utils/utils');
2+
var constants = require('../../config/constants');
33

44
var Q = require('q');
55

cronjobs/push-chat-messages.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ var logger = require('log4js').getLogger('peterparker');
88

99
var utils = require('../controllers/utils/utils');
1010

11-
var pushSender = require('./push-sender');
11+
var pushSender = require('./helpers/push-sender');
1212

1313
var i18n = require("i18n");
1414

cronjobs/push-new-candidates.js

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
const mongoose = require('mongoose');
2+
3+
const Item = mongoose.model('Item');
4+
5+
const logger = require('log4js').getLogger('peterparker');
6+
7+
const utils = require('../controllers/utils/utils');
8+
9+
const constants = require('../config/constants');
10+
11+
const pushCandidatesHelper = require('./helpers/push-candidates-helper');
12+
13+
const moment = require('moment');
14+
const Q = require('q');
15+
16+
module.exports = {
17+
pushNewCandidates: pushNewCandidates
18+
};
19+
20+
function pushNewCandidates() {
21+
22+
if (!constants.ARN_IOS_PROD) {
23+
return;
24+
}
25+
26+
const now = new Date();
27+
28+
let sinceWhen = moment(now).add(-3, 'w').toDate();
29+
30+
const query = {
31+
status: 'published',
32+
createdAt: {$gt: sinceWhen, $lte: now}
33+
};
34+
35+
const populateFields = [
36+
{path: "user", select: "_id userParams facebook.firstName", options: {lean: true}}
37+
];
38+
39+
Item.find(query).lean().populate(populateFields).exec((err, items) => {
40+
if (err) {
41+
logger.info('error', 'Error while looking for notifications.');
42+
} else {
43+
if (utils.isNotEmpty(items)) {
44+
const promises = new Array();
45+
46+
for (let i = 0, len = items.length; i < len; i++) {
47+
if (hasItemNoLimitations(items[i])) {
48+
promises.push(hasItemCandidates(items[i]));
49+
}
50+
}
51+
52+
if (utils.isNotEmpty(promises)) {
53+
Promise.all(promises).then((values) => {
54+
if (utils.isNotEmpty(values)) {
55+
for (let i = 0, len = values.length; i < len; i++) {
56+
if (values[i].hasCandidates) {
57+
pushCandidatesHelper.sendPushNotification(values[i], 'ITEMS.NEW_CANDIDATES');
58+
}
59+
}
60+
}
61+
})
62+
.catch((err) => {
63+
logger.info('error', 'Error while querying for notifications.');
64+
});
65+
}
66+
}
67+
}
68+
});
69+
}
70+
71+
function hasItemCandidates(item) {
72+
const deferred = Q.defer();
73+
74+
const ageMin = item.userLimitations.age.min === 18 ? 18 : getFilterAgeMin(item.userLimitations.age.min);
75+
const ageMax = item.userLimitations.age.max === 99 ? 99 : getFilterAgeMax(item.userLimitations.age.max);
76+
77+
pushCandidatesHelper.findCandidates(item, ageMin, ageMax).then((users) => {
78+
deferred.resolve({
79+
item: item,
80+
hasCandidates: utils.isNotEmpty(users)
81+
});
82+
}, function (err) {
83+
deferred.reject(new Error(err));
84+
});
85+
86+
return deferred.promise;
87+
}
88+
89+
function getFilterAgeMin(age) {
90+
const minAge = age - Math.round(age * 0.1);
91+
return minAge > 18 ? minAge : 18;
92+
}
93+
94+
function getFilterAgeMax(age) {
95+
const maxAge = age + Math.round(age * 0.1);
96+
return maxAge < 99 ? maxAge : 99;
97+
}
98+
99+
function hasItemNoLimitations(item) {
100+
if (!item || !item.userLimitations) {
101+
return false;
102+
}
103+
104+
const genderIrrelevant = item.userLimitations.gender === 'irrelevant';
105+
const ageIrrelevant = item.userLimitations.age && item.userLimitations.age.min === 18 && item.userLimitations.age.min === 99;
106+
107+
return genderIrrelevant && ageIrrelevant;
108+
}

0 commit comments

Comments
 (0)