-
Notifications
You must be signed in to change notification settings - Fork 6
/
iNat_user_obs_photo_license_summary.html
201 lines (189 loc) · 8.82 KB
/
iNat_user_obs_photo_license_summary.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
193
194
195
196
197
198
199
200
201
<!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 User Observation Summary by Photo License" />
<title>iNaturalist User Observation Summary by Photo License</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-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-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; z-index:100; 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); }
</style>
</head>
<body>
<script>
//get parameters from the url
let winurlstr = window.location.href;
let winurlsearchstr = window.location.search;
let winurlexsearchstr = winurlstr.replace(winurlsearchstr,'');
let winurlparams = new URLSearchParams(winurlsearchstr.substring(1));
let p_user_id = winurlparams.get('user_id')?.split(',') || winurlparams.get('user_login')?.split(',') || [];
p_user_id = p_user_id.sort().filter((item,pos,ary)=>{ return !pos || item!=ary[pos-1]; }); // remove duplicate values;
winurlparams.delete('user_id');
winurlparams.delete('user_login');
winurlparams.delete('photo_license');
winurlparams.delete('photos');
winurlparams.delete('photos_licensed');
winurlparams.delete('page');
winurlparams.delete('per_page');
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 fround(num,places) {
let n = num*1;
return n.toFixed(places);
};
function ffetch(url) {
return fetch(url)
.then((response) => {
if (!response.ok) { throw new Error(response.status+': '+response.statusText); };
return response.json();
})
.then((data) => { return data; })
.catch((err) => { console.error(err); });
};
function delay(time,value) {
return new Promise(function(resolve) {
setTimeout(resolve.bind(null, value), time)
console.log('Pausing '+time+'ms...')
});
};
async function fgetstats(users,licenses) {
// recommended API request limits are 1 request per second.
// so if we're going to be issuing a larger number of requests, then we'll want to wait a 1 second between requests.
let delayms = (users.length * (licenses.length + 1) > 20) ? 1000 : 0;
for (user of users) {
// get base user info (we're getting these one by one because the API doesn't accept multiple inputs)
let urluser = `https://api.inaturalist.org/v1/users/${user}`;
let user_results = await ffetch(urluser);
let user_rec = user_results?.results[0];
// if user exists, then set the user login link and get user observation counts
if (user_rec?.login) {
document.getElementById(user+'_user_id').innerHTML = furl(`https://www.inaturalist.org/users/${user_rec.id}`,user_rec.id);
document.getElementById(user+'_user_login').innerHTML = furl(`https://www.inaturalist.org/users/${user_rec.login}`,user_rec.login);
document.getElementById(user+'_user_name').innerHTML = user_rec.name||'';
let urluserobs = `https://api.inaturalist.org/v1/observations?per_page=0&${winurlparams}&user_id=${user}`;
for (license of licenses) {
await delay(delayms); // ignore this until this page is modified to return multiple users
let paramlicense = (license.code==='all')?'&photos=true':(license.code==='none')?'&photos=true&photo_licensed=false':('&photo_license='+license.code);
Promise.all([
Promise.resolve(user),
Promise.resolve(license.code),
ffetch(urluserobs+paramlicense),
Promise.resolve(paramlicense),
])
.then(results=>{
document.getElementById(results[0]+'_'+results[1]).innerHTML = furl(`https://www.inaturalist.org/observations?${winurlparams}&user_id=${results[0]}${paramlicense}`,(results[2]?fcomnum(results[2].total_results):0));
});
};
};
await delay(delayms);
};
};
function fresults(results) {
if (results) {
// faddelem('p',document.body,{innerHTML:'total records: '+fcomnum(results.length)});
let licenses = [
{code:'all',description:'Obs with Photos'},
{code:'none',description:'None Licensed'},
{code:'cc-by-nc-sa',description:'CC BY-NC-SA'},
{code:'cc-by-nc-nd',description:'CC BY-NC-ND'},
{code:'cc-by-sa',description:'CC BY-SA'},
{code:'cc-by-nc',description:'CC BY-NC'},
{code:'cc-by-nd',description:'CC BY-ND'},
{code:'cc-by',description:'CC BY'},
{code:'cc0',description:'CC0'},
];
// table and headers
let table = faddelem('table',document.body,{id:'main'});
let thead = faddelem('thead',table);
let hrow = faddelem('tr',thead);
let labels = [
{innerText:'#'},
{innerText:'ID'},
{innerText:'Login'},
{innerText:'Name'},
//{classList:'tar',innerText:'Obs Count'},
];
for (license of licenses) { labels.push({classList:'tar',innerText:license.description}); };
faddelems('th',hrow,labels);
// table rows
let tbody = faddelem('tbody',table);
for (let i=0; i<results.length; i++) {
let brow = faddelem('tr',tbody);
let rec = results[i];
let values = [
{innerText:i+1},
{innerText:rec,id:`${rec}_user_id`},
{innerText:rec,id:`${rec}_user_login`},
{id:`${rec}_user_name`},
//{id:`${rec}_user_obs_count`},
];
for (license of licenses) { values.push({classList:'tar',id:`${rec}_${license.code}`}); };
faddelems('td',brow,values);
};
fgetstats(results,licenses);
}
else { faddelem('p',document.body,{innerText:'No results returned.'}); };
};
faddelem('h1',document.body,{innerText:document.title});
if (p_user_id.length===0) {
let instructions = [
{innerHTML:'This page gets iNaturalist users, along with counts of observations with photos by photo license for each user. You can click on any of the counts to see the underlying observations. You must input at least a user_id to get results -- separate multiple user_id values with commas -- and you can also add additional filters for observations.'},
{innerHTML:'For example, to get a summary of animal observations for users kueda and loarie, open '+furl(winurlexsearchstr+'?user_id=kueda,loarie&taxon_id=1&verifiable=any')+' in your browser.'},
{innerHTML:'Note that because iNat puts limits on API requests, this page will intentionally slow down the rate at which it gets data if you query for more than a couple of users at a time.'},
];
faddelems('p',document.body,instructions);
}
else { fresults(p_user_id); };
</script>
</body>
</html>