-
Notifications
You must be signed in to change notification settings - Fork 6
/
iNat_identifier_stats.html
192 lines (187 loc) · 10.8 KB
/
iNat_identifier_stats.html
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
180
181
182
183
184
185
186
187
188
189
190
191
192
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, minimum-scale=1.0" />
<meta name="description" content="iNaturalist Identifier Stats" />
<title>iNaturalist Identifier Stats</title>
<style>
:root {
background: var(--color-base);
color: var(--color-text);
font: 14px Sans-Serif;
--color-base: white;
--color-alt: whitesmoke;
--color-brand: forestgreen;
--color-brand-accent: #ddffdd;
--color-text: black;
--color-text-invert: white;
--color-text-link: royalblue;
--color-border: lightgray;
--color-hover: lightgray;
--color-base-translucent: rgba(255,255,255,0.85);
}
@media (prefers-color-scheme: dark) {
:root {
--color-base: black;
--color-alt: #171717;
--color-brand: forestgreen;
--color-brand-accent: #003300;
--color-text: #bababa;
--color-text-invert: black;
--color-text-link: cornflowerblue;
--color-border: #444;
--color-hover: #444;
--color-base-translucent: rgba(0,0,0,0.85);
}
}
#main { width:100%; }
table, td, th { border-collapse:collapse; margin:0; padding:4px; }
th { position:-webkit-sticky /*Safari*/; position:sticky; top:0; font-weight:600; background:var(--color-brand); color:var(--color-text-invert); text-align:left; vertical-align:bottom; }
tbody>tr { border-width:1px 0px; border-style:solid; border-color:var(--color-border); background:var(--color-alt);}
tr:nth-child(even) { background:var(--color-base); }
.tar { text-align:right; }
.icon { height:48px; width:48px; border-radius:50%; }
.photo { height:64px; width:64px; }
img { margin:0; padding:0; border:0; }
a { text-decoration:none; color:var(--color-text-link); }
a:hover { background:var(--color-hover); }
#nav { margin-left:-8px; position:-webkit-sticky /*Safari*/; position:sticky; left:0; bottom:0; background:var(--color-base-translucent); width:188px; height:52px; border-radius:14px; }
@media print { #nav { display:none; } }
.button, .button_inactive { display:inline-block; margin-left:10px; margin-top:10px; height:32px; width:32px; border:1px solid var(--color-border); border-radius:35%; font-size:24px; vertical-align:middle; text-align:center; }
.button { background:var(--color-alt); }
.button_inactive { background:none; color:var(--color-border); }
.bin_true { text-decoration: underline wavy rgba(34,139,34,0.35); }
.bin_false { text-decoration: underline wavy rgba(178,34,34,0.35); }
#divresults { padding-top:12px; }
select { border-color:color:var(--color-border); border-style:solid; border-width:1px; padding:2px 0px; background:var(--color-base); color:var(--color-text); }
input { border-color:color:var(--color-border); border-style:solid; border-width:1px; padding:2px 3px; background:var(--color-base); color:var(--color-text); }
form { background:var(--color-base); padding:0px; }
</style>
</head>
<body>
<form action="javascript:fgetuser()">
<input type='text' id="input_user" placeholder='Input a User ID here' />
<button type='submit'>Get Identifier Stats</button>
</form>
<hr />
<script>
let winurlstr = window.location.href;
let winurlsearchstr = window.location.search;
let winurlexsearchstr = winurlstr.replace(winurlsearchstr,'');
let winurlparams = new URLSearchParams(winurlsearchstr.substring(1));
//let p_per_page = winurlparams.get('per_page') || 200;
//let p_page = winurlparams.get('page') || 1;
let input_user = document.getElementById('input_user');
let p_user_id = winurlparams.get('user_id');
if (p_user_id) {
input_user.value = p_user_id;
fresults();
}
else {
faddelem('p',document.body,{innerText:'Input a valid User Login or ID, and click the button to retrieve identifier stats. Alternatively, you may select a user by adding user_id as a query parameter in the URL. Note that user logins input into the text input field on the page will be automatically translated to lowercase. In the rare cases of users with mixed-case logins, those can be searched by querying for user_id=[Login] in the URL or by searching using a numeric ID.'});
};
function furl(url,txt=url) { return '<a href="'+url+'">'+txt+'</a>'; };
function famp(str) { return str.replace(/&/g,'&'); };
function fcomnum(n) { return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g,',') };
function faddelem(etype,eparent=null,eattributes={}) {
let eobj = document.createElement(etype);
for (let [key,value] of Object.entries(eattributes)) {
if ( typeof value === 'object' && value !== null ) {
for (let [subkey,subvalue] of Object.entries(value)) { eobj[key][subkey] = subvalue; };
}
else { eobj[key] = value; };
};
if (eparent) { eparent.appendChild(eobj); };
return eobj;
};
function faddelems(etype,eparent=null,eattributes=[]) { for (let e of eattributes) { faddelem(etype,eparent,e); }; };
function faddtr(tparent,td=[]) {
let row = faddelem('tr',tparent);
faddelems('td',row,td);
};
function fISOdate(s) {
let d = s.split(/\D+/);
let o = (s.search('[\+]')>=0)?-1:1;
return new Date(Date.UTC(d[0], d[1]-1 /*months:0-11*/, d[2], d[3], d[4], d[5]) + ((d[6]||0)*3600000*o) + ((d[7]||0)*60000*o) ); // iNat dates don't come with milliseconds
};
function fparamdate(n) {
d = new Date(n);
function fpad(num) { return num.toString().padStart(2,'0') };
return fpad(d.getFullYear())+'-'+fpad(d.getMonth()+1)+'-'+fpad(d.getDate())+'T'+fpad(d.getHours())+':'+fpad(d.getMinutes())+':'+fpad(d.getSeconds());
};
function ffetch(url) {
return fetch(url)
.then((response) => {
if (!response.ok) { throw new Error(response.status+': '+response.statusText); };
return response.json();
})
.catch((err) => { console.error(err); });
};
function fgetuser() {
if (input_user.value.length === 0) { document.location.href=winurlexsearchstr }
else {
winurlparams.set('user_id',input_user.value.toLowerCase());
input_user.value = p_user_id;
document.location.href=winurlexsearchstr+'?'+winurlparams;
};
};
function fresults() {
let div_main = document.getElementById('main');
if (div_main) { div_main.remove(); }
div_main = faddelem('div',document.body,{id:'main'});
let user_id = input_user.value
let urlsumbycat = 'iNat_id_summary_by_category.html';
let urlsumbyic = 'iNat_id_counts_by_iconic_taxa.html';
let urlids = 'iNatAPIv1_identifications.html'
let urlobs = 'iNatAPIv1_observations.html'
let apibase = 'https://api.inaturalist.org/v1/';
let date_curr = new Date();
let prom_user = ffetch(`${apibase}users/${user_id}`);
let prom_id_all = ffetch(`${apibase}identifications?per_page=0&only_id=true&user_id=${user_id}`);
let prom_id_others = ffetch(`${apibase}identifications?per_page=0&only_id=true&own_observation=false&user_id=${user_id}`);
let prom_id_last = ffetch(`${apibase}identifications?per_page=1&only_id=false¤t=any&is_change=false&user_id=${user_id}`);
//let prom_id_t24hrs = ffetch(apibase + 'identifications?per_page=0&only_id=true&is_change=false&user_id=' + user_id +'&d1=' + fparamdate(date_curr-(1000*60*60*24)));
let prom_annotated_obs_all = ffetch(`${apibase}observations?per_page=0&only_id=true&annotation_user_id=${user_id}`);
let prom_annotated_obs_others = ffetch(`${apibase}observations?per_page=0&only_id=true&annotation_user_id=${user_id}¬_user_id=${user_id}`);
Promise.all([prom_user,prom_id_all,prom_id_others,prom_id_last/*,prom_id_t24hrs*/,prom_annotated_obs_all,prom_annotated_obs_others])
.then(req=>{
if (!req[0]) {
faddelem('p',div_main,{innerText:'No data found. Input a valid User Login or ID and try again.'});
return false;
}
else {
let user = req[0].results[0];
let id_all = req[1].total_results || user.identifications_count || 0 ;
let id_others = req[2].total_results || user.identifications_count || 0 ;
let date_last_id = (req[3].results && req[3].results[0]) ? fISOdate(req[3].results[0].created_at) : 'N/A';
let diff_last_id = (date_curr-date_last_id) / 1000 / 60 / 60 / 24;
//let id_t24hrs = req[4].total_results;
let date_join = fISOdate(user.created_at);
let diff_days = (date_curr-date_join) / 1000 / 60 / 60 / 24;
let annotated_obs_all = req[4].total_results || 0 ;
let annotated_obs_others = req[5].total_results || 0 ;
if (user.icon_url) { faddelem('img',div_main,{src:user.icon_url}); };
let table = faddelem('table',div_main);
let tbody = faddelem('tbody',table);
faddtr(tbody,[{innerText:'User Login:'},{innerHTML:furl('https://www.inaturalist.org/people/'+user.login,user.login)}]);
faddtr(tbody,[{innerText:'User ID:'},{innerText:user.id}]);
if (user.name) { faddtr(tbody,[{innerText:'User Name:'},{innerText:user.name}]); };
faddtr(tbody,[{innerText:'Current Date:'},{innerText:date_curr.toString()}]);
faddtr(tbody,[{innerText:'Date of Last ID:'},{innerText:date_last_id.toString()}]);
faddtr(tbody,[{innerText:'Days Since Last ID:'},{innerText:diff_last_id?fcomnum(diff_last_id.toFixed(2)):'N/A'}]);
faddtr(tbody,[{innerText:'Date Joined:'},{innerText:date_join.toString()}]);
faddtr(tbody,[{innerText:'Days Since Joining:'},{innerText:fcomnum(diff_days.toFixed(2))}]);
faddtr(tbody,[{innerText:'IDs (all):'},{innerHTML:fcomnum(id_all)+' ( view: '+furl(urlids+'?per_page=100&user_id='+user.id,'detail')+' | '+furl(urlsumbycat+'?user_id='+user.id,'by category')+' | '+furl(urlsumbyic+'?user_id='+user.id,'by iconic taxa')+' )'}]);
faddtr(tbody,[{innerText:'IDs (for others):'},{innerHTML:fcomnum(id_others)+' ( view: '+furl(urlids+'?per_page=100&own_observation=false&user_id='+user.id,'detail')+' | '+furl(urlsumbycat+'?own_observation=false&user_id='+user.id,'by category')+' | '+furl(urlsumbyic+'?own_observation=false&user_id='+user.id,'by iconic taxa')+' )'}]);
faddtr(tbody,[{innerText:'IDs (for others) per day:'},{innerText:fcomnum((id_others/diff_days).toFixed(2))}]);
//faddtr(tbody,[{innerText:'IDs (all) last 24hrs:'},{innerHTML:fcomnum(id_t24hrs)}]);
faddtr(tbody,[{innerText:'Annotated Obs (all):'},{innerHTML:furl(`${urlobs}?per_page=200&annotation_user_id=${user_id}`,fcomnum(annotated_obs_all))}]);
faddtr(tbody,[{innerText:'Annotated Obs (for others):'},{innerHTML:furl(famp(`${urlobs}?per_page=200&annotation_user_id=${user_id}¬_user_id=${user_id}`),fcomnum(annotated_obs_others))}]);
faddtr(tbody,[{innerText:'Annotated Obs (for others) per day:'},{innerText:fcomnum((annotated_obs_others/diff_days).toFixed(2))}]);
};
});
};
</script>
</body>
</html>