Commit 2431ed7933f1ccc58cca2210b879873dcd59e924
- Diff rendering mode:
- inline
- side by side
setup.py
(2 / 1)
  | |||
7 | 7 | ||
8 | 8 | requires = [ | |
9 | 9 | 'Flask', | |
10 | 'requests' | ||
10 | 'requests', | ||
11 | 'lxml' | ||
11 | 12 | ] | |
12 | 13 | ||
13 | 14 | setup(name='Swtr', |
swtr/server.py
(61 / 26)
  | |||
3 | 3 | import flask | |
4 | 4 | from flask import session | |
5 | 5 | import lxml.html | |
6 | import config | ||
7 | 6 | import requests | |
8 | 7 | import json | |
9 | 8 | import StringIO | |
10 | 9 | import imghdr | |
11 | 10 | from datetime import datetime, timedelta | |
12 | 11 | ||
12 | import config | ||
13 | 13 | ||
14 | |||
14 | 15 | app = flask.Flask(__name__) | |
15 | 16 | app.config['SECRET_KEY'] = config.secret_key | |
16 | 17 | ||
… | … | ||
81 | 81 | url=flask.request.args.get('where')) | |
82 | 82 | ||
83 | 83 | ||
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']) | ||
88 | def 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']) | ||
121 | def 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 | |||
84 | 131 | @app.route('/annotate', methods=['GET']) | |
85 | 132 | def annotate(): | |
86 | print flask.request.args['where'] | ||
87 | 133 | # img = urllib2.urlopen(flask.request.args['where']).read() | |
88 | 134 | request = requests.get(flask.request.args['where']) | |
89 | 135 | content = request.text | |
… | … | ||
188 | 188 | "js/lib/backbone-1.0.0.min.js")) | |
189 | 189 | backboneJS.set("type", "text/javascript") | |
190 | 190 | ||
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 | |||
191 | 203 | if 'auth_tok' in session: | |
192 | 204 | auth_tok = session['auth_tok'] | |
193 | 205 | else: | |
… | … | ||
261 | 261 | refresh_token=auth_tok['refresh_token'], | |
262 | 262 | config=config, | |
263 | 263 | url=flask.request.args.get('where')) | |
264 | |||
265 | |||
266 | @app.route("/search") | ||
267 | def 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()) | ||
288 | 264 | ||
289 | 265 | ||
290 | 266 | # if the app is run directly from command-line |
swtr/static/css/swtmaker.css
(29 / 6)
  | |||
10 | 10 | min-height: 35px; | |
11 | 11 | } | |
12 | 12 | ||
13 | |||
14 | 13 | #swt-maker { | |
15 | 14 | } | |
15 | |||
16 | #control-panel { | ||
17 | margin-top: 10px; | ||
18 | margin-left: 0; | ||
19 | } | ||
20 | |||
16 | 21 | #signinview { | |
17 | 22 | margin 0 20px; | |
18 | 23 | padding: 10px; | |
19 | 24 | text-align: center; | |
20 | 25 | } | |
21 | #img-annotation-wrapper { | ||
26 | |||
27 | #img-annotation-wrapper, #ocd-results { | ||
22 | 28 | margin: 30px auto 0 auto; | |
23 | 29 | } | |
30 | |||
24 | 31 | #annotatable-img { | |
25 | 32 | margin: 0 auto; | |
26 | 33 | max-width: 1125px; | |
27 | 34 | } | |
35 | |||
28 | 36 | #sweet-list-wrapper { | |
29 | 37 | width: 500px; | |
30 | 38 | min-height: 300px; | |
… | … | ||
46 | 46 | padding: 30px; | |
47 | 47 | display: none; | |
48 | 48 | } | |
49 | |||
49 | 50 | #sweet-list { | |
50 | 51 | margin-bottom: 10px; | |
51 | 52 | } | |
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; | ||
56 | 60 | } | |
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 | |||
57 | 71 | #sweet { | |
58 | 72 | display: none; | |
59 | 73 | } | |
74 | |||
60 | 75 | #helpview { | |
61 | 76 | font-weight: bold; | |
62 | 77 | font-size: 1.3em; |
swtr/static/js/app.js
(1 / 0)
  | |||
130 | 130 | this.annotator = new Annotator(document.body); | |
131 | 131 | swtr.anno = this.annotator; | |
132 | 132 | this.annotator.addPlugin("Tags"); | |
133 | this.annotator.addPlugin("AnnotoriousImagePlugin"); | ||
133 | 134 | this.listenTo(this.collection, "add", this.loadAnno); | |
134 | 135 | this.annotator.subscribe("annotationCreated", this.storeAnno); | |
135 | 136 | }, |
swtr/static/js/swtmaker.js
(119 / 13)
  | |||
233 | 233 | cleanUp: function() { | |
234 | 234 | //console.log('cleaning up'); | |
235 | 235 | $(this.el).hide(); | |
236 | if(swtr.appView.source === 'ocd') { | ||
237 | $('#img-annotation-wrapper').hide(); | ||
238 | $('#ocd-results').show(); | ||
239 | } | ||
236 | 240 | } | |
237 | 241 | }); | |
238 | 242 | ||
239 | 243 | var AppView = Backbone.View.extend({ | |
240 | 244 | el: $('#swt-maker'), | |
241 | 245 | events: { | |
242 | 'click #img-url-load': 'setImage', | ||
243 | 'click #img-url-submit': 'setImage', | ||
246 | 'click #user-input-submit': 'submitUserInput', | ||
244 | 247 | 'click #sweet': 'sweet', | |
245 | 248 | 'click #sign-in': 'signIn', | |
246 | 249 | 'click #setbox': 'showHide', | |
247 | 'change #custom-dropdown ': 'getFormValue' | ||
250 | 'change #custom-dropdown ': 'getFormValue', | ||
251 | 'click #ocd-source': 'sourceChanged' | ||
248 | 252 | //'mouseup .annotorious-editor-button-save': 'addnew_anno' | |
249 | 253 | }, | |
250 | 254 | initialize: function() { | |
251 | 255 | // initialize components | |
256 | this.source = 'none'; | ||
252 | 257 | this.helpview = new HelpView(); | |
253 | 258 | this.sweetsview = new SweetsView({collection: swtr.sweets}); | |
254 | 259 | ||
… | … | ||
282 | 282 | this.imgURL = this.$img.attr('src'); | |
283 | 283 | if(this.imgURL) { | |
284 | 284 | this.initImageAnno(); | |
285 | $('#img-url-input').val(this.imgURL); | ||
285 | $('#user-input').val(this.imgURL); | ||
286 | 286 | } | |
287 | 287 | else { | |
288 | 288 | this.helpview.step(1); | |
… | … | ||
297 | 297 | scopes: 'email,sweet' | |
298 | 298 | }); | |
299 | 299 | }, | |
300 | setImage: function(event) { | ||
300 | submitUserInput: function(event) { | ||
301 | 301 | 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 | ||
303 | 317 | if(url.match(/.jpg|.jpeg|.png|.gif|.bmp|.svg/)) { | |
304 | 318 | ||
305 | this.imgURL = $('#img-url-input').val(); | ||
306 | if(!this.imgURL) { | ||
307 | return false; | ||
308 | } | ||
319 | this.imgURL = url; | ||
320 | |||
309 | 321 | if(this.$img.attr('src') === this.imgURL) { | |
310 | 322 | return; | |
311 | 323 | } | |
… | … | ||
328 | 328 | this.$img.attr('src', this.imgURL); | |
329 | 329 | return false; | |
330 | 330 | } | |
331 | // else load text annotation | ||
331 | 332 | else { | |
332 | if(!url) { | ||
333 | return false; | ||
334 | } | ||
335 | 333 | window.location.href = '/annotate?where=' + url; | |
336 | 334 | } | |
337 | 335 | }, | |
… | … | ||
552 | 552 | userLoggedOut: function() { | |
553 | 553 | swtr.who = 'Guest'; | |
554 | 554 | $('#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 | }); | ||
555 | 599 | } | |
556 | 600 | }); | |
557 | 601 | ||
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 | |||
558 | 647 | var HelpView = Backbone.View.extend({ | |
559 | 648 | el: $('#helpview'), | |
560 | 649 | events: { | |
… | … | ||
678 | 678 | case 9: text = 'You have to be <i>signed in</i> to sweet store to post sweets'; | |
679 | 679 | break; | |
680 | 680 | 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>'; | ||
681 | 683 | break; | |
682 | 684 | } | |
683 | 685 | $(this.el).html(text); |
swtr/templates/index.html
(36 / 23)
  | |||
3 | 3 | <head> | |
4 | 4 | <meta charset="utf-8"> | |
5 | 5 | <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> | ||
7 | 7 | ||
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"> | ||
9 | 9 | <link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet"> | |
10 | 10 | <link href="{{ url_for('static', filename='css/bootstrap-theme.min.css') }}" rel="stylesheet"> | |
11 | 11 | <link href="{{ url_for('static', filename='css/annotorious.css') }}" rel="stylesheet"> | |
12 | 12 | <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"> | ||
14 | 14 | </head> | |
15 | 15 | ||
16 | 16 | <body> | |
… | … | ||
36 | 36 | <div class="col-md-8"> | |
37 | 37 | <form class="form-inline" role="form"> | |
38 | 38 | <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> | ||
40 | 40 | <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"> | ||
42 | 42 | </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> | ||
44 | 44 | </form> | |
45 | 45 | </div> | |
46 | 46 | <div class="col-md-4"> | |
47 | 47 | </div> | |
48 | 48 | </div> <!-- end second row --> | |
49 | 49 | <!-- 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> | ||
54 | 67 | </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> | ||
58 | 69 | </div> <!-- end third row -- > | |
59 | 70 | <!-- fourth row: the image annotation window --> | |
60 | 71 | <div class="row"> | |
… | … | ||
76 | 76 | <img src="" id="annotatable-img" class="img-responsive"> | |
77 | 77 | {% endif %} | |
78 | 78 | </div> | |
79 | <div id="ocd-results"></div> | ||
79 | 80 | </div> <!-- end fourth row --> | |
80 | 81 | ||
81 | 82 | <div id="sweet-list-wrapper"> | |
… | … | ||
134 | 134 | <option value="link">Link</option> | |
135 | 135 | </select> | |
136 | 136 | </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--> | ||
147 | 137 | <script type="text/template" id="popup-template"> | |
148 | 138 | <span class="annotorious-popup-text"> | |
149 | 139 | <ul class="custom-style-popup"> | |
… | … | ||
143 | 143 | <li class="custom-popup-line"><span>Related Links: </span><%= Links %></li> | |
144 | 144 | </ul> | |
145 | 145 | </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> | ||
146 | 157 | </script> | |
147 | 158 | </body> | |
148 | 159 | </html> |