-
Notifications
You must be signed in to change notification settings - Fork 3
/
retrieveData.js
179 lines (150 loc) · 6.79 KB
/
retrieveData.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
const algosdk = require('algosdk');
const fs = require('fs');
// This example uses the PureStake API
const server = "https://testnet-algorand.api.purestake.io/ps1";
const port = "";
const token = {
'X-API-key' : 'B3SU4KcVKi94Jap2VXkK83xx38bsv95K5UZm2lab',
};
// Replace the above by https://mainnet-algorand.api.purestake.io/ps1
// and your API-Token from PureStake
// To use your own node, find the token, server, and port values for
// your installation in the algod.net and algod.token files within
// the Algorand data directory, and put them below:
// const token = "token from the file algod.token";
// const server = "http://127.0.0.1"; // usually running as localhost
// const port = 8081; // the default port for local algod
// An array of all possible attributes
const headers = [
'_t', // string, app identifier, mandatory, must be 'report'
'_v', // integer, version number, mandatory, must be 1
// general demographic data
'gc', // string, country code (see Location Data section below)
'gr', // string, region code (see Location Data section below)
'gzp', // string, 3-digit zip code (US only)
'ga', // integer, age group, if present must be in 1,11,21,31,41,51,56,61,66,71,76,81,85
'gs', // string , gender, if present must be 'm','f'
// symptoms
'sz', // integer, is symptomatic, no-answer=0/no=-1/yes=1
's1', // boolean, fever
's2', // boolean, cough
's3', // boolean, difficulty breathing
's4', // boolean, fatigue
's5', // boolean, sore throat
'sds', // date, when symptoms started, yyyy-mm-dd
'sde', // date, when symptoms ended, yyyy-mm-dd
'sdn', // boolean, still symptomatic
// tested
'tz', // integer, tested, no-answer=0/no=-1/yes=1
'tt', // integer, tried to get tested, no=-1, yes=1, yes but was denied=2
'td', // date, test date, yyyy-mm-dd
'tr', // integer, test results, -1=negative,1=positive,2=waiting for result
'tl', // integer, test location, 1=Dr office/2=Hospital/3=Urgent care/4=Ad-hoc center/5=Other
// medical care
'mz', // integer, received care, no-answer=0/no=-1/yes=1
'm1', // boolean, doctor's office
'm2', // boolean, walk-in clinic
'm3', // boolean, virtual care
'm4', // boolean, hospital/ER
'm5', // boolean, other
'mh', // integer, hospitalized, no-answer=0/no=-1/yes=1
'mhs', // date, when admitted, yyyy-mm-dd
'mhe', // date, when discharged, yyyy-mm-dd
'mhn', // boolean, still in hospital
// quarantine
'qz', // integer, was quarantined, no-answer=0/no=-1/yes=1
'q1', // boolean, due to symptoms
'q2', // boolean, voluntarily
'q3', // boolean, personally required
'q4', // boolean, general quarantine
'qds', // date, when quarantine started, yyyy-mm-dd
'qde', // date, when quarantine ended, yyyy-mm-dd
'qdn', // boolean, still quarantined
'ql', // integer, left quarantine temporarily no-answer=0/no=-1/yes=1
'consent' // boolean' , user's consent, mandatory, must be 'true'
];
// When retrieving the real data from mainnet, replace the address below by
// const address = "COVIDR5MYE757XMDCFOCS5BXFF4SKD5RTOF4RTA67F47YTJSBR5U7TKBNU"
// const fromRound = 5646000
const address = "7IOLVZCTP3N5ZZMMDLPZI5EQP7PGXO2A7CYHBZR6IGXKE56H7ISUS66ZKI";
const fromRound = 5690110;
// The PureStake API put a limit of how many transactions you can get
// for each call to algod.transactionByAddress(...). Experimentally
// it seems like 500 is the bound used there
const maxTxnPerCall = 500;
// Initialize the algod client and get the basic parameters
const algod = new algosdk.Algod(token, server, port);
// A recursive function for getting a batch of transactions, to overcome
// the limitation of maxTxnPerCall transaction per call to the API
async function getTransactionBatch(fromRnd, toRnd) {
if (fromRnd > toRnd) { // sanity check
return [];
}
// make an API call to get the transactions
let txs = await algod.transactionByAddress(address,fromRnd,toRnd,maxTxnPerCall);
// there might not have any tx in range in which case sdk returns empty object ...
if (typeof txs.transactions === 'undefined') {
return [ ];
}
// If we got all the transactions, just return them
if (fromRnd == toRnd || txs.transactions.length < maxTxnPerCall) {
return txs.transactions;
// FIXME: If a single block contains more than maxTxnPerCall
// transactions for the target address, the code above will return
// only maxTxnPerCall of them.
// This is an unlikely case, and not easy to handle. The only way to
// handle it is to call algod.block(round#), then go over all the
// transactions in this block and take only the ones corresponding
// to the target address.
}
else { // recursive call to get them in two smaller chunks
let midRnd = Math.floor((fromRnd+toRnd) / 2);
let txns1 = await getTransactionBatch(fromRnd, midRnd);
let txns2 = await getTransactionBatch(midRnd+1, toRnd);
// return the concatenation of the two chunks
return txns1.concat(txns2);
}
}
// Read the transactions from the blockchain. Your Algorand node must
// be an archival node in indexer mode for the code below to work
(async() => {
let rowcnt = 0;
let fstr = fs.createWriteStream('covidData.csv')
fstr.write('sr,rnd,'+headers.toString()+'\n'); // write the header
// 'sr' is serial number, 'rnd' is round number
const params = await algod.getTransactionParams();
// Read the transactions from the blockchain in 512-block installations
const batchSize = 512;
for (let rnd = fromRound; rnd < params.lastRound; rnd+=batchSize) {
let toRnd = rnd + batchSize;
if (toRnd > params.lastRound) {
toRnd = params.lastRound;
}
// Fetch transactions for these rounds
let txns = await getTransactionBatch(rnd, toRnd);
// Write transaction
for (let i = 0; i < txns.length; i++) {
// Decode data in Note field
let tx = txns[i];
let noteData = algosdk.decodeObj(tx.note);
// must have a serial number and data, else continue to next one
if (!noteData.s || !noteData.d) {
continue;
}
let line = noteData.s+','+tx.round;
for (let j = 0; j < headers.length; j++) {
line += ","; // followed by a comma
let key = headers[j];
if (noteData.d[key]) { // if a value exists
line += noteData.d[key]; // write it
}
}
fstr.write(line+'\n');
rowcnt++;
}
}
console.log("Number of Rows: " + rowcnt);
fstr.end(); // close the file
})().catch(e => {
console.log(e);
});