Commit a1067cbd authored by Sam Muirhead's avatar Sam Muirhead

add working Noun Project 'Print from API' interface

- update output images for READMEs
- add video with overview of how to use the interface
- basic installation documentation
- add printable  PDFs with hundreds of hand-drawn illustrations
- index.js provided without Noun Project key/secret
parent ef5380af
# Using the Noun Project API to make use and attribution of images easier
(see Issue #21, [_'Making accurate credits takes a long time'_](https://gitlab.com/cameralibre/cut-copy-and-paste/issues/21) for more context)
I'm trying to make a very simple search interface for the Noun Project, where a workshop participant can type in a word, eg. _bee_, and the top 8 images returned will be immediately printed out, with license, attribution, ID, and a thumbnail (they will likely cut the metadata off the image, so the thumbnail helps them link it back to the image they have used)
![Noun Project Images: Printing from the API](Noun-Project_print-from-API.webm)
This is a very simple search interface for the Noun Project, designed to be set up on a laptop at the workshop. It uses the Noun Project API and a proxy server on the same computer for authentication.
In the search interface, a workshop participant can type in a word, eg. _banana_, and the top 8 images returned will be immediately printed out, with license, attribution, ID, and a thumbnail (they will likely cut the metadata off the image, so the thumbnail helps them link it back to the image they have used)
### Current layout:
![](../../../Images/README-Images/np-search-print-output.png)
I'm also trying to make an automated credits page - participants can note down the images they have used (by referring to the commit log) and then type a series of IDs into the connected laptop. Up to 8 IDs can be input, and a credits page is printed.
I would like to prioritize Noun Project images with the 'handdrawn' tag but I haven't worked out how to do that yet.
There's also an automated credits page - participants can note down the images they have used (by referring to the commit log, or by gluing the 'metadata' info to the back of their work). They then type a series of IDs into the connected laptop. Up to 8 IDs can be input, and a credits page is printed.
(for now, if a participant uses more than 8 images, they can print multiple credits pages and hack them together with scissors and glue)
### Current layout:
![](../../../Images/README-Images/np-credits-print-output.png)
The layouts are good enough to work with for now, and they're printing ok (with Beaker Browser) so the next steps are writing the rest of the javascript to correctly populate both pages, and setting it up to work with the Noun Project API directly.
\ No newline at end of file
The layouts are printing nicely and have been tested in a workshop at the Mozilla Fellows Winter Summit in San Francisco.
Many thanks to [Darius Kazemi](https://tinysubversions.com/) for helping me with OAuth authentication and setting up the proxy server!
### Using the interface
To run this yourself, you'll need a free 'Playground' access key from the Noun Project's [Developers page](https://thenounproject.com/developers/).
Download this folder! You will need to paste your Noun Project key and secret into lines 47 & 48 of index.js for it to authenticate properly.
You'll also need to install [node.js](https://nodejs.org/en/) for your system.
In your terminal, [change directory](https://tutorial.djangogirls.org/en/
intro_to_command_line/#change-current-directory) to this folder, and type ```node index.js``` to start the server.
If it's working, you should see a message saying ```Example app running on port 3000!```
If you have any trouble, make sure that the ```cors``` module is installed. Still in the terminal, type ```npm install cors``` to install it.
Once the server is running, you can open ```search-results.html``` with a browser. Although Firefox is my go-to browser, but I found that printing from [Beaker Browser](https://beakerbrowser.com) gave me more reliable results with smaller margins - I assume any Chromium-based browser will perform similarly.
\ No newline at end of file
......@@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<title>Noun-project-SVG</title>
<title>CREDITS</title>
<link rel="stylesheet" href="./styles.css">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
......@@ -202,11 +202,11 @@
<use href="#panel-0" transform="scale(2) translate(280.75,0)"/>
</svg>
<form action="/url-where-you-want-to-submit-form-data">
<input type="text" placeholder="12345">
<button type="submit">Add ID</button></form>
<button type="button" id="printButton" onclick="window.print()" >Print!</button>
<input id=text type="text" placeholder="eg. 12345">
<button type="submit" onclick="getCreditInfo()">Add ID</button>
<button type="button" id="printButton" onclick="window.print()" >Print!</button>
</body>
<script type="application/javascript" src="./populate-credits.js"></script>
......
......@@ -16,15 +16,36 @@ app.get('/', cors(), (req, res) => {
});
});
app.get('/icon', cors(), (req, res) => {
console.log(req.query);
let icon = req.query.icon;
getIcon(icon).then((data) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.json(data)
});
});
app.get('/user', cors(), (req, res) => {
console.log(req.query);
let username = req.query.username;
let page = req.query.page;
getUserUploads(username, page).then((data) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.json(data)
});
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
function getImages(term) {
return new Promise((resolve, reject) => {
var twitter = OAuth({
var nounProject = OAuth({
consumer: {
key: 'xxxxxx',
secret: 'xxxxxx'
key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
// ENTER YOUR NOUN PROJECT KEY & SECRET HERE
},
signature_method: 'HMAC-SHA1',
......@@ -34,7 +55,8 @@ function getImages(term) {
});
twitter.get({
nounProject.get({
// url: 'https://api.thenounproject.com/icons/' + term
url: 'https://api.thenounproject.com/icons/' + term,
qs: {
limit: 8
......@@ -45,3 +67,54 @@ function getImages(term) {
});
});
}
function getIcon(icon) {
return new Promise((resolve, reject) => {
var nounProject = OAuth({
consumer: {
key: 'b55cb0b73b784d579415f0e668d3b487',
secret: 'e0fc5d67199f44d7a23b6fc4337aea44'
// ENTER YOUR NOUN PROJECT KEY & SECRET HERE
},
signature_method: 'HMAC-SHA1',
hash_function: function(base_string, key) {
return crypto.createHmac('sha1', key).update(base_string).digest('base64');
}
});
nounProject.get({
// url: 'https://api.thenounproject.com/icons/' + term
url: 'https://api.thenounproject.com/icon/' + icon,
json: true
}, function(err, res, icon) {
resolve(icon);
});
});
}
function getUserUploads(username, page) {
return new Promise((resolve, reject) => {
var nounProject = OAuth({
consumer: {
key: 'b55cb0b73b784d579415f0e668d3b487',
secret: 'e0fc5d67199f44d7a23b6fc4337aea44'
// ENTER YOUR NOUN PROJECT KEY & SECRET HERE
},
signature_method: 'HMAC-SHA1',
hash_function: function(base_string, key) {
return crypto.createHmac('sha1', key).update(base_string).digest('base64');
}
});
nounProject.get({
// url: 'https://api.thenounproject.com/icons/' + term
url: 'https://api.thenounproject.com/user/' + username + "/uploads?limit=8&page=" + page,
json: true
}, function(err, res, icon) {
resolve(icon);
});
});
}
//
// async function getData()
// {
// //await the response of the fetch call
// let response = await fetch('https://gitlab.com/cameralibre/cut-copy-and-paste/raw/master/Print/Noun-Project-Illustrations/Print-from-API/api-response');
// //proceed once the first promise is resolved.
// let data = await response.json()
// //proceed only when the second promise is resolved
// return data;
// }
// //call getData function
// getData()
// .then(data => console.log(data));
var creditCounter = 0
function getCreditInfo() {
let icon = document.getElementById('text').value;
fetch('http://localhost:3000/icon/?icon=' + icon)
.then(function(response) {
return response.json();
})
.then(function(iconData) {
console.log(iconData);
var iconData = { "icons": [
{
"attribution": "bee by Juan Sebastian Rickenmann from Noun Project",
"attribution_preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/attribution/5673-600.png",
"collections": [],
"date_uploaded": "2012-10-04",
"id": "5673",
"is_active": "1",
"is_explicit": "0",
"license_description": "creative-commons-attribution",
"nounji_free": "0",
"permalink": "/term/bee/5673",
"preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/png/5673-200.png",
"preview_url_42": "https://d30y9cdsu7xlg0.cloudfront.net/png/5673-42.png",
"preview_url_84": "https://d30y9cdsu7xlg0.cloudfront.net/png/5673-84.png",
"sponsor": {},
"sponsor_campaign_link": null,
"sponsor_id": "",
"tags": [
{
"id": 1584,
"slug": "bee"
},
{
"id": 1656,
"slug": "fly"
},
{
"id": 1586,
"slug": "honey"
},
{
"id": 666,
"slug": "insect"
},
{
"id": 1587,
"slug": "nectar"
},
{
"id": 2218,
"slug": "sting"
}
],
"term": "bee",
"term_id": 1584,
"term_slug": "bee",
"uploader": {
"location": "CH",
"name": "Juan Sebastian Rickenmann",
"permalink": "/sebastian.rickenmann",
"username": "sebastian.rickenmann"
},
"uploader_id": "1656",
"year": 1987
},
{
"attribution": "bee by James Keuning from Noun Project",
"collections": [],
"date_uploaded": "2012-10-12",
"icon_url": "https://d30y9cdsu7xlg0.cloudfront.net/noun-svg/6063.svg?Expires=1548922759&Signature=H98F5sMdrh2icccuZyHYwd70cDztXLPmqckqBReNxu7g0mN3JHPIaM88Wuo7F6lVzqjxfyZBQu6uOWbv9iVmvJKzlNrk3rgYBick9jqdn~tSCnEEOHED24Mae8~zTPULNOXaV~m2db3USYNcbbR7Y4BHMuoAYYbry75dS1Z6zQ_&Key-Pair-Id=APKAI5ZVHAXN65CHVU2Q",
"id": "6063",
"is_active": "1",
"is_explicit": "0",
"license_description": "public-domain",
"nounji_free": "0",
"permalink": "/term/bee/6063",
"preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/png/6063-200.png",
"preview_url_42": "https://d30y9cdsu7xlg0.cloudfront.net/png/6063-42.png",
"preview_url_84": "https://d30y9cdsu7xlg0.cloudfront.net/png/6063-84.png",
"sponsor": {},
"sponsor_campaign_link": null,
"sponsor_id": "",
"tags": [
{
"id": 1584,
"slug": "bee"
},
{
"id": 2175,
"slug": "colony"
},
{
"id": 1656,
"slug": "fly"
},
{
"id": 1585,
"slug": "hive"
},
{
"id": 1586,
"slug": "honey"
},
{
"id": 666,
"slug": "insect"
},
{
"id": 6667,
"slug": "pollen"
},
{
"id": 2218,
"slug": "sting"
}
],
"term": "bee",
"term_id": 1584,
"term_slug": "bee",
"uploader": {
"location": "Saint Paul, Minnesota, US",
"name": "James Keuning",
"permalink": "/jmkeuning",
"username": "jmkeuning"
},
"uploader_id": "12121",
"year": 2012
},
{
"attribution": "bee by Rosie Hardwick from Noun Project",
"attribution_preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/attribution/9437-600.png",
"collections": [],
"date_uploaded": "2012-12-30",
"id": "9437",
"is_active": "1",
"is_explicit": "0",
"license_description": "creative-commons-attribution",
"nounji_free": "0",
"permalink": "/term/bee/9437",
"preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/png/9437-200.png",
"preview_url_42": "https://d30y9cdsu7xlg0.cloudfront.net/png/9437-42.png",
"preview_url_84": "https://d30y9cdsu7xlg0.cloudfront.net/png/9437-84.png",
"sponsor": {},
"sponsor_campaign_link": null,
"sponsor_id": "",
"tags": [
{
"id": 1584,
"slug": "bee"
},
{
"id": 244,
"slug": "bug"
},
{
"id": 666,
"slug": "insect"
}
],
"term": "bee",
"term_id": 1584,
"term_slug": "bee",
"uploader": {
"location": "Kingston upon Thames, England, GB",
"name": "Rosie Hardwick",
"permalink": "/rosie_h",
"username": "rosie_h"
},
"uploader_id": "21770",
"year": 2012
},
{
"attribution": "bee by Rémy Médard from Noun Project",
"attribution_preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/attribution/10312-600.png",
"collections": [],
"date_uploaded": "2013-01-22",
"id": "10312",
"is_active": "1",
"is_explicit": "0",
"license_description": "creative-commons-attribution",
"nounji_free": "0",
"permalink": "/term/bee/10312",
"preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/png/10312-200.png",
"preview_url_42": "https://d30y9cdsu7xlg0.cloudfront.net/png/10312-42.png",
"preview_url_84": "https://d30y9cdsu7xlg0.cloudfront.net/png/10312-84.png",
"sponsor": {},
"sponsor_campaign_link": null,
"sponsor_id": "",
"tags": [
{
"id": 1584,
"slug": "bee"
},
{
"id": 666,
"slug": "insect"
},
{
"id": 2218,
"slug": "sting"
}
],
"term": "bee",
"term_id": 1584,
"term_slug": "bee",
"uploader": {
"location": "Paris, France, FR",
"name": "Rémy Médard",
"permalink": "/catalarem",
"username": "catalarem"
},
"uploader_id": "5386",
"year": 2011
},
{
"attribution": "bee by Shirley Wu from Noun Project",
"attribution_preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/attribution/12089-600.png",
"collections": [],
"date_uploaded": "2013-02-23",
"id": "12089",
"is_active": "1",
"is_explicit": "0",
"license_description": "creative-commons-attribution",
"nounji_free": "0",
"permalink": "/term/bee/12089",
"preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/png/12089-200.png",
"preview_url_42": "https://d30y9cdsu7xlg0.cloudfront.net/png/12089-42.png",
"preview_url_84": "https://d30y9cdsu7xlg0.cloudfront.net/png/12089-84.png",
"sponsor": {},
"sponsor_campaign_link": null,
"sponsor_id": "",
"tags": [
{
"id": 1584,
"slug": "bee"
},
{
"id": 244,
"slug": "bug"
},
{
"id": 11698,
"slug": "hornet"
},
{
"id": 666,
"slug": "insect"
},
{
"id": 11699,
"slug": "wasp"
}
],
"term": "bee",
"term_id": 1584,
"term_slug": "bee",
"uploader": {
"location": "San Francisco, CA, US",
"name": "Shirley Wu",
"permalink": "/shirleyy_wu",
"username": "shirleyy_wu"
},
"uploader_id": "37568",
"year": 2013
},
{
"attribution": "bee by iconoci from Noun Project",
"attribution_preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/attribution/13031-600.png",
"collections": [],
"date_uploaded": "2013-03-08",
"id": "13031",
"is_active": "1",
"is_explicit": "0",
"license_description": "creative-commons-attribution",
"nounji_free": "0",
"permalink": "/term/bee/13031",
"preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/png/13031-200.png",
"preview_url_42": "https://d30y9cdsu7xlg0.cloudfront.net/png/13031-42.png",
"preview_url_84": "https://d30y9cdsu7xlg0.cloudfront.net/png/13031-84.png",
"sponsor": {},
"sponsor_campaign_link": null,
"sponsor_id": "",
"tags": [
{
"id": 1584,
"slug": "bee"
},
{
"id": 244,
"slug": "bug"
},
{
"id": 12283,
"slug": "bumble"
},
{
"id": 1656,
"slug": "fly"
},
{
"id": 666,
"slug": "insect"
},
{
"id": 2860,
"slug": "wings"
}
],
"term": "bee",
"term_id": 1584,
"term_slug": "bee",
"uploader": {
"location": "GB",
"name": "iconoci",
"permalink": "/iconoci",
"username": "iconoci"
},
"uploader_id": "21070",
"year": 2013
},
{
"attribution": "bee by David Lopez from Noun Project",
"attribution_preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/attribution/15518-600.png",
"collections": [],
"date_uploaded": "2013-04-17",
"id": "15518",
"is_active": "1",
"is_explicit": "0",
"license_description": "creative-commons-attribution",
"nounji_free": "0",
"permalink": "/term/bee/15518",
"preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/png/15518-200.png",
"preview_url_42": "https://d30y9cdsu7xlg0.cloudfront.net/png/15518-42.png",
"preview_url_84": "https://d30y9cdsu7xlg0.cloudfront.net/png/15518-84.png",
"sponsor": {},
"sponsor_campaign_link": null,
"sponsor_id": "",
"tags": [
{
"id": 1584,
"slug": "bee"
},
{
"id": 244,
"slug": "bug"
},
{
"id": 1656,
"slug": "fly"
},
{
"id": 666,
"slug": "insect"
},
{
"id": 2860,
"slug": "wings"
}
],
"term": "bee",
"term_id": 1584,
"term_slug": "bee",
"uploader": {
"location": "Paris, Île-de-France, FR",
"name": "David Lopez",
"permalink": "/davidlopez.fr",
"username": "davidlopez.fr"
},
"uploader_id": "24959",
"year": 2012
},
{
"attribution": "bee by Cassie McKown from Noun Project",
"attribution_preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/attribution/16937-600.png",
"collections": [],
"date_uploaded": "2013-05-20",
"id": "16937",
"is_active": "1",
"is_explicit": "0",
"license_description": "creative-commons-attribution",
"nounji_free": "0",
"permalink": "/term/bee/16937",
"preview_url": "https://d30y9cdsu7xlg0.cloudfront.net/png/16937-200.png",
"preview_url_42": "https://d30y9cdsu7xlg0.cloudfront.net/png/16937-42.png",
"preview_url_84": "https://d30y9cdsu7xlg0.cloudfront.net/png/16937-84.png",
"sponsor": {},
"sponsor_campaign_link": null,
"sponsor_id": "",
"tags": [
{
"id": 1584,
"slug": "bee"
},
{
"id": 499,
"slug": "animal"
},
{
"id": 244,
"slug": "bug"
},
{
"id": 1656,
"slug": "fly"
},
{
"id": 666,
"slug": "insect"
}
],
"term": "bee",
"term_id": 1584,
"term_slug": "bee",
"uploader": {
"location": "Falls Church, VA, US",
"name": "Cassie McKown",
"permalink": "/mckowncr",
"username": "mckowncr"
},
"uploader_id": "86458",
"year": 2013
}
]
}
var icon = iconData.icon
var icons = iconData.icons
var id = icon.id
var thumbnail = icon.preview_url
var license = icon.license_description
var title = icon.term
var author = icon.uploader.name
console.log(author)
for(i = 0; i < 8; i++){
document.getElementById(( "id-" + creditCounter )).textContent = id
document.getElementById(( "thumbnail-" + creditCounter )).setAttribute("href", thumbnail)
document.getElementById(( "title-" + creditCounter )).textContent = title
document.getElementById(( "author-" + creditCounter )).textContent = author
document.getElementById('text').value = ""
var id = icons[i].id
var thumbnail = icons[i].preview_url
var license = icons[i].license_description
var title = icons[i].term
var author = icons[i].uploader.name
console.log(author)
creditCounter ++
document.getElementById(( "id-" + i )).textContent = id
document.getElementById(( "thumbnail-" + i )).setAttribute("href", thumbnail)
document.getElementById(( "title-" + i )).textContent = title
document.getElementById(( "author-" + i )).textContent = author
}
})
}
......@@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<title>Noun-project-SVG</title>
<title>SEARCH</title>
<link rel="stylesheet" href="./styles.css">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
......
......@@ -123,6 +123,6 @@ svg line {
border: none;
}
button, form, input {
button, form, input, label {
display: none;
}
function getUserIcons() {
let term = document.getElementById('text').value;