-
Notifications
You must be signed in to change notification settings - Fork 1
/
lib.js
148 lines (119 loc) · 3.87 KB
/
lib.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
const { default: axios } = require('axios');
const allLocations = require('./locations.json');
const debug = require('debug')('lib')
const getLocations = (ids) => {
return allLocations.filter(location => ids.includes(location.id + ''));
}
const getAllLocations = () => {
return allLocations;
}
const calculateAggregates = (data, perMonth) => {
const buckets = {
all: data
};
if (perMonth) {
// split per month
for (const datum of data) {
const soldDate = new Date(datum.soldDate);
const bucketKey = `${soldDate.getFullYear()}-${soldDate.getMonth() + 1}`;
if (!(bucketKey in buckets)) {
buckets[bucketKey] = [];
}
buckets[bucketKey].push(datum);
}
}
const aggregates = {}
Object.keys(buckets)
.forEach(key => aggregates[key] = _calculateAverageFromData(buckets[key]));
return perMonth ? aggregates : aggregates.all
}
const _isValidRecord = (record) => {
const hasSoldPrice = record.soldPrice && record.soldPrice.raw;
const hasSoldSqmPrice = record.soldSqmPrice && record.soldSqmPrice.raw;;
return hasSoldPrice && hasSoldSqmPrice;
}
const _calculateAverageFromData = (data) => {
const filteredData = data.filter(_isValidRecord)
const recordCount = filteredData.length;
const metrics = ['soldPrice', 'soldSqmPrice'];
const sums = { soldPrice: 0, soldSqmPrice: 0 };
for (const datum of filteredData) {
metrics.forEach(metric => sums[metric] += datum[metric].raw);
}
debug("Calculating aggregated with count", recordCount, sums);
const result = {};
metrics.forEach(metric => {
const newMetricKey = 'average' + metric.charAt(0).toUpperCase() + metric.slice(1);
result[newMetricKey] = Math.round(sums[metric] / recordCount)
});
return {
...result,
recordCount
}
}
const getDataFromBooli = async (locations, options) => {
const result = [];
for (const location of locations) {
const data = await getDataFromBooliForLocation(location.id, options);
result.push({ ...location, data });
}
return result;
}
const query = `
query SearchSold($page: Int!, $areaId: ID!, $objectType: String!)
{
searchSold(input: {areaId: $areaId, page:$page, filters: [{ key: "objectType", value: $objectType}], sort:"soldDate"}) {
pages,
result {
soldPrice{
raw
},
soldSqmPrice {
raw
}
soldDate
}
}
}`
const endpoint = 'https://www.booli.se/graphql'
const getDataFromBooliForLocation = async (locationId, options) => {
const pagesToCheck = options.pages || 1;
let data = [];
let objectTypesArray = [];
if (options.flats) {
objectTypesArray = objectTypesArray.concat(['Lägenhet']);
}
if (options.houses) {
objectTypesArray = objectTypesArray.concat(['Villa', 'Parhus', 'Radhus', 'Kedjehus']);
}
objectType = objectTypesArray.join(',');
for (let page = 1; page <= pagesToCheck; page++) {
debug(`Fetching from ${endpoint}`);
try {
const variables = {
page,
areaId: locationId,
objectType
}
const response = await axios.post(endpoint, { query, variables }, { headers: { 'Content-type': 'application/json' } });
if (response.data) {
data = data.concat(response.data.data.searchSold.result)
}
} catch (e) {
console.error("Request failed", e.response.data)
}
}
debug(`Fetched ${data.length} records for location ${locationId}`)
return data;
}
const panic = (msg) => {
console.error(msg);
process.exit(1);
}
module.exports = {
getLocations,
getAllLocations,
getDataFromBooli,
calculateAggregates,
panic
}