Web · Wiki · Activities · Blog · Lists · Chat · Meeting · Bugs · Git · Translate · Archive · People · Donate
1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2012, Sebastian Silva 
3
#
4
# This program is free software: you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17
import logging
18
import os
19
import sys
20
import signal
21
import locale
22
from math import ceil
23
from string import join,split
24
25
import gevent
26
from urllib2 import urlopen
27
28
from flask import flash,Flask,request,url_for,abort,jsonify,Response,\
29
                redirect,session,render_template,render_template_string,g,\
30
                send_from_directory,Markup
31
from flaskext.babel import Babel, gettext as _, lazy_gettext as __, format_datetime
32
from werkzeug import Headers
33
import simplejson
34
35
from sugar_network import Client, sugar, ServerError
36
37
_BUFFER_SIZE = 1024 * 10
38
39
""" Initialization of local app """
40
app = Flask(__name__)
41
app.secret_key="ilovesugar" #necessary to initialize sessions
42
babel = Babel(app)
43
app.config['BABEL_DEFAULT_LOCALE'] = 'en'
44
app.config['BABEL_DEFAULT_TIMEZONE'] = 'America/Lima'
45
46
from cursors import *
47
mounts = client.mounts()
48
49
@babel.localeselector
50
def get_locale():
51
    # try to guess the language from the user accept
52
    # header the browser transmits.
53
    lang = request.accept_languages.best_match(['es', 'en'])
54
    if not lang:
55
        # otherwise we're probably embedded, get from env
56
        lang = locale.getdefaultlocale()[0].split('_')[0]
57
    return lang
58
59
@app.template_filter('special_str')
60
def special_str(arg):
61
    if isinstance(arg, basestring): # Python 3: isinstance(arg, str)
62
        return arg
63
    else:
64
        return(arg[0])
65
66
@app.template_filter('timedelta')
67
def timedelta(mtime):
68
    return format_datetime(mtime, _('MMMM d, yyyy')) 
69
70
def get_colors():
71
    return sugar.color().split(',')
72
73
def mount_event(e):
74
    if e.get('event')=='mount':
75
        mounts.append({'name':e['name'],
76
                         'mountpoint':e['mountpoint']})
77
    elif e.get('event')=='unmount':
78
        mounts[:] = [m for m in mounts if m.get('mountpoint') != e.get('mountpoint')]
79
80
Client.connect(mount_event)
81
82
@app.context_processor
83
def inject_vars():
84
    # Here we can inject variables into every template call
85
    stroke,fill = get_colors()
86
    connected = session.get('connected')
87
    #TODO: Mark which mount is active
88
    if connected==True:
89
        mountpoint="/"
90
    elif connected==False:
91
        mountpoint="~"
92
    else:
93
        mountpoint=connected
94
    kwvar = {
95
        'userid': sugar.uid(),
96
        'mounts': mounts,
97
        'current_mount': mountpoint
98
        }
99
    return dict(stroke=stroke, fill=fill, **kwvar)
100
101
@app.before_request
102
def before_request():
103
    if 'connected' not in session:
104
        session['connected'] = client.connected
105
    if session['connected']==False:
106
        g.client = offline_client
107
        g.Contexts = offline_Contexts
108
        g.Activities= offline_Activities
109
        g.Projects = offline_Projects
110
        g.autocomplete_Contexts = autocomplete_offline_Contexts
111
    elif session['connected']==True:
112
        g.client = client
113
        g.Contexts = Contexts
114
        g.Activities= Activities
115
        g.Projects = Projects
116
        g.autocomplete_Contexts = autocomplete_Contexts
117
    else:
118
        try:
119
            cm = g.custom_mount 
120
        except AttributeError:
121
            g.custom_mount = Custom_Mount(session['connected'])
122
            cm = g.custom_mount 
123
        g.client = cm.client
124
        g.Contexts = cm.Contexts
125
        g.Activities= cm.Activities
126
        g.Projects = cm.Projects
127
        g.autocomplete_Contexts = cm.autocomplete_Contexts
128
129
130
@app.route('/')
131
def home():
132
    return redirect(url_for('context_grid'))
133
134
@app.route('/_toggle_connect')
135
def toggle_connect():
136
    if session.get('connected'):
137
        session['connected'] = False
138
    else:
139
        session['connected'] = True 
140
141
    # Palette mount selector
142
    if request.args.get('select')=='~':
143
        session['connected'] = False
144
    elif request.args.get('select')=='/':
145
        session['connected'] = True 
146
    elif request.args.get('select'):
147
        session['connected'] = request.args.get('select')
148
        g.custom_mount = Custom_Mount(session['connected'])
149
        
150
    return redirect(url_for('context_grid'))
151
152
@app.route('/context/icon/<context_guid>')
153
def gen_icon(context_guid):
154
    type=request.args.get('type') or session['current_type']
155
    offset=int(request.args.get('cursor_offset') or 0)
156
    if offset and type:
157
        if type in ['activity']:
158
            context_cursor = g.Activities
159
        elif type in ['project']:
160
            context_cursor = g.Projects
161
        else:
162
            context_cursor = g.Contexts
163
        context=context_cursor[offset]
164
    else:
165
        context=g.client.Context(context_guid)
166
    def download(context):
167
        with context.get_blob('icon') as icon:
168
            if icon.closed:
169
                icon=app.open_resource('static/icons/document-generic.png')
170
            while True:
171
                chunk = icon.read(_BUFFER_SIZE)
172
                if not chunk:
173
                    break
174
                yield chunk
175
    header = Headers()
176
    header.add("Content-Type", "image/png")
177
    return Response(download(context), headers=header, direct_passthrough=True)
178
179
@app.route('/launch/<context_guid>')
180
def launch(context_guid):
181
    #workaround in 0.3 : non-activity contexts
182
    if context_guid=='sugar-network' or context_guid=='83b8e18cb6b611e1aa6b0016360ee2af':
183
        return redirect('/context/reviews/%s' % context_guid)
184
    try:
185
        client.launch(context_guid)
186
    except ServerError,RuntimeError:
187
        pass
188
    return redirect('/context/reviews/%s' % context_guid)
189
190
@app.route('/new/resource')
191
@app.route('/new/resource/<context_guid>')
192
def case_1(context_guid=None):
193
    if not context_guid:
194
        context_guid=session.get('last_context')
195
    if not context_guid:
196
        return redirect(url_for('context_grid'))
197
198
    offset=int(request.args.get('cursor_offset') or 0)
199
    if offset:
200
        context=Contexts[offset]
201
    else:
202
        context=g.client.Context(context_guid, reply=['title', 'keep', 'keep_impl'])
203
    return render_template('resource-form.html', context=context)
204
205
@app.route('/_report')
206
def report():
207
    return render_template('report-form.html',
208
            context=request.args.get('context') or '',
209
            implementation=request.args.get('implementation') or '',
210
            filename=request.args.get('filename') or '')
211
212
@app.route('/favorites')
213
def favorites():
214
    if session.get('favorites-filter'):
215
        session['favorites-filter']=False
216
    else:
217
        session['favorites-filter']=True
218
    return redirect(url_for('context_grid'))
219
220
@app.route('/query')
221
def autocomplete_context():
222
    query = "title:"+request.args.get('term')
223
    r, total_pages, total, info = paginate(g.autocomplete_Contexts, query)
224
    result = []
225
    for item in r:
226
        result.append( {'value':item['guid'],
227
                        'label':item['title']} )
228
    return simplejson.dumps(result)
229
230
@app.route('/search/<query>')
231
def search(query=None):
232
    return redirect(url_for('context_grid', query=query))
233
234
@app.route('/_tags/<tag>', methods=['DELETE'])
235
def del_tag(tag):
236
    try:
237
        session['tags'].pop(session['tags'].index(tag))
238
        session.modified = True
239
    except:
240
        pass
241
    return "True" 
242
243
@app.route('/_tags', methods=['POST', 'GET'])
244
def tags():
245
    hashtag = request.form['tag'][1:]
246
    try:
247
        session['tags'].append( hashtag )
248
        session.modified = True
249
    except KeyError:
250
        session['tags'] = [hashtag, ] 
251
    return render_template_string(
252
            """{% for tag in tags %}
253
            <span class="tag">{{tag}}</span>
254
            {% endfor %}""",
255
            tags=session['tags'])
256
257
@app.route('/_stars/<context>')
258
def stars(context=None):
259
    if not context:
260
        return jsonify()
261
262
    guid = context[5:] #remove "stars-" from id
263
    keep = request.args.get('keep')
264
265
    context = g.client.Context(guid)
266
    context['keep'] = (keep == 'true')
267
    context.post()
268
269
    # TODO Need to reset query object until supporting notifications
270
    Contexts._reset()
271
272
    return jsonify(keep=keep)
273
274
@app.route('/_moon/<context>')
275
def moon(context=None):
276
    if not context:
277
        return jsonify()
278
279
    keep_impl = request.args.get('keep_impl', None)
280
    guid = context[5:] #remove "moon-" from id
281
282
    context = g.client.Context(guid)
283
    context['keep_impl'] = 1 if keep_impl == 'true' else 0
284
    context.post()
285
286
    return jsonify(keep_impl=keep_impl)
287
288
def paginate(resource, full_query, _PAGE_SIZE=6, page=1, context=None):
289
    """ This function attempts to query SN resources and
290
    return a specific page of result items. """
291
    r = []
292
293
    resource.update_filter(full_query, context=context)
294
    result=resource
295
296
    page_offset = _PAGE_SIZE * (page - 1)
297
    if page_offset>result.total:
298
        raise KeyError
299
300
    for i in range(page_offset, page_offset + _PAGE_SIZE):
301
        try:
302
            r.append(result[i])
303
        except KeyError:
304
            pass
305
306
    total_pages = int(ceil(result.total / float(_PAGE_SIZE)))
307
308
    if total_pages==0:
309
        info = _(u'zero results')
310
    else:
311
        info = _(u'page %(page)s of %(total)s', page=page, total=total_pages)
312
    if page > total_pages and total_pages > 0:
313
        abort(404)
314
315
    return r, total_pages, result.total, info
316
317
@app.route('/resource/search/')
318
@app.route('/resource/search/<query>')
319
@app.route('/resource/reviews')
320
@app.route('/resource/reviews/<query>')
321
@app.route('/resource/questions')
322
@app.route('/resource/questions/<query>')
323
@app.route('/resource/ideas')
324
@app.route('/resource/ideas/<query>')
325
@app.route('/resource/problems')
326
@app.route('/resource/problems/<query>')
327
@app.route('/resource')
328
def resource_list(query=None):
329
    """
330
    Feedback Resource Browser (list) 
331
    """
332
    page=request.args.get('page')
333
    if page:
334
        page=int(page)
335
    else:
336
        return redirect(request.path + "?page=1")
337
338
    if request.path=="/resource":
339
        resource='all'
340
    else:
341
        resource=split(request.path, "/")[2]
342
343
    if resource=='search':
344
        resource = session.get('last_resource') or 'all'
345
346
    if resource=='questions':
347
        resource_object=Questions
348
        resource_label=_("questions")
349
    elif resource=='problems':
350
        resource_object=Problems
351
        resource_label=_("problems")
352
    elif resource=='ideas':
353
        resource_object=Ideas
354
        resource_label=_("ideas")
355
    elif resource=='reviews':
356
        resource_object=Reviews
357
        resource_label=_("reviews")
358
    elif resource=='all':
359
        resource_object=Resources
360
        resource_label=_("resources")
361
        resource='all_' # avoid chop
362
    resource_type=resource[:-1]
363
364
    session['last_resource']=resource
365
    session.modified = True
366
367
    try:
368
        r, total_pages, total, info = paginate(resource_object,
369
            query, page=page, _PAGE_SIZE=4)
370
    except KeyError:
371
        return redirect(url_for('resource_browser', 
372
                context_guid=context_guid, page=1))
373
    
374
    meta=_("browsing %(total)s %(resource_label)s", 
375
            total=total, resource_label=resource_label)
376
377
    if '_pjax' in request.args:
378
        template='_resource-list.html'
379
    else:
380
        template='resource-list.html'
381
    kwargs = {str(resource): 'button_selected'}
382
    return render_template(template, query=query, resource=resource, 
383
                    result=r, type='resource', page=page, info=info,
384
                    total_pages=total_pages, meta=meta, resource_view='true', 
385
                    resource_type=resource_type, **kwargs) 
386
387
@app.errorhandler(404)
388
def page_not_found(error):
389
    template='browser-view.html'
390
    return render_template(template, total=0, info=_('Error'), resource_type='context',
391
                    query='', total_pages=0, browser_view='true',
392
                    result=[], type='context', meta=_('Object not found.'), page=1) 
393
394
@app.errorhandler(500)
395
def page_not_found(error):
396
    template='browser-view.html'
397
    return render_template(template, total=0, info=_('Error'), resource_type='context',
398
                    query='', total_pages=0, browser_view='true',
399
                    result=[], type='context', meta=_('Server error.'), page=1) 
400
401
@app.route('/resource/contexts/<query>')
402
@app.route('/resource/contexts/')
403
@app.route('/context/search/')
404
@app.route('/context/search/<query>')
405
@app.route('/context')
406
def context_grid(query=None, page=None):
407
    """
408
    Context Grid
409
    """
410
    try:
411
        page=int(request.args['page'])
412
        session['page']=page
413
        session.modified = True
414
    except KeyError:
415
        return redirect(url_for('context_grid', 
416
                type=request.args.get('type'),
417
                query=query, page=session.get('page',1)))
418
419
    #try:
420
    #    terms = session['tags'][:]
421
    #except KeyError:
422
    terms = []
423
424
    type=request.args.get('type') or session.get('current_type')
425
    if query:
426
        terms.append(query)
427
        type='all'
428
    
429
    if type in ['activity']:
430
        context_cursor = g.Activities
431
        resource_label=_('activities')
432
    elif type in ['project']:
433
        context_cursor = g.Projects
434
        resource_label=_('projects')
435
    else: # type in [None, '', 'all']:
436
        type='all'
437
        context_cursor = g.Contexts
438
        resource_label=_('contexts')
439
    session['current_type']=type
440
    
441
    full_query = join(terms, " AND ")
442
443
    try:
444
        r, total_pages, total, info = paginate(context_cursor, full_query, page=page)
445
    except KeyError:
446
        if '_pjax' in request.args:
447
            return ''
448
        else:
449
            return redirect(url_for('context_grid',resource_type='context', 
450
                    query=query, page=1))
451
452
    meta = _("browsing %(total)s %(label)s", total=total, label=resource_label)
453
454
    if '_pjax' in request.args:
455
        template='_browser-grid.html'
456
    else:
457
        template='browser-view.html'
458
    kwargs = {str(type): 'tab_selected button_selected'}
459
    return render_template(template, total=total, meta=meta, resource_type='context',
460
                    query=query, total_pages=total_pages, browser_view='true',
461
                    result=r, type=type, info=info, page=page, **kwargs) 
462
 
463
@app.route('/user/search/')
464
@app.route('/user/search/<query>')
465
@app.route('/user')
466
def users_grid(query=None):
467
    """
468
    Users Grid
469
    """
470
    result = client.User.cursor(query)
471
    return render_template('users-grid.html',query=query,
472
                    result=result, type='user') 
473
474
@app.route('/_comments/<resource_guid>', methods=['DELETE'])
475
def del_comment(resource_guid):
476
    client.Comment.delete(resource_guid)
477
    return "true" 
478
479
@app.route('/_comments/<resource_guid>')
480
def comments_browser(resource_guid=None):
481
    resource_type = request.args.get('resource_type')
482
    Comments.filter(parent=resource_guid)
483
    result = Comments
484
    if not result:
485
        result = []
486
    return render_template('_context-comment-list.html', 
487
        result=result, resource_guid=resource_guid)
488
489
@app.route('/context/reviews/<resource_guid>')
490
@app.route('/context/wikiwiki/<resource_guid>')
491
@app.route('/context/gallery/<resource_guid>')
492
@app.route('/review/<review_guid>')
493
def reviews_browser(resource_guid=None, review_guid=None):
494
    if review_guid:
495
        r = client.Review(guid=review_guid, reply=['context'])
496
        resource_guid = r['context']
497
        return redirect ('/context/reviews/'+resource_guid)
498
    Reviews.filter(context=resource_guid, type='review')
499
500
    inner_template='_context-review-list.html'
501
    if '_pjax' in request.args:
502
        template=inner_template
503
        context=None
504
    else:
505
        template='context-view.html'
506
        context=g.client.Context(resource_guid,
507
            reply=['guid', 'title', 'description', 'author', 'user', 'keep', 'keep_impl', 'type'])
508
        try:
509
            session['last_context_title']=context['title']
510
        except RuntimeError:
511
            abort(404)
512
        session['last_context']=context['guid']
513
        session.modified = True
514
515
    kwargs = {'reviews':'button_selected'}
516
    return render_template(template, context=context,
517
        result=Reviews, inner_template=inner_template,
518
        resource_label=_('reviews'),resource_type="review", **kwargs
519
        )
520
521
@app.route('/question/<resource_guid>')
522
@app.route('/idea/<resource_guid>')
523
@app.route('/problem/<resource_guid>')
524
def solution_browser(resource_guid=None):
525
    resource_type=split(request.path, "/")[1]
526
    if resource_type=='question':
527
        resource_cursor=Questions
528
        resource_class=client.Question
529
    elif resource_type=='problem':
530
        resource_cursor=Problems
531
        resource_class=client.Problem
532
    elif resource_type=='idea':
533
        resource_cursor=Ideas
534
        resource_class=client.Idea
535
    
536
    offset=int(request.args.get('cursor_offset') or 0)
537
    if offset:
538
        resource=resource_cursor[offset]
539
    else:
540
        resource=resource_class(resource_guid, 
541
            reply=['guid', 'title', 'content', 'author', 'user', 'context', 'tags', 'mtime'])
542
543
    Solutions.filter(parent=resource['guid'])
544
545
    inner_template='_context-solution-list.html'
546
    if '_pjax' in request.args:
547
        template=inner_template
548
        context=None
549
    else:
550
        template='context-view.html'
551
        context=g.client.Context(resource['context'],
552
            reply=['guid', 'title', 'description', 'author', 'keep', 'keep_impl'])
553
554
    return render_template(template, context=context,
555
        result=Solutions, inner_template=inner_template,
556
        resource=resource, resource_type=resource_type, 
557
        cursor_offset=offset)
558
559
@app.route('/context/view/<context_guid>/<query>')
560
@app.route('/context/view/<context_guid>')
561
@app.route('/context/questions/<context_guid>')
562
@app.route('/context/ideas/<context_guid>')
563
@app.route('/context/problems/<context_guid>')
564
@app.route('/context/all/<context_guid>')
565
def resource_browser(context_guid=None, query=None):
566
    """
567
    Context Resources View 
568
    """
569
    page=request.args.get('page')
570
    if page:
571
        page=int(page)
572
    else:
573
        return redirect(request.path + "?page=1")
574
    
575
    resource_type=split(request.path, "/")[2][:-1]
576
    if resource_type=='question':
577
        resource_object = Questions
578
        resource_label = _("questions")
579
    elif resource_type=='problem':
580
        resource_object = Problems
581
        resource_label = _("problems")
582
    elif resource_type=='idea':
583
        resource_object = Ideas
584
        resource_label = _("ideas")
585
    elif resource_type=='al':
586
        resource_object = Resources 
587
        resource_label = _("resources")
588
        resource_type='all'
589
    
590
    try:
591
        r, total_pages, total, info = paginate(resource_object,
592
            query, page=page, _PAGE_SIZE=4, context=context_guid)
593
    except KeyError:
594
        return redirect(url_for('resource_browser', 
595
                context_guid=context_guid, page=1))
596
597
    meta=_("browsing %(total)s %(resource_label)s", 
598
            total=total, resource_label=resource_label)
599
600
    offset=int(request.args.get('cursor_offset') or 0)
601
    if offset:
602
        context=resource_cursor[offset]
603
    else:
604
        context=g.client.Context(context_guid, reply=['guid', 'title', 'author', 'description', 'keep', 'keep_impl'])
605
    try:
606
        session['last_context']=context['guid']
607
        session['last_context_title']=context['title']
608
        session.modified = True
609
    except RuntimeError:
610
        abort(404)
611
612
    stroke, fill = get_colors()
613
    if '_pjax' in request.args:
614
        template='_context-resource-list.html'
615
        _pjax=True
616
    else:
617
        template='context-view.html'
618
        _pjax=False
619
    kwargs = {str(resource_type) + 's': 'button_selected'}
620
    return render_template(template, result=r, 
621
                    total_pages=total_pages, info=info, meta=meta,
622
                    resource_label=resource_label, stroke=stroke,
623
                    resource_type=resource_type, context=context,
624
                    page=page, fill=fill, _pjax=_pjax, **kwargs) 
625
626
@app.route('/submit_edit', methods=['POST'])
627
def edit_resource():
628
    resource_type = request.form['resource_type']
629
    resource_guid = request.form['edit_guid']
630
    if resource_type=='question':
631
        resource = client.Question(resource_guid)
632
        resource_cursor = Questions
633
    elif resource_type=='idea':
634
        resource = client.Idea(resource_guid)
635
        resource_cursor = Ideas
636
    elif resource_type=='problem':
637
        resource = client.Problem(resource_guid)
638
        resource_cursor = Problems
639
    elif resource_type=='review':
640
        resource = client.Review(resource_guid)
641
        resource_cursor = Reviews
642
    elif resource_type=='solution':
643
        resource = client.Solution(resource_guid)
644
        resource_cursor = Solutions 
645
    
646
    if request.form.get('title'):
647
        resource['title']=request.form['title']
648
    if request.form.get('content'):
649
        resource['content']=request.form['content']
650
    try:
651
        resource.post()
652
    except NotFound:
653
        abort(500)
654
    resource_cursor._reset()
655
    return redirect(request.form.get('href'))
656
657
@app.route('/submit_resource/<resource_type>', methods=['POST'])
658
def new_resource(resource_type):
659
    if resource_type=='question':
660
        resource = client.Question()
661
        resource_cursor = Questions
662
    elif resource_type=='idea':
663
        resource = client.Idea()
664
        resource_cursor = Ideas
665
    elif resource_type=='problem':
666
        resource = client.Problem()
667
        resource_cursor = Problems
668
    resource['content'] = request.form['content']
669
    resource['title'] = request.form['title']
670
    resource['context'] = request.form['guid']
671
    resource['type'] = resource_type
672
    if resource['title']:
673
        resource.post()
674
        resource_cursor._reset()
675
    return redirect('/context/%ss/%s' % (resource_type, resource['context']))
676
677
@app.route('/submit_context', methods=['POST'])
678
def new_context():
679
    context=client.Context()
680
    context['type'] = ['project']
681
    context['title'] = request.form['title']
682
    context['summary'] = request.form['summary']
683
    context['description'] = request.form['content']
684
    if context['title'] and context['description'] and context['summary']:
685
        context.post()
686
        Contexts._reset()
687
    return redirect(url_for('context_grid', page=1, type='project'))
688
689
@app.route('/submit_solution', methods=['POST'])
690
def new_solution():
691
    solution=client.Solution()
692
    solution['content'] = request.form['solution']
693
    solution['parent'] = request.form['resource_guid']
694
    solution['parent_resource'] = request.form['resource_type']
695
    if solution['content']:
696
        solution.post()
697
        Solutions._reset()
698
    return redirect('/%s/%s' % (solution['parent_resource'], solution['parent']))
699
700
@app.route('/submit_review', methods=['POST'])
701
def new_review():
702
    review=client.Review()
703
    review['content'] = request.form['review']
704
    review['title'] = ''
705
    review['type'] = 'review'
706
    context = review['context'] = request.form['resource_guid']
707
    if review['content']:
708
        review.post()
709
        Reviews._reset()
710
    return redirect('/context/reviews/%s' % context)
711
712
@app.route('/submit_report', methods=['POST'])
713
def new_report():
714
    if client.connected:
715
        report = client.Report()
716
    else:
717
        report = offline_client.Report()
718
    report['context'] = request.form['context']
719
    report['implementation'] = request.form['implementation']
720
    report['description'] = request.form['content']
721
    report.post()
722
723
    filename = request.form.get('filename')
724
    if filename:
725
        report.upload_blob('data', filename)
726
727
    return redirect('/resource/problems')
728
729
@app.route('/submit_comment', methods=['POST'])
730
def new_comment():
731
    comment=client.Comment()
732
    comment['message'] = request.form['comment']
733
    comment['parent'] = request.form['resource_guid']
734
    comment['parent_resource'] = request.form['resource_type']
735
    if comment['message']:
736
        comment.post()
737
        Comments._reset()
738
    return redirect('_comments/%s?resource_type=%s' % 
739
                        (comment['parent'], comment['parent_resource']) )
740
741
@app.route('/_events/comment')
742
@app.route('/_events/solution')
743
def event_comment():
744
    resource_type=split(request.path, "/")[2]
745
    if resource_type=='comment':
746
        cursor = Comments
747
    elif resource_type=='solution':
748
        cursor = Solutions 
749
    def feed():
750
        for i in cursor.read_events():
751
            yield 'data: %s\n\n' % i
752
    return Response(feed(), mimetype='text/event-stream',
753
            direct_passthrough=True)
754
755
@app.route('/_events/network')
756
def event_network():
757
    def feed():
758
        while True:
759
            gevent.sleep(1)
760
            for i in Solutions.read_events():
761
                yield 'data: %s\n\n' % i
762
    return Response(feed(), mimetype='text/event-stream',
763
            direct_passthrough=True)
764
765
@app.route('/_shutdown')
766
def shutdown_server():
767
    func = request.environ.get('werkzeug.server.shutdown')
768
    if func is None:
769
        raise RuntimeError('Not running with the Werkzeug Server')
770
    func()
771
    return 'Goodbye'
772
773
@app.route('/_debug')
774
def debug():
775
    raise Warning('All your base are belong to us.')
776
    return 'Take off every Zig'
777
778
if __name__=="__main__":
779
    host = '0.0.0.0' #'0.0.0.0' for all
780
781
    try:
782
        port=int(sys.argv[1])
783
    except IndexError:
784
        port=5000
785
786
    app.debug = True
787
    from gevent.wsgi import WSGIServer
788
    http_server = WSGIServer((host, port), app)
789
    http_server.serve_forever()
790
    #app.run(host=host, port=port)