find matching real estates for new search request and notify

This commit is contained in:
Bilal Catic
2019-10-07 20:49:54 +02:00
parent 502c7c3e35
commit 615aed65b6
7 changed files with 154 additions and 26 deletions

View File

@@ -25,6 +25,8 @@ const AWS_EMAIL_CONFIG = {
const MAX_REAL_ESTATES_IN_EMAIL =
parseInt(process.env.MAX_REAL_ESTATES_IN_EMAIL) || 10;
const MAX_REAL_ESTATES_IN_FIRST_EMAIL =
parseInt(process.env.MAX_REAL_ESTATES_IN_FIRST_EMAIL) || 5;
module.exports = {
APP_PORT,
@@ -33,5 +35,6 @@ module.exports = {
CRAWLER_INTERVAL,
STOP_CRAWLER,
AWS_EMAIL_CONFIG,
MAX_REAL_ESTATES_IN_EMAIL
MAX_REAL_ESTATES_IN_EMAIL,
MAX_REAL_ESTATES_IN_FIRST_EMAIL
};

View File

@@ -1,5 +1,7 @@
"use strict";
const db = require("../../models/index");
const sequelize = require("sequelize");
const Op = sequelize.Op;
const bulkUpsertRealEstates = async realEstateData => {
try {
@@ -26,10 +28,12 @@ const bulkUpsertRealEstates = async realEstateData => {
"updatedAt",
"renewedDate"
];
const order = [["updatedAt", "desc"]];
return await db.RealEstate.bulkCreate(realEstateData, {
updateOnDuplicate: fieldsToUpdateIfDuplicate,
returning: true
returning: true,
order
});
} catch (e) {
console.log("Error bulk upserting realEstates : ", e);
@@ -40,7 +44,68 @@ const getRealEstateById = async id => {
return db.RealEstate.findByPk(id);
};
const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => {
const {
priceMin,
priceMax,
sizeMin,
sizeMax,
adType,
realEstateType,
areaToSearch
} = searchRequest;
const longitudeColumn = sequelize.col("locationLong");
const latitudeColumn = sequelize.col("locationLat");
const pointGeometry = sequelize.fn(
"ST_Point",
longitudeColumn,
latitudeColumn
);
const pointWithSRID = sequelize.fn("ST_SetSRID", pointGeometry, 4326);
const areaToSearchAsGeometry = sequelize.fn(
"ST_GeomFromGeoJSON",
JSON.stringify(areaToSearch)
);
const areaToSearchWithSRID = sequelize.fn(
"ST_SetSRID",
areaToSearchAsGeometry,
4326
);
const contains = sequelize.fn(
"ST_Contains",
areaToSearchWithSRID,
pointWithSRID
);
const geoSearchQueryPart = sequelize.where(contains, true);
const query = {
adType,
realEstateType,
price: {
[Op.lte]: priceMax,
[Op.gte]: priceMin
},
area: {
[Op.lte]: sizeMax,
[Op.gte]: sizeMin
},
[Op.and]: geoSearchQueryPart
};
const order = [["updatedAt", "desc"]];
return await db.RealEstate.findAll({
where: query,
limit: maxResults,
order
});
};
module.exports = {
bulkUpsertRealEstates,
getRealEstateById
getRealEstateById,
findRealEstatesForSearchRequest
};

View File

@@ -6,11 +6,14 @@ const findRealEstatesForSearchRequest = async searchRequestId => {
searchRequestId
};
const include = [{ model: db.RealEstate, as: "realEstates" }];
const realEstatesModel = { model: db.RealEstate, as: "realEstates" };
const order = [[realEstatesModel, "updatedAt", "desc"]];
const include = [realEstatesModel];
const matches = await db.SearchRequestMatch.findAll({
where: query,
include
include,
order
});
const matchingRealEstates = [];

View File

@@ -10,6 +10,16 @@ const generateEmailFooter = searchRequestId => {
<strong>Vaš,<br/>Javimi tim</strong>`;
};
const generateRealEstateLinks = realEstates => {
let realEstateLinks = "";
for (const realEstate of realEstates) {
const { id: realEstateId, title } = realEstate;
realEstateLinks += `<li><a href="${APP_URL}/redirect/${realEstateId}">${title}</a></li>`;
}
return realEstateLinks;
};
const generateNotificationEmail = (realEstates, searchRequestId) => {
const truncateList = realEstates.length > MAX_REAL_ESTATES_IN_EMAIL;
const realEstatesToShow = truncateList
@@ -17,16 +27,8 @@ const generateNotificationEmail = (realEstates, searchRequestId) => {
: realEstates;
const allRealEstatesLink = `${APP_URL}/nekretnine/${searchRequestId}`;
let realEstateLinks = "";
for (const realEstate of realEstatesToShow) {
const { id: realEstateId, title } = realEstate;
realEstateLinks += `<li><a href="${APP_URL}/redirect/${realEstateId}">${title}</a></li><br />`;
}
const realEstateLinks = generateRealEstateLinks(realEstatesToShow);
const moreRealEstates = `<div>Kompletan spisak nekretnina možete pegledati na <a href="${allRealEstatesLink}">listi nekretnina</a><div>`;
const emailFooter = generateEmailFooter(searchRequestId);
return `<h3>Zdravo</h3>
@@ -40,7 +42,7 @@ const generateNotificationEmail = (realEstates, searchRequestId) => {
${emailFooter}`;
};
const generateNewSearchRequestEmail = searchRequest => {
const generateNewSearchRequestEmail = (searchRequest, matchingRealEstates) => {
const realEstateType = AD_CATEGORY[searchRequest.realEstateType];
const {
id,
@@ -52,8 +54,15 @@ const generateNewSearchRequestEmail = searchRequest => {
priceMax
} = searchRequest;
const realEstateLinks = generateRealEstateLinks(matchingRealEstates);
const instantRealEstatesText = `<br/>
<div>
U međuvremenu pogledajte neke od nedavno objavljenih nekretnina koje odgovaraju Vašim uslovima pretrage :<br/>
${realEstateLinks}
</div>`;
const gardenSize = realEstateType.hasGardenSize
? `<div><strong>Kvadratura okućnice: Od ${gardenSizeMin} do ${gardenSizeMax} m2 </strong></div>`
? `<div><strong>Kvadratura okućnice: Od ${gardenSizeMin} do ${gardenSizeMax} m2</strong></div>`
: ``;
const emailFooter = generateEmailFooter(id);
@@ -68,6 +77,7 @@ const generateNewSearchRequestEmail = searchRequest => {
${gardenSize}
<div><strong>Cijena:</strong> ${priceMin} do ${priceMax} KM</div>
</div>
${matchingRealEstates.length > 0 ? instantRealEstatesText : ""}
<br/>
${emailFooter}`;
};

View File

@@ -1,5 +1,8 @@
"use strict";
const { matchRealEstates } = require("../services/searchMatchService");
const {
matchRealEstates,
matchSearchRequest
} = require("../services/searchMatchService");
const {
generateNotificationEmail,
generateNewSearchRequestEmail
@@ -8,6 +11,24 @@ const { sendEmail } = require("../services/emailService");
const notifyForNewRealEstates = async newRealEstates => {
const matches = await matchRealEstates(newRealEstates);
await notifyMatches(matches);
};
const notifyForNewSearchRequest = async searchRequest => {
const matches = await matchSearchRequest(searchRequest);
const searchRequestId = searchRequest.id;
const matchingRealEstates = matches[searchRequestId].realEstates;
const emailContent = generateNewSearchRequestEmail(
searchRequest,
matchingRealEstates
);
const { email } = searchRequest;
await sendEmail(email, "Market Alert", emailContent);
};
const notifyMatches = async matches => {
const searchRequestsToNotify = Object.keys(matches);
const asyncSendEmailActions = [];
@@ -27,12 +48,6 @@ const notifyForNewRealEstates = async newRealEstates => {
await Promise.all(asyncSendEmailActions);
};
const notifyForNewSearchRequest = async searchRequest => {
const emailContent = generateNewSearchRequestEmail(searchRequest);
const { email } = searchRequest;
await sendEmail(email, "Market Alert", emailContent);
};
module.exports = {
notifyForNewRealEstates,
notifyForNewSearchRequest

View File

@@ -3,7 +3,9 @@
const {
findSearchRequestsForRealEstate
} = require("../helpers/db/searchRequest");
const { findRealEstatesForSearchRequest } = require("../helpers/db/realEstate");
const { addMatches } = require("../helpers/db/searchRequestMatch");
const { MAX_REAL_ESTATES_IN_FIRST_EMAIL } = require("../config/appConfig");
const matchRealEstates = async realEstates => {
if (Array.isArray(realEstates)) {
@@ -40,6 +42,35 @@ const matchRealEstates = async realEstates => {
}
};
module.exports = {
matchRealEstates
const matchSearchRequest = async searchRequest => {
const { id: searchRequestId } = searchRequest;
const realEstates = await findRealEstatesForSearchRequest(
searchRequest,
MAX_REAL_ESTATES_IN_FIRST_EMAIL
);
const matches = {
[searchRequestId]: {
searchRequest,
realEstates: []
}
};
const matchingRecords = [];
for (const realEstate of realEstates) {
matches[searchRequestId].realEstates.push(realEstate);
matchingRecords.push({
searchRequestId,
realEstateId: realEstate.id,
notified: false
});
}
await addMatches(matchingRecords);
return matches;
};
module.exports = {
matchRealEstates,
matchSearchRequest
};

View File

@@ -8,7 +8,8 @@ SEQUELIZE_LOGGING=0- no sequelize logging, 1- log to the console
PORT=Port for the app, defaults to 5000
APP_BASE_URL=base url for the app
MAX_REAL_ESTATES_IN_EMAIL=Number of real estates that will be shown in URL, others will be truncated and URL with full list will be shwon
MAX_REAL_ESTATES_IN_EMAIL=Max number of real estates that will be shown in email, others will be truncated and URL with full list will be shwon
MAX_REAL_ESTATES_IN_FIRST_EMAIL=Max number of real estates that will be shown in first (welcome) email
AWS_KEY_ID=(your-key-here)
AWS_SECRET_ACCESS_KEY=(your-key-here)