UNPKG

7.57 kBJavaScriptView Raw
1var config = require('../lib/config')();
2var pg = require('pg');
3require('../validators');
4var validate = require('validate.js');
5var errors = require('../errors');
6var squel = require('squel').useFlavour('postgres');
7var helpers = require('../helpers');
8var moment = require('moment');
9var union = require('lodash.union');
10var changes = {};
11
12module.exports = changes;
13var pgURL = config.PostgresURL;
14
15changes.get = function(from, to, users, tags, bbox, callback) {
16 var parseError = validateParams({'from': from, 'to': to});
17 if (parseError) {
18 return callback(new errors.ParseError(parseError));
19 }
20
21 getQuery(from, to, users, tags, bbox, function(err, query, usersData) {
22 if (err) {
23 callback(err, null);
24 return;
25 }
26 pg.connect(pgURL, function(err, client, done) {
27 if (err) {
28 callback(err, null);
29 return;
30 }
31 console.log(query);
32 client.query(query, function(err, result) {
33 done();
34 if (err) {
35 return callback(err, null);
36 }
37 if (result.rows.length === 0) {
38 return callback(new errors.NotFoundError('No records found'));
39 }
40
41 var reducerFn = function(memo, row) {
42 var hour = moment.utc(row.change_at).startOf('hour').toISOString();
43 if (!memo.hasOwnProperty(hour)) {
44 memo[hour] = {};
45 }
46 var username = row.username;
47 if (!memo[hour].hasOwnProperty(username)) {
48 memo[hour][username] = {};
49 }
50 var thisUserMemo = memo[hour][username];
51 ['nodes', 'ways', 'relations'].forEach(function(thing) {
52 if (!thisUserMemo.hasOwnProperty(thing)) {
53 thisUserMemo[thing] = {
54 'c': 0,
55 'm': 0,
56 'd': 0
57 };
58 }
59 ['c', 'm', 'd'].forEach(function(type) {
60 // if row does not always contain these, add error handling:
61 thisUserMemo[thing][type] += row[thing][type];
62 });
63 });
64 ['tags_created', 'tags_modified', 'tags_deleted'].forEach(function(thing) {
65 if (!thisUserMemo.hasOwnProperty(thing)) {
66 thisUserMemo[thing] = {};
67 }
68 Object.keys(row[thing]).forEach(function(tag) {
69 if (!thisUserMemo[thing].hasOwnProperty(tag)) {
70 thisUserMemo[thing][tag] = {};
71 }
72 Object.keys(row[thing][tag]).forEach(function(value) {
73 if (!thisUserMemo[thing][tag].hasOwnProperty(value)) {
74 thisUserMemo[thing][tag][value] = 0;
75 }
76 thisUserMemo[thing][tag][value] += row[thing][tag][value];
77 });
78
79 });
80 });
81
82 thisUserMemo.changesets = union(thisUserMemo.changesets, row.changesets);
83 return memo;
84 };
85
86 callback(null, result.rows.reduce(reducerFn, {}));
87 });
88 });
89 });
90};
91
92function validateParams(params) {
93 var constraints = {
94 'from': {
95 'presence': true,
96 'datetime': true
97 },
98 'to': {
99 'presence': true,
100 'datetime': true
101 }
102 };
103 var errs = validate(params, constraints);
104 if (errs) {
105 var errMsg = Object.keys(errs).map(function(key) {
106 return errs[key][0];
107 }).join(', ');
108 return errMsg;
109 }
110 return null;
111}
112
113function getQuery(from, to, users, tags, bbox, callback) {
114
115 var sql = squel.select({'parameterCharacter': '!!'})
116 .from('stats')
117 .where('change_at > !!', from)
118 .where('change_at < !!', to);
119
120 if (bbox) {
121 var polygonGeojson = JSON.stringify(helpers.getPolygon(bbox).geometry);
122 var changesetSql = squel.select({'parameterCharacter': '!!'})
123 .field('array_agg(id)')
124 .from('changesets')
125 .where('created_at > !!', from)
126 .where('created_at < !!', to)
127 .where('ST_Intersects(changesets.bbox, ST_SetSRID(ST_GeomFromGeoJSON(!!), 4326))', polygonGeojson);
128 sql = sql.where('changesets <@ !!', changesetSql);
129 }
130
131 if (tags) {
132 var tagsArray = tags.split(',').map(function(tag) {
133 return tag;
134 });
135 var tagSql = prepareTagQuery(tagsArray, sql);
136 sql = sql.where(tagSql);
137 }
138
139 if (users) {
140 var usersArray = users.split(',').map(function(user) {
141 return user;
142 });
143
144 getUserIds(usersArray, function(err, usersData) {
145 if (err) {
146 return callback(err);
147 }
148
149 var userIds = [];
150 usersData.rows.forEach(function (r) {
151 userIds.push(r.id);
152 });
153
154 sql.where('uid in !!', userIds);
155 callback(null, sql.toParam(), usersData);
156 });
157 } else {
158 sql.field('u.name', 'username')
159 .field('stats.id', 'id')
160 .field('uid')
161 .field('change_at')
162 .field('nodes')
163 .field('ways')
164 .field('relations')
165 .field('tags_created')
166 .field('tags_modified')
167 .field('tags_deleted')
168 .field('changesets')
169 .join('users', 'u', 'u.id = stats.uid');
170 console.log('#sql', sql.toString());
171 callback(null, sql.toParam());
172 }
173
174
175}
176
177function getUserIds(users, callback) {
178 var userSql = squel.select({'parameterCharacter': '!!'})
179 .from('users')
180 .field('id')
181 .field('name')
182 .where('name in !!', users);
183
184 pg.connect(pgURL, function(err, client, done) {
185 if (err) {
186 return callback(err, null);
187 }
188 client.query(userSql.toParam(), function(err, result) {
189 done();
190 if (err) {
191 return callback(err);
192 }
193
194 if (result.rows.length === 0) {
195 return callback(new errors.NotFoundError('No such users'));
196 }
197 callback(null, result);
198
199 });
200 });
201}
202
203function prepareTagQuery(tags, sql) {
204 // SELECT * FROM json_test WHERE data ? 'a'; - check if 'a' exists as a key.
205 // SELECT * FROM json_test WHERE data ?| array['a', 'b'];
206 // select tags_created from stats where tags_created -> 'shop' ? 'tattoo';
207
208 var tagsSql = squel.expr();
209 tagsSql.options.parameterCharacter = '!!';
210
211 tags.forEach(function (tag) {
212 var key = tag.split('=')[0];
213 var value = tag.split('=')[1];
214 if (value !== '*') {
215 tagsSql.or('tags_created -> !! ? !!', key, value);
216 tagsSql.or('tags_modified -> !! ? !!', key, value);
217 tagsSql.or('tags_deleted -> !! ? !!', key, value);
218 } else {
219 tagsSql.or('tags_created ? !!', key);
220 tagsSql.or('tags_modified ? !!', key);
221 tagsSql.or('tags_deleted ? !!', key);
222 }
223 });
224 return tagsSql;
225}