Compare commits

...

14 Commits

Author SHA1 Message Date
Naida Vatric
fa46f75dd3 Changed default to switched on. 2020-01-28 22:28:52 +01:00
Naida Vatric
98263364c7 Added option to include-exclude ads without price 2020-01-23 11:13:53 +01:00
Naida Vatric
5b3491fdba Added migration and model change for searchReq table. 2020-01-23 10:12:56 +01:00
Naida Vatric
d117383802 Tested both ways for realestate and search req filters. 2020-01-17 22:58:22 +01:00
Naida Vatric
870b71a3c7 WIP Changed all logic for searchRequest. 2020-01-17 01:54:06 +01:00
Naida Vatric
4fd4018bf6 Merge branch 'sliders-formating' of gitlab.com:saburly/marketalarm/web into add-even-more-filters 2020-01-14 23:19:51 +01:00
Naida Vatric
259799144e Merge branch 'rental-crawler-fix' into 'master'
Rental crawler fix

See merge request saburly/marketalarm/web!80
2020-01-06 23:12:52 +00:00
Naida Vatric
bc73d4159d Merge branch 'master' into 'rental-crawler-fix'
# Conflicts:
#   .gitignore
2020-01-06 23:12:40 +00:00
Naida Vatric
37ad32fe76 Merge branch 'edit-location-start' into 'master'
Edit location start

See merge request saburly/marketalarm/web!79
2020-01-06 23:10:16 +00:00
Naida Vatric
94875a0fa3 Merge branch 'add-currency-to-price-filters' into 'master'
Add currency to price filters

See merge request saburly/marketalarm/web!78
2020-01-06 23:09:40 +00:00
Naida Vatric
0c2d218d29 Changed floor numbers and basement-attic tag. 2020-01-02 00:10:31 +01:00
Naida Vatric
fed2dc00dc Changed number of rooms. 2019-12-29 23:42:39 +01:00
Naida Vatric
d5d3a1f306 Changed accesRoadType logic 2019-12-26 23:30:05 +01:00
Naida Vatric
42ff1f762f Changed to avoid falsy values and not defined realestate parametrs. 2019-12-21 02:20:26 +01:00
10 changed files with 506 additions and 131 deletions

2
.gitignore vendored
View File

@@ -2,4 +2,4 @@ node_modules/
.env .env
.idea/ .idea/
.eslintrc .eslintrc
.vscode/ .vscode/

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
*.ejs

View File

@@ -35,7 +35,8 @@ const getFilters = async (req, res) => {
balcony, balcony,
elevator, elevator,
newBuilding, newBuilding,
accessRoadType accessRoadType,
includeWithoutPrice
} = searchRequest; } = searchRequest;
const category = AD_CATEGORY[realEstateType] || AD_CATEGORY.FLAT; const category = AD_CATEGORY[realEstateType] || AD_CATEGORY.FLAT;
@@ -115,7 +116,8 @@ const getFilters = async (req, res) => {
advancedSegmentSelectFilterValues, advancedSegmentSelectFilterValues,
advancedRangeFilterObjects, advancedRangeFilterObjects,
advancedRangeFilterValues, advancedRangeFilterValues,
includeIncompleteAds includeIncompleteAds,
includeWithoutPrice
}); });
}; };
@@ -191,6 +193,7 @@ const postFilters = async (req, res) => {
}); });
const includeIncompleteAds = req.body.includeIncompleteAds === "on"; const includeIncompleteAds = req.body.includeIncompleteAds === "on";
const includeWithoutPrice = req.body.includeWithoutPrice === "on";
const balcony = req.body.balcony === "on"; const balcony = req.body.balcony === "on";
const elevator = req.body.elevator === "on"; const elevator = req.body.elevator === "on";
@@ -217,6 +220,7 @@ const postFilters = async (req, res) => {
searchRequest.newBuilding = newBuilding; searchRequest.newBuilding = newBuilding;
searchRequest.includeIncompleteAds = includeIncompleteAds; searchRequest.includeIncompleteAds = includeIncompleteAds;
searchRequest.includeWithoutPrice = includeWithoutPrice;
searchRequest.accessRoadType = accessRoadType; searchRequest.accessRoadType = accessRoadType;

View File

@@ -312,7 +312,7 @@ class RentalCrawler {
let numberOfRooms = let numberOfRooms =
parseInt(extractedData["re_realEstates_roomsNO"]) + parseInt(extractedData["re_realEstates_roomsNO"]) +
parseInt(extractedData["re_realEstates_bedroomNO"]) || null, parseInt(extractedData["re_realEstates_bedNO"]) || null,
numberOfFloors = numberOfFloors =
parseInt(extractedData["re_realEstates_floorsNO"]) || parseInt(extractedData["re_realEstates_floorsNO"]) ||
this.getNumberOfFloorsFromFloorId(extractedData["re_floorNO_id"]), this.getNumberOfFloorsFromFloorId(extractedData["re_floorNO_id"]),
@@ -352,7 +352,9 @@ class RentalCrawler {
realEstatePropertiesFromInfrastructure.phoneConnection, realEstatePropertiesFromInfrastructure.phoneConnection,
cableTV = realEstatePropertiesFromInfrastructure.cableTV, cableTV = realEstatePropertiesFromInfrastructure.cableTV,
internet = realEstatePropertiesFromInfrastructure.internet, internet = realEstatePropertiesFromInfrastructure.internet,
basementAttic = realEstatePropertiesFromSpaces.basementAttic, basementAttic =
realEstatePropertiesFromSpaces.basementAttic ||
this.checkBasemAtticFromFloors(extractedData["re_floorNO_id"]),
storeRoom = realEstatePropertiesFromSpaces.storeRoom, storeRoom = realEstatePropertiesFromSpaces.storeRoom,
videoSurveillance = videoSurveillance =
realEstatePropertiesFromDescriptions.videoSurveillance || realEstatePropertiesFromDescriptions.videoSurveillance ||
@@ -397,9 +399,7 @@ class RentalCrawler {
); );
if (!publishedDateMoment.isValid()) { if (!publishedDateMoment.isValid()) {
throw { throw {
message: `Invalid published date : ${ message: `Invalid published date : ${extractedData["re_realEstates_inserted"]}`
extractedData["re_realEstates_inserted"]
}`
}; };
} }
@@ -410,9 +410,7 @@ class RentalCrawler {
); );
if (!renewedDateMoment.isValid()) { if (!renewedDateMoment.isValid()) {
throw { throw {
message: `Invalid renewed date : ${ message: `Invalid renewed date : ${extractedData["re_realEstates_edited"]}`
extractedData["re_realEstates_edited"]
}`
}; };
} }
@@ -782,8 +780,42 @@ class RentalCrawler {
if (floorIds.length === 0) { if (floorIds.length === 0) {
return null; return null;
} }
let noOfFloors = floorIds.length;
// Floors of 'suteren', 'podrum', 'tavan' and 'potkrovlje' are not counted
floorIds.forEach(id => {
if (
parseInt(id) === 1 ||
parseInt(id) === 2 ||
parseInt(id) === 12 ||
parseInt(id) === 14
) {
noOfFloors--;
}
});
return noOfFloors;
}
return floorIds.length; checkBasemAtticFromFloors(floorsIdText) {
// floorIdText can be array of numbers, separated by comma or number
const floorIds = floorsIdText.split(",");
let check = false;
if (floorIds.length === 0) {
check = false;
}
//If floors 'suteren', 'podrum', 'tavan' and 'potkrovlje' exists then tag for basement-attic is true
floorIds.forEach(id => {
if (
parseInt(id) === 1 ||
parseInt(id) === 2 ||
parseInt(id) === 12 ||
parseInt(id) === 14
) {
check = true;
}
});
return check;
} }
async sleep(ms) { async sleep(ms) {

View File

@@ -2,6 +2,8 @@
const db = require("../../models/index"); const db = require("../../models/index");
const sequelize = require("sequelize"); const sequelize = require("sequelize");
const Op = sequelize.Op; const Op = sequelize.Op;
const { AD_CATEGORY } = require("../../common/enums");
const bulkUpsertRealEstates = async realEstateData => { const bulkUpsertRealEstates = async realEstateData => {
try { try {
const fieldsToUpdateIfDuplicate = [ const fieldsToUpdateIfDuplicate = [
@@ -96,12 +98,16 @@ const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => {
floorMin, floorMin,
floorMax, floorMax,
includeIncompleteAds, includeIncompleteAds,
includeWithoutPrice,
balcony, balcony,
elevator, elevator,
newBuilding, newBuilding,
accessRoadType accessRoadType
} = searchRequest; } = searchRequest;
//Needed for defining which attribute should exist or not
const realEstateTypeObject = AD_CATEGORY[realEstateType];
const longitudeColumn = sequelize.col("locationLong"); const longitudeColumn = sequelize.col("locationLong");
const latitudeColumn = sequelize.col("locationLat"); const latitudeColumn = sequelize.col("locationLat");
@@ -134,15 +140,6 @@ const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => {
const query = { const query = {
adType, adType,
realEstateType, realEstateType,
price: {
[Op.or]: {
[Op.and]: {
[Op.lte]: priceMax,
[Op.gte]: priceMin
},
[Op.is]: null
}
},
area: { area: {
[Op.lte]: sizeMax, [Op.lte]: sizeMax,
[Op.gte]: sizeMin [Op.gte]: sizeMin
@@ -154,15 +151,6 @@ const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => {
const queryIncludeIncomplete = { const queryIncludeIncomplete = {
adType, adType,
realEstateType, realEstateType,
price: {
[Op.or]: {
[Op.and]: {
[Op.lte]: priceMax,
[Op.gte]: priceMin
},
[Op.is]: null
}
},
area: { area: {
[Op.or]: { [Op.or]: {
[Op.and]: { [Op.and]: {
@@ -175,8 +163,49 @@ const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => {
[Op.and]: geoSearchQueryPart [Op.and]: geoSearchQueryPart
}; };
//Every other attribute is checked separately and included in query only if it is defined //Is user unchecked includeWithoutPrice FALSE then it shouldn't return null values of price
if (gardenSizeMax && gardenSizeMin) { //If not then null values are accepted (this is DEFAULT)
//includeIncpompleteAds does not have effect on price query
if (includeWithoutPrice) {
query.price = {
[Op.or]: {
[Op.and]: {
[Op.lte]: priceMax,
[Op.gte]: priceMin
},
[Op.is]: null
}
};
queryIncludeIncomplete.price = {
[Op.or]: {
[Op.and]: {
[Op.lte]: priceMax,
[Op.gte]: priceMin
},
[Op.is]: null
}
};
} else {
query.price = {
[Op.and]: {
[Op.lte]: priceMax,
[Op.gte]: priceMin
}
};
queryIncludeIncomplete.price = {
[Op.and]: {
[Op.lte]: priceMax,
[Op.gte]: priceMin
}
};
}
//Every other attribute is checked separately and included in query only if it is defined for real estate type
if (
realEstateTypeObject.hasGardenSize &&
gardenSizeMax != null &&
gardenSizeMin != null
) {
query.gardenSize = { query.gardenSize = {
[Op.lte]: gardenSizeMax, [Op.lte]: gardenSizeMax,
[Op.gte]: gardenSizeMin [Op.gte]: gardenSizeMin
@@ -192,7 +221,11 @@ const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => {
}; };
} }
if (numberOfRoomsMin && numberOfRoomsMax) { if (
realEstateTypeObject.hasNumberOfRoom &&
numberOfRoomsMin != null &&
numberOfRoomsMax != null
) {
query.numberOfRooms = { query.numberOfRooms = {
[Op.lte]: numberOfRoomsMax, [Op.lte]: numberOfRoomsMax,
[Op.gte]: numberOfRoomsMin [Op.gte]: numberOfRoomsMin
@@ -208,7 +241,11 @@ const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => {
}; };
} }
if (numberOfFloorsMin && numberOfFloorsMax) { if (
realEstateTypeObject.hasNumberOfFloors &&
numberOfFloorsMin != null &&
numberOfFloorsMax != null
) {
query.numberOfFloors = { query.numberOfFloors = {
[Op.lte]: numberOfFloorsMax, [Op.lte]: numberOfFloorsMax,
[Op.gte]: numberOfFloorsMin [Op.gte]: numberOfFloorsMin
@@ -224,7 +261,11 @@ const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => {
}; };
} }
if (floorMin && floorMax) { if (
realEstateTypeObject.hasFloorProp &&
floorMin != null &&
floorMax != null
) {
query.floor = { query.floor = {
[Op.lte]: floorMax, [Op.lte]: floorMax,
[Op.gte]: floorMin [Op.gte]: floorMin
@@ -239,8 +280,10 @@ const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => {
} }
}; };
} }
//Logic for balcony, newBuilding and elevator from users side
if (balcony) { //If true is checked, then I want characteristic to be true but,
//if it is not checked, then I dont care - it can be null or false or true
if (realEstateTypeObject.hasBalconyProp && balcony === true) {
query.balcony = { query.balcony = {
[Op.eq]: balcony [Op.eq]: balcony
}; };
@@ -252,7 +295,7 @@ const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => {
}; };
} }
if (newBuilding) { if (realEstateTypeObject.hasNewBuildingProp && newBuilding === true) {
query.newBuilding = { query.newBuilding = {
[Op.eq]: newBuilding [Op.eq]: newBuilding
}; };
@@ -264,7 +307,7 @@ const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => {
}; };
} }
if (elevator) { if (realEstateTypeObject.hasElevatorProp && elevator === true) {
query.elevator = { query.elevator = {
[Op.eq]: elevator [Op.eq]: elevator
}; };
@@ -275,7 +318,8 @@ const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => {
} }
}; };
} }
//If user wants 'ANY' road type acces then it is not included in query -
//returns every road type and null values
if (accessRoadType !== "ANY") { if (accessRoadType !== "ANY") {
query.accessRoadType = { query.accessRoadType = {
[Op.eq]: accessRoadType [Op.eq]: accessRoadType

View File

@@ -49,128 +49,390 @@ const findSearchRequestsForRealEstate = async realEstate => {
const geoSearchQueryPart = sequelize.where(contains, true); const geoSearchQueryPart = sequelize.where(contains, true);
//General query contains only attributes that are defined for every RealEstate - not null
const query = {
adType,
realEstateType,
subscribed: true,
[Op.and]: geoSearchQueryPart
};
//Needed for defining which attribute should exist or not //Needed for defining which attribute should exist or not
const realEstateTypeObject = AD_CATEGORY[realEstateType]; const realEstateTypeObject = AD_CATEGORY[realEstateType];
//Needed to decide on including incomplete RealEstates data
// ?? Needed to decide on including incomplete RealEstates data
let checkForIncompleteWanted = false; let checkForIncompleteWanted = false;
//Attributes are checked separately and included in query only if defined //Attributes are checked separately to make different query parts
//Price and area should be defined for every property
if (price) { //If real estate price is number then it searches for req that have priceMin and priceMax
query.priceMin = { //If real estate price is null it searches for req that accept ads without price
[Op.lte]: price //User always defines price and area (sliders) - not null in search req
let priceQuery = {};
if (price != null) {
priceQuery = {
[Op.and]: [
{
priceMin: {
[Op.lte]: price
}
},
{
priceMax: {
[Op.gte]: price
}
}
]
}; };
query.priceMax = { } else {
[Op.gte]: price priceQuery = {
includeWithoutPrice: {
[Op.eq]: true
}
}; };
} }
if (area) { let areaQuery = {};
query.sizeMin = { if (area != null) {
[Op.lte]: area areaQuery = {
}; [Op.and]: [
query.sizeMax = { {
[Op.gte]: area sizeMin: {
[Op.lte]: area
}
},
{
sizeMax: {
[Op.gte]: area
}
}
]
}; };
} else { } else {
checkForIncompleteWanted = true; checkForIncompleteWanted = true;
} }
//Other attributes can be defined or not depending on RealEstate type //Other attributes can be defined or not depending on RealEstate type
if (gardenSize) { //we check what to include in query based on real estate type object
query.gardenSizeMin = { let gardenSizeQuery = {};
[Op.lte]: gardenSize if (realEstateTypeObject.hasGardenSize) {
}; if (gardenSize != null) {
query.gardenSizeMax = { gardenSizeQuery = {
[Op.gte]: gardenSize [Op.and]: [
}; {
} else if (realEstateTypeObject.hasGardenSize) { gardenSizeMin: {
checkForIncompleteWanted = true; [Op.lte]: gardenSize
}
},
{
gardenSizeMax: {
[Op.gte]: gardenSize
}
}
]
};
} else {
checkForIncompleteWanted = true;
}
} }
if (numberOfRooms) { let numberOfRoomsQuery = {};
query.numberOfRoomsMin = { if (realEstateTypeObject.hasNumberOfRoom) {
[Op.lte]: numberOfRooms if (numberOfRooms != null) {
}; //If real estate has defined number of rooms ex. 3 it returns req
query.numberOfRoomsMax = { // that accepts 3 rooms or ones that don't have defined number - null
[Op.gte]: numberOfRooms //Ex. they didnt choose advanced filters at all
}; numberOfRoomsQuery = {
} else if (realEstateTypeObject.hasNumberOfRoom) { [Op.and]: [
checkForIncompleteWanted = true; {
numberOfRoomsMin: {
[Op.or]: {
[Op.lte]: numberOfRooms,
[Op.is]: null
}
}
},
{
numberOfRoomsMax: {
[Op.or]: {
[Op.gte]: numberOfRooms,
[Op.is]: null
}
}
}
]
};
} else {
// If real estate dont have defined number of rooms ex. null
//It returns requests that didn't choose number of rooms - also null
//Or ones that picked some values but also picked to includeIncomplete ads
numberOfRoomsQuery = {
[Op.or]: [
{
[Op.and]: [
{
numberOfRoomsMin: {
[Op.is]: null
}
},
{
numberOfRoomsMax: {
[Op.is]: null
}
}
]
},
{
includeIncompleteAds: {
[Op.eq]: true
}
}
]
};
}
}
//Same logic for number of Floors and floors
let numberOfFloorsQuery = {};
if (realEstateTypeObject.hasNumberOfFloors) {
if (numberOfFloors != null) {
numberOfFloorsQuery = {
[Op.and]: [
{
numberOfFloorsMin: {
[Op.or]: {
[Op.lte]: numberOfFloors,
[Op.is]: null
}
}
},
{
numberOfFloorsMax: {
[Op.or]: {
[Op.gte]: numberOfFloors,
[Op.is]: null
}
}
}
]
};
} else {
numberOfFloorsQuery = {
[Op.or]: [
{
[Op.and]: [
{
numberOfFloorsMin: {
[Op.is]: null
}
},
{
numberOfFloorsMax: {
[Op.is]: null
}
}
]
},
{
includeIncompleteAds: {
[Op.eq]: true
}
}
]
};
}
}
let floorQuery = {};
if (realEstateTypeObject.hasFloorProp) {
if (floor != null) {
floorQuery = {
[Op.and]: [
{
floorMin: {
[Op.or]: {
[Op.lte]: floor,
[Op.is]: null
}
}
},
{
floorMax: {
[Op.or]: {
[Op.gte]: floor,
[Op.is]: null
}
}
}
]
};
} else {
floorQuery = {
[Op.or]: [
{
[Op.and]: [
{
floorMin: {
[Op.is]: null
}
},
{
floorMax: {
[Op.is]: null
}
}
]
},
{
includeIncompleteAds: {
[Op.eq]: true
}
}
]
};
}
} }
if (numberOfFloors) { //Logic for balcony, newBuilding and elevator
query.numberOfFloorsMin = { //If user dont check checkbox for ex. elevator it does not mean he only wants no elevator
[Op.lte]: numberOfFloors //If real estate characteristic =true find all req, one that wants charachertistic or dont care - dont need query
}; //If real estate characteristic = false, find all req exept for ones that wants characteristic to be true
query.numberOfFloorsMax = { //If real estate characteristic = null, dont know if true or false, find req that dont care or want char and want incomplete ads
[Op.gte]: numberOfFloors let balconyQuery = {};
}; if (realEstateTypeObject.hasBalconyProp && balcony !== true) {
} else if (realEstateTypeObject.hasNumberOfFloors) { if (balcony === false) {
checkForIncompleteWanted = true; balconyQuery = {
balcony: {
[Op.ne]: true
}
};
} else if (balcony === null) {
balconyQuery = {
[Op.or]: [
{
balcony: {
[Op.ne]: true
}
},
{
[Op.and]: [
{
balcony: {
[Op.eq]: true
}
},
{
includeIncompleteAds: {
[Op.eq]: true
}
}
]
}
]
};
}
} }
let newBuildingQuery = {};
if (floor) { if (realEstateTypeObject.hasNewBuildingProp && newBuilding !== true) {
query.floorMin = { if (newBuilding === false) {
[Op.lte]: floor newBuildingQuery = {
}; newBuilding: {
query.floorMax = { [Op.ne]: true
[Op.gte]: floor }
}; };
} else if (realEstateTypeObject.hasFloorProp) { } else if (newBuilding === null) {
checkForIncompleteWanted = true; newBuildingQuery = {
[Op.or]: [
{
newBuilding: {
[Op.ne]: true
}
},
{
[Op.and]: [
{
newBuilding: {
[Op.eq]: true
}
},
{
includeIncompleteAds: {
[Op.eq]: true
}
}
]
}
]
};
}
} }
let elevatorQuery = {};
if (realEstateTypeObject.hasElevatorProp && elevator !== true) {
if (elevator === false) {
elevatorQuery = {
elevator: {
[Op.ne]: true
}
};
} else if (elevator === null) {
elevatorQuery = {
[Op.or]: [
{
elevator: {
[Op.ne]: true
}
},
{
[Op.and]: [
{
elevator: {
[Op.eq]: true
}
},
{
includeIncompleteAds: {
[Op.eq]: true
}
}
]
}
]
};
}
}
//General query consists of each individual query
const query = {
adType,
realEstateType,
subscribed: true,
[Op.and]: [
geoSearchQueryPart,
priceQuery,
areaQuery,
gardenSizeQuery,
numberOfRoomsQuery,
numberOfFloorsQuery,
floorQuery,
balconyQuery,
newBuildingQuery,
elevatorQuery
]
};
if (accessRoadType) { //AccessRoadType is defined - should exists for each ad and estate type
if (accessRoadType != null) {
query.accessRoadType = { query.accessRoadType = {
[Op.or]: { [Op.or]: {
[Op.eq]: "ANY", [Op.like]: "ANY",
[Op.eq]: accessRoadType [Op.eq]: accessRoadType
} }
}; };
} else if (realEstateTypeObject.hasAccesRoadType) { } else {
checkForIncompleteWanted = true; //Null values are returned for user request that wanted ANY acces road type
} query.accessRoadType = {
[Op.eq]: "ANY"
if (balcony) {
query.balcony = {
[Op.eq]: balcony
}; };
} else if (realEstateTypeObject.hasBalconyProp) {
checkForIncompleteWanted = true;
} }
//Tag to check if incomplete ads are accepted in query
if (newBuilding) {
query.newBuilding = {
[Op.eq]: newBuilding
};
} else if (realEstateTypeObject.hasNewBuildingProp) {
checkForIncompleteWanted = true;
}
if (elevator) {
query.elevator = {
[Op.eq]: elevator
};
} else if (realEstateTypeObject.hasElevatorProp) {
checkForIncompleteWanted = true;
}
//If one of the attributes that exists for property type is null
//we include in query to check if incomplete real estates are accepted
if (checkForIncompleteWanted) { if (checkForIncompleteWanted) {
query.includeIncompleteAds = { query.includeIncompleteAds = {
[Op.eq]: true [Op.eq]: true
}; };
} }
return await db.SearchRequest.findAll({ where: query });
return await db.SearchRequest.findAll({
where: query
});
}; };
module.exports = { module.exports = {

View File

@@ -0,0 +1,14 @@
"use strict";
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn("SearchRequests", "includeWithoutPrice", {
type: Sequelize.BOOLEAN,
defaultValue: true
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.removeColumn("SearchRequests", "includeWithoutPrice");
}
};

View File

@@ -15,7 +15,15 @@ module.exports = (sequelize, DataTypes) => {
allowNull: false, allowNull: false,
defaultValue: { defaultValue: {
type: "Polygon", type: "Polygon",
coordinates: [[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]], coordinates: [
[
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0]
]
],
crs: { type: "name", properties: { name: "EPSG:4326" } } crs: { type: "name", properties: { name: "EPSG:4326" } }
} }
}, },
@@ -71,6 +79,7 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.TEXT type: DataTypes.TEXT
}, },
includeIncompleteAds: DataTypes.BOOLEAN, includeIncompleteAds: DataTypes.BOOLEAN,
includeWithoutPrice: DataTypes.BOOLEAN,
balcony: DataTypes.BOOLEAN, balcony: DataTypes.BOOLEAN,
elevator: DataTypes.BOOLEAN, elevator: DataTypes.BOOLEAN,
newBuilding: DataTypes.BOOLEAN, newBuilding: DataTypes.BOOLEAN,

View File

@@ -18,6 +18,15 @@
</div> </div>
</div> </div>
<br />
<p class="distinguished">
<label class="checkbox-label">
<input type="checkbox" class="filled-in" name="includeWithoutPrice"
checked
>
<span>Uključi i oglase bez cijene</span>
</label>
</p>
<br /> <br />
<div class="row center-align"> <div class="row center-align">

View File

@@ -13,5 +13,5 @@ if (urlToScrape) {
})(); })();
} else { } else {
console.log("No URL to scrape. Use like this : "); console.log("No URL to scrape. Use like this : ");
console.log("npm run test-olx-scraper -- URL_TO_SCRAPE"); console.log("npm run test-rental-scraper -- URL_TO_SCRAPE");
} }