Commit 2431ed7933f1ccc58cca2210b879873dcd59e924

Initial implementation of external API source (Open Cuultur Data)

  - An intial version to search in the Open Cuultur Data APIs and annotate
    images from search results. Right now fetching, and displaying of search
results is implemented along with first attempt at annotating search result
images.
setup.py
(2 / 1)
  
77
88requires = [
99 'Flask',
10 'requests'
10 'requests',
11 'lxml'
1112]
1213
1314setup(name='Swtr',
  
33import flask
44from flask import session
55import lxml.html
6import config
76import requests
87import json
98import StringIO
109import imghdr
1110from datetime import datetime, timedelta
1211
12import config
1313
14
1415app = flask.Flask(__name__)
1516app.config['SECRET_KEY'] = config.secret_key
1617
8181 url=flask.request.args.get('where'))
8282
8383
84# endpoint to search the Open Cuultur Data APIs
85# takes in `query`, `size`, and `from` parameters in query string
86# returns a JSON response
87@app.route('/search/ocd', methods=['GET'])
88def search_ocd():
89 query = flask.request.args.get('query')
90 #collection = flask.request.args.get('collection')
91 size = flask.request.args.get('size') or 10
92 offset = flask.request.args.get('from') or 0
93
94 # if query parameter is not passed, return bad request.
95 if not query:
96 flask.abort(400)
97
98 payload = {
99 'query': query,
100 'facets': {
101 'collection': {},
102 'date': {'interval': 'day'}
103 },
104 'filters': {
105 'media_content_type': {'terms': ['image/jpeg', 'image/png']}
106 },
107 'size': size,
108 'from': offset
109 }
110 resp = requests.post('http://api.opencultuurdata.nl/v0/search',
111 data=json.dumps(payload))
112
113 response = flask.make_response()
114 response.data = json.dumps(resp.json())
115 response.headers['Content-type'] = 'application/json'
116 return response
117
118
119# resolve OCD Media URLs: http://docs.opencultuurdata.nl/user/api.html#resolver
120@app.route('/resolve-ocd-media/<media_hash>', methods=['GET'])
121def resolve_ocd_media_urls(media_hash):
122
123 resp = requests.get('http://api.opencultuurdata.nl/v0/resolve/' +
124 media_hash)
125
126 response = flask.make_response()
127 response.data = resp.url
128 return response
129
130
84131@app.route('/annotate', methods=['GET'])
85132def annotate():
86 print flask.request.args['where']
87133 # img = urllib2.urlopen(flask.request.args['where']).read()
88134 request = requests.get(flask.request.args['where'])
89135 content = request.text
188188 "js/lib/backbone-1.0.0.min.js"))
189189 backboneJS.set("type", "text/javascript")
190190
191 # annotorious plugin
192 annotoriousCSS = root.makeelement('link')
193 root.body.append(annotoriousCSS)
194 annotoriousCSS.set("href", flask.url_for('static',
195 filename='css/annotorious.css'))
196 annotoriousCSS.set('rel', 'stylesheet')
197
198 annotoriousJS = root.makeelement('script')
199 root.body.append(annotoriousJS)
200 annotoriousJS.set('src', flask.url_for('static',
201 filename='js/annotorious.okfn.0.3.js'))
202
191203 if 'auth_tok' in session:
192204 auth_tok = session['auth_tok']
193205 else:
261261 refresh_token=auth_tok['refresh_token'],
262262 config=config,
263263 url=flask.request.args.get('where'))
264
265
266@app.route("/search")
267def search():
268 if 'size' not in flask.request.args:
269 size = 10
270 else:
271 size = flask.request.args['size']
272 if 'from' not in flask.request.args:
273 fr = 0
274 else:
275 fr = flask.request.args['from']
276
277 results = requests.post("http://api.opencultuurdata.nl/v0/search",
278 data=json.dumps({
279 "query": flask.request.args['term'],
280 "facets": {"collection": {},
281 "date": {"interval": "day"}},
282 "filters": {"media_content_type":
283 {"terms": ["image/jpeg",
284 "image/png"]}},
285 "size": size,
286 "from": fr}))
287 return flask.jsonify(results.json())
288264
289265
290266# if the app is run directly from command-line
  
1010 min-height: 35px;
1111}
1212
13
1413#swt-maker {
1514}
15
16#control-panel {
17 margin-top: 10px;
18 margin-left: 0;
19}
20
1621#signinview {
1722 margin 0 20px;
1823 padding: 10px;
1924 text-align: center;
2025}
21#img-annotation-wrapper {
26
27#img-annotation-wrapper, #ocd-results {
2228 margin: 30px auto 0 auto;
2329}
30
2431#annotatable-img {
2532 margin: 0 auto;
2633 max-width: 1125px;
2734}
35
2836#sweet-list-wrapper {
2937 width: 500px;
3038 min-height: 300px;
4646 padding: 30px;
4747 display: none;
4848}
49
4950#sweet-list {
5051 margin-bottom: 10px;
5152}
52.btn-grp {
53 position: relative;
54 bottom: -10%;
55 right: -10%;
53
54.ocd-item {
55 padding-bottom: 4px;
56 border: 1px solid #ddd;
57 border-radius: 4px;
58 margin-bottom: 20px;
59 text-align: center;
5660}
61
62.thumb-image {
63 background-position: center center;
64 background-repeat: no-repeat;
65 background-size: cover;
66 display: block;
67 height: 400px;
68 margin: 0 auto;
69}
70
5771#sweet {
5872 display: none;
5973}
74
6075#helpview {
6176 font-weight: bold;
6277 font-size: 1.3em;
  
130130 this.annotator = new Annotator(document.body);
131131 swtr.anno = this.annotator;
132132 this.annotator.addPlugin("Tags");
133 this.annotator.addPlugin("AnnotoriousImagePlugin");
133134 this.listenTo(this.collection, "add", this.loadAnno);
134135 this.annotator.subscribe("annotationCreated", this.storeAnno);
135136 },
  
233233 cleanUp: function() {
234234 //console.log('cleaning up');
235235 $(this.el).hide();
236 if(swtr.appView.source === 'ocd') {
237 $('#img-annotation-wrapper').hide();
238 $('#ocd-results').show();
239 }
236240 }
237241 });
238242
239243 var AppView = Backbone.View.extend({
240244 el: $('#swt-maker'),
241245 events: {
242 'click #img-url-load': 'setImage',
243 'click #img-url-submit': 'setImage',
246 'click #user-input-submit': 'submitUserInput',
244247 'click #sweet': 'sweet',
245248 'click #sign-in': 'signIn',
246249 'click #setbox': 'showHide',
247 'change #custom-dropdown ': 'getFormValue'
250 'change #custom-dropdown ': 'getFormValue',
251 'click #ocd-source': 'sourceChanged'
248252 //'mouseup .annotorious-editor-button-save': 'addnew_anno'
249253 },
250254 initialize: function() {
251255 // initialize components
256 this.source = 'none';
252257 this.helpview = new HelpView();
253258 this.sweetsview = new SweetsView({collection: swtr.sweets});
254259
282282 this.imgURL = this.$img.attr('src');
283283 if(this.imgURL) {
284284 this.initImageAnno();
285 $('#img-url-input').val(this.imgURL);
285 $('#user-input').val(this.imgURL);
286286 }
287287 else {
288288 this.helpview.step(1);
297297 scopes: 'email,sweet'
298298 });
299299 },
300 setImage: function(event) {
300 submitUserInput: function(event) {
301301 event.preventDefault();
302 var url = $('#img-url-input').val();
302 var input = $('#user-input').val();
303 if(this.source === 'ocd') {
304 this.loadOCDSearch(input);
305 }
306 else if (this.source === 'none') {
307 this.loadURL(input);
308 }
309 },
310 loadURL: function(url) {
311 $('#ocd-results').hide();
312 $('#img-annotation-wrapper').show();
313 if(!url) {
314 return false;
315 }
316 // if image url then load the image annotation
303317 if(url.match(/.jpg|.jpeg|.png|.gif|.bmp|.svg/)) {
304318
305 this.imgURL = $('#img-url-input').val();
306 if(!this.imgURL) {
307 return false;
308 }
319 this.imgURL = url;
320
309321 if(this.$img.attr('src') === this.imgURL) {
310322 return;
311323 }
328328 this.$img.attr('src', this.imgURL);
329329 return false;
330330 }
331 // else load text annotation
331332 else {
332 if(!url) {
333 return false;
334 }
335333 window.location.href = '/annotate?where=' + url;
336334 }
337335 },
552552 userLoggedOut: function() {
553553 swtr.who = 'Guest';
554554 $('#signinview').html('Logged out');
555 },
556 changeURLInputPlaceholder: function(source) {
557 switch (source) {
558 case 'ocd' : $('#user-input').attr('placeholder', 'Enter search query');
559 break;
560 case 'none' : $('#user-input').attr('placeholder', 'Enter URL of image or web page');
561 break;
562 }
563 },
564 // function to change the source in the application and update the UI
565 changeSource: function(source) {
566 switch (source) {
567 case 'ocd' : this.source = 'ocd';
568 this.helpview.step(11);
569 this.changeURLInputPlaceholder('ocd');
570 break;
571 case 'none' : this.source = 'none';
572 this.helpview.step(1);
573 this.changeURLInputPlaceholder('none');
574 break;
575 }
576 },
577 // event handler to capture control panel UI change of source
578 sourceChanged: function(event) {
579 if($('#ocd-source').is(':checked')) {
580 this.changeSource('ocd');
581 }
582 else {
583 this.changeSource('none');
584 }
585 },
586 loadOCDSearch: function(input) {
587 var self = this;
588 $('#img-annotation-wrapper').hide();
589 $('#ocd-results').show();
590 $('#ocd-results').append('Loading..');
591 $.ajax({
592 type: 'GET',
593 url: '/search/ocd',
594 data: {query: input},
595 success: function(data) {
596 self.ocdView = new OCDView({model: data.hits.hits});
597 }
598 });
555599 }
556600 });
557601
602 var OCDView = Backbone.View.extend({
603 el: $('#ocd-results'),
604 events: {
605 'click .ocd-item a': 'onClickImg'
606 },
607 initialize: function() {
608 this.item_template = _.template($('#ocd-item-template').html());
609 this.render();
610 },
611 render: function() {
612 var $row_el;
613 this.$el.html('');
614 _.each(this.model, function(item, idx) {
615 if(idx % 3 === 0) {
616 $row_el = $('<div class="row"></div>');
617 this.$el.append($row_el);
618 }
619 $row_el.append(this.item_template({
620 title: item._source.title,
621 media_url: item._source.media_urls[0].url,
622 authors: item._source.authors
623 }));
624 }, this);
625 this.resolve();
626 },
627 // resolve the OCD media URLs
628 resolve: function() {
629 var self = this;
630 $('.ocd-item').each(function(idx, elem) {
631 var temp_arr = self.model[idx]._source.media_urls[0].url.split('/');
632 var media_hash = temp_arr[temp_arr.length - 1];
633 $.get('/resolve-ocd-media/'+ media_hash, function(resp) {
634 $(elem).find('img').attr('src', resp);
635 });
636 });
637 },
638 onClickImg: function(event) {
639 event.preventDefault();
640 // TODO: init the image anno
641 var url = $(event.currentTarget).find('img').attr('src');
642 swtr.appView.loadURL(url);
643 return false;
644 }
645 });
646
558647 var HelpView = Backbone.View.extend({
559648 el: $('#helpview'),
560649 events: {
678678 case 9: text = 'You have to be <i>signed in</i> to sweet store to post sweets';
679679 break;
680680 case 10: text = 'Oops! Something went wrong. We couldn\'t publish the sweets. Try again.'
681 break;
682 case 11: text = 'Search in <a href="http://www.opencultuurdata.nl/">Open Cuultur Data API</a>';
681683 break;
682684 }
683685 $(this.el).html(text);
  
33 <head>
44 <meta charset="utf-8">
55 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>swtr - the default sweet web app</title>
6 <title> swtr - the canonical sweet web app </title>
77
8 <meta name="description" content="Sweet Maker, Social, Semantic, Web, Decentralized, Makes Sweet, Sweet Swagger">
8 <meta name="description" content="Sweet Maker, Social, Semantic, Web, Decentralized, Makes Sweet, Sweet Swagger, Swtr, swtr">
99 <link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
1010 <link href="{{ url_for('static', filename='css/bootstrap-theme.min.css') }}" rel="stylesheet">
1111 <link href="{{ url_for('static', filename='css/annotorious.css') }}" rel="stylesheet">
1212 <link href="{{ url_for('static', filename='css/swtmaker.css') }}" rel="stylesheet">
13 <link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap-tags.css') }}" type="text/css" media="screen" />
13 <link href="{{ url_for('static', filename='css/bootstrap-tags.css') }}" rel="stylesheet">
1414 </head>
1515
1616 <body>
3636 <div class="col-md-8">
3737 <form class="form-inline" role="form">
3838 <div class="form-group col-md-11">
39 <label class="sr-only" for="img-url-input">Enter URL of the image</label>
39 <label class="sr-only" for="user-input">Enter URL of an image or web page</label>
4040 <input class="form-control" type="text"
41 placeholder="Enter URL of the image or web page" id="img-url-input">
41 placeholder="Enter URL of an image or web page" id="user-input">
4242 </div>
43 <button type="submit" class="btn btn-primary" id="img-url-load">Load</button>
43 <button type="submit" class="btn btn-primary" id="user-input-submit">Load</button>
4444 </form>
4545 </div>
4646 <div class="col-md-4">
4747 </div>
4848 </div> <!-- end second row -->
4949 <!-- third row - control panel? -->
50 <div class="row">
51 <div class="col-md-3">
52 <input id="setbox" type="checkbox"/>
53 <label id="setcontrol" for="setbox">Show annotated areas</label>
50 <div class="row" id="control-panel">
51 <div class="col-md-8">
52 <form class="form-inline" role="form">
53 <div class="form-group">
54 <label class="checkbox-inline">
55 <input type="checkbox" id="setbox"> Show annotated areas
56 </label>
57 </div>
58 <div class="form-group">
59 <label class="checkbox-inline">
60 <input type="checkbox" id="ocd-source"> Search in Open Cuultur Data
61 </label>
62 </div>
63 <div class="form-group">
64 <button class="btn btn-default" id="sweet">Sweet</button>
65 </div>
66 </form>
5467 </div>
55 <div class="col-md-1">
56 <button class="btn btn-default" id="sweet">Sweet</button>
57 </div>
68 <div class="col-md-4"></div>
5869 </div> <!-- end third row -- >
5970 <!-- fourth row: the image annotation window -->
6071 <div class="row">
7676 <img src="" id="annotatable-img" class="img-responsive">
7777 {% endif %}
7878 </div>
79 <div id="ocd-results"></div>
7980 </div> <!-- end fourth row -->
8081
8182 <div id="sweet-list-wrapper">
134134 <option value="link">Link</option>
135135 </select>
136136 </script>
137 <!-- script type="text/template" id="dropdown-template">
138 <form class="form-control" id="custom-dropdown">
139 <textarea class="annotorious-editor-text goog-textarea"
140 placeholder="Add Label" id="label"></textarea>
141 <textarea class="annotorious-editor-text goog-textarea"
142 placeholder="Add Tags" id="tags"></textarea>
143 <textarea class="annotorious-editor-text goog-textarea"
144 placeholder="Add Links" id="links"></textarea>
145 </form>
146 </script-->
147137 <script type="text/template" id="popup-template">
148138 <span class="annotorious-popup-text">
149139 <ul class="custom-style-popup">
143143 <li class="custom-popup-line"><span>Related Links: </span><%= Links %></li>
144144 </ul>
145145 </span>
146 </script>
147 <script type="text/template" id="ocd-item-template">
148 <div class="col-sm-6 col-md-4 ocd-item">
149 <h4> <%= title %> </h4>
150 <div class="media">
151 <a href="<%= media_url %>">
152 <img src="" class="responsive thumb-image">
153 </a>
154 </div>
155 <div>Authors: <%= authors %> </div>
156 </div>
146157 </script>
147158 </body>
148159</html>