# -*- coding: utf-8 -*-
# Copyright (C) 2012, Sebastian Silva
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
import errno
import logging
import sys
import os
import locale
import subprocess
from math import ceil
from string import join, split
import gevent
from flask import Flask, request, url_for, abort, jsonify, Response,\
redirect, session, render_template, render_template_string, g
from flaskext.babel import Babel, gettext as _, format_datetime
from werkzeug import Headers
from werkzeug import secure_filename
import simplejson
import tempfile
from sugar_network.toolkit.http import NotFound
from client import Client
_BUFFER_SIZE = 1024 * 10
UPLOAD_FOLDER = tempfile.mkdtemp()
WWW = None # regular browser or embedded in shell
_pull_events = []
_pull_listener = None
""" Initialization of local app """
app = Flask(__name__)
app.secret_key = "ilovesugar" # necessary to initialize sessions
babel = Babel(app)
app.config['BABEL_DEFAULT_LOCALE'] = 'en'
app.config['BABEL_DEFAULT_TIMEZONE'] = 'America/Lima'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
from cursors import *
from dialogs import *
FeedbackView.register(app)
ProjectView.register(app)
AboutView.register(app)
def event_stream():
global _pull_events
while True:
for event in _pull_events:
yield 'data: %s\n\n' % simplejson.dumps(_pull_events.pop(0))
gevent.sleep(1)
@babel.localeselector
def get_locale():
global WWW
if ('localhost' in request.host) or ('127.0.0.1' in request.host):
# we're probably embedded, get locale from env
lang = locale.getdefaultlocale()[0].split('_')[0]
logging.debug('Locale from env: %s' % lang)
WWW = False
else:
lang = request.accept_languages.best_match(['es', 'en'])
logging.debug('Locale from headers: %s' % lang)
WWW = True
return lang
@app.template_filter('special_str')
def special_str(arg=None):
if not arg:
return ''
if isinstance(arg, basestring): # in Python 3: isinstance(arg, str)
return arg
else:
return(arg[0])
@app.template_filter('timedelta')
def timedelta(mtime):
return format_datetime(mtime, _('MMMM d, yyyy'))
def get_colors():
if Client.anonymous:
return ('#000000', '#000000')
try:
import gconf
except ImportError:
return ('#000000', '#000000')
conf = gconf.client_get_default()
return conf.get_string('/desktop/sugar/user/color').split(',')
def get_user():
try:
import gconf
except ImportError:
return 'demo'
conf = gconf.client_get_default()
return conf.get_string('/desktop/sugar/user/nick')
def get_documents_path():
"""Gets the path of the DOCUMENTS folder
If xdg-user-dir can not find the DOCUMENTS folder it returns
$HOME, which we omit. xdg-user-dir handles localization
(i.e. translation) of the filenames.
Returns: Path to $HOME/DOCUMENTS or None if an error occurs
Taken from jarabe/journal/model.py
"""
try:
pipe = subprocess.Popen(['xdg-user-dir', 'DOCUMENTS'],
stdout=subprocess.PIPE)
documents_path = os.path.normpath(pipe.communicate()[0].strip())
if os.path.exists(documents_path) and \
os.environ.get('HOME') != documents_path:
return documents_path
except OSError, exception:
if exception.errno != errno.ENOENT:
logging.exception('Could not run xdg-user-dir')
return None
@app.context_processor
def inject_vars():
# Here we can inject variables into every template call
stroke, fill = get_colors()
kwvar = {
'userid': Client.sugar_uid,
'sugar_nick' : get_user()
}
return dict(stroke=stroke, fill=fill, **kwvar)
@app.before_request
def before_request():
g.home_mount = home_mount
g.network_mount = network_mount
session['connected'] = network_mount.client.inline
if not session['connected']:
g.client = home_mount.client
g.Contexts = home_mount.Contexts
g.Activities = home_mount.Activities
g.Artifacts = home_mount.Artifacts
g.Projects = home_mount.Projects
g.autocomplete_Contexts = home_mount.autocomplete_Contexts
g.Questions = home_mount.Questions
g.Ideas = home_mount.Ideas
g.Problems = home_mount.Problems
g.Solutions = home_mount.Solutions
g.Comments = home_mount.Comments
g.Reviews = home_mount.Reviews
g.Resources = home_mount.Resources
else:
g.client = network_mount.client
g.Contexts = network_mount.Contexts
g.Activities = network_mount.Activities
g.Artifacts = network_mount.Artifacts
g.Projects = network_mount.Projects
g.autocomplete_Contexts = network_mount.autocomplete_Contexts
g.Questions = network_mount.Questions
g.Ideas = network_mount.Ideas
g.Problems = network_mount.Problems
g.Solutions = network_mount.Solutions
g.Comments = network_mount.Comments
g.Reviews = network_mount.Reviews
g.Resources = network_mount.Resources
def incoming(event):
global _pull_events
_pull_events.append(event)
#if event['event']=='inline' and event.get('state')=='offline':
# session['connected'] = False
#if event['event']=='inline' and event.get('state')=='online':
# session['connected'] = True
return None
_pull_listener = Client.connect(incoming)
@app.route('/event_source')
def sse_request():
return Response(
event_stream(),
mimetype='text/event-stream')
@app.route('/')
def home():
return redirect(url_for('context_grid'))
@app.route('/_stars/')
def stars(context=None):
""" Will make favorite and put in Home view
"""
if not context:
return jsonify()
guid = context[5:] # remove "stars-" from id
favorite = request.args.get('favorite')
Client.call('PUT', ['context', guid], 'favorite',
content=(favorite == 'true'))
# TODO Need to reset query object until supporting notifications
g.Contexts._reset()
return jsonify(favorite=favorite)
@app.route('/_moon/')
def moon(context=None):
if not context:
return jsonify()
clone = request.args.get('clone', None)
guid = context[5:] # remove "moon-" from id
Client.call('PUT', ['context', guid], 'clone', clone == 'true', spawn=True)
return jsonify(clone=clone)
@app.route('/_toggle_connect')
@app.route('/_toggle_connect/')
def toggle_connect(returnto='/context'):
session['connected'] = not session.get('connected')
return redirect(returnto)
@app.route('/context/icon/')
@app.route('/context/icon/')
def gen_icon(context_guid=None):
try:
cur_type = request.args.get('type') or session['current_type']
except KeyError:
pass
offset = int(request.args.get('cursor_offset') or 0)
if offset and cur_type:
if cur_type in ['activity']:
context_cursor = g.Activities
elif cur_type in ['project']:
context_cursor = g.Projects
else:
context_cursor = g.Contexts
context_guid = context_cursor[offset].guid
else:
context_cursor = g.client.Context
return redirect(context_cursor.url(context_guid, 'icon'))
@app.route('/launch/')
def launch(context_guid):
g.client.launch(context_guid)
return redirect('/context/reviews/%s' % context_guid)
@app.route('/new/resource')
@app.route('/new/resource/')
def new_resource(context_guid=None):
""" Use Case 1: New question idea or problem
"""
if not context_guid:
context_guid = session.get('last_context')
if not context_guid:
return redirect(url_for('context_grid'))
offset = int(request.args.get('cursor_offset') or 0)
if offset:
context = g.Contexts[offset]
else:
context = g.client.Context(context_guid,
reply=['title', 'layer'])
return render_template('resource-form.html', context=context)
@app.route('/artifacts/preview/')
def gen_preview(guid):
return redirect(g.client.Artifact.url(guid, 'preview'))
@app.route('/artifacts/upload', methods=['POST'])
def artifact_post():
artifact = g.client.Artifact()
artifact['context'] = request.form['context']
artifact['description'] = request.form['content']
upload = request.files['artifact_file']
if upload:
filename = secure_filename(upload.filename)
artifact['title'] = filename
artifact['type'] = 'instance'
artifact.post()
if filename:
artifact.upload_blob('data', upload.read(), upload.content_type)
return redirect('/context/artifacts/' + request.form['context'])
@app.route('/artifacts/upload/to_context/')
@app.route('/artifacts/upload')
def artifact_upload(context=''):
return render_template('upload-form.html',
context=context)
@app.route('/artifacts/copy/')
def artifact_copy(guid):
artifact = g.client.Artifact(guid, reply=['guid', 'title', 'context'])
filename = artifact['title']
blob = artifact.get_blob('data')
document_path = get_documents_path()
with file(os.path.join(document_path, filename), 'w') as f:
for chunk in blob:
f.write(chunk)
title = _('Artifact has been downloaded.')
body = _('Success!\n\n'
'File %(filename)s has been copied to your Documents folder.\n'
'You can access it from the Journal.' , filename=filename)
return render_template('dialog.html', title=title, body=body)
@app.route('/artifacts/download/')
def artifact_download(guid):
if not WWW:
return redirect(url_for('artifact_copy', guid=guid))
return redirect(g.client.Artifact.url(guid, 'data'))
@app.route('/favorites')
def favorites():
if session.get('favorites-filter'):
session['favorites-filter'] = False
else:
session['favorites-filter'] = True
return redirect(url_for('context_grid'))
@app.route('/query')
def autocomplete_context():
query = "title:" + request.args.get('term')
r, total_pages, total, info = paginate(g.autocomplete_Contexts, query)
result = []
for item in r:
result.append({'value': item['guid'],
'label': item['title']})
return simplejson.dumps(result)
@app.route('/search/')
def search(query=None):
return redirect(url_for('context_grid', query=query))
def paginate(resource, full_query, _PAGE_SIZE=6, page=1, context=None):
""" This function attempts to query SN resources and
return a specific page of result items. """
r = []
resource.update_filter(full_query, context=context)
result = resource
page_offset = _PAGE_SIZE * (page - 1)
if page_offset > result.total:
raise KeyError
for i in range(page_offset, page_offset + _PAGE_SIZE):
try:
r.append(result[i])
except KeyError:
pass
total_pages = int(ceil(result.total / float(_PAGE_SIZE)))
if total_pages == 0:
info = _(u'zero results')
else:
info = _(u'page %(page)s of %(total)s', page=page, total=total_pages)
if page > total_pages and total_pages > 0:
abort(404)
return r, total_pages, result.total, info
@app.route('/resource/artifacts')
@app.route('/resource/search/')
@app.route('/resource/search/')
@app.route('/resource/reviews')
@app.route('/resource/reviews/')
@app.route('/resource/questions')
@app.route('/resource/questions/')
@app.route('/resource/ideas')
@app.route('/resource/ideas/')
@app.route('/resource/problems')
@app.route('/resource/problems/')
@app.route('/resource')
def resource_list(query=None):
"""
Feedback Resource Browser (list)
"""
page = request.args.get('page')
if page:
page = int(page)
else:
return redirect(request.path + "?page=1")
if request.path == "/resource":
resource = 'all'
else:
resource = split(request.path, "/")[2]
if resource == 'search':
resource = session.get('last_resource') or 'all'
if resource == 'questions':
resource_object = g.Questions
resource_label = _("questions")
elif resource == 'problems':
resource_object = g.Problems
resource_label = _("problems")
elif resource == 'ideas':
resource_object = g.Ideas
resource_label = _("ideas")
elif resource == 'reviews':
resource_object = g.Reviews
resource_label = _("reviews")
elif resource == 'artifacts':
resource_object = g.Artifacts
resource_label = _("artifacts")
elif resource == 'all':
resource_object = g.Resources
resource_label = _("resources")
resource = 'all_' # avoid chop of plural form
resource_type = resource[:-1]
session['last_resource'] = resource
session.modified = True
try:
r, total_pages, total, info = paginate(resource_object,
query, page=page, _PAGE_SIZE=3)
except KeyError:
return redirect(url_for('context_resource_browser',
context_guid=context_guid, page=1))
meta = _("browsing %(total)s %(resource_label)s",
total=total, resource_label=resource_label)
if '_pjax' in request.args:
if resource == 'artifacts':
template = '_artifact-list.html'
elif resource == 'reviews':
template = '_review-list.html'
else:
template = '_resource-list.html'
else:
template = 'resource-list.html'
kwargs = {str(resource): 'button_selected'}
if resource == 'artifacts':
kwargs['inner_template'] = '_artifact-list.html'
kwargs['artifacts_view'] = 'true'
elif resource == 'reviews':
kwargs['inner_template'] = '_review-list.html'
else:
kwargs['resource_view'] = 'true'
return render_template(template, query=query, resource=resource,
result=r, type='resource', page=page, info=info,
total_pages=total_pages, meta=meta,
resource_type=resource_type, **kwargs)
@app.errorhandler(404)
def page_not_found(error):
title = _('Object not found.')
body = _('The resource you are looking for '\
+ 'is not available at the moment.\n\n'\
+ 'If you are offline try connecting.');
return render_template('dialog.html', title=title, body=body)
@app.errorhandler(500)
def server_error(error):
template = 'browser-view.html'
return render_template(template, total=0, info=_('Error'),
resource_type='context', query='', total_pages=0,
browser_view='true', result=[], type='context',
meta=_('Server error.'), page=1), 500
@app.route('/resource/contexts/')
@app.route('/resource/contexts/')
@app.route('/context/search/')
@app.route('/context/search/')
@app.route('/context')
def context_grid(query=None, page=None):
"""
Context Grid
"""
try:
page = int(request.args['page'])
preload = request.args.get('_preload')
if not ('_preload' in request.args):
session['page'] = page
else:
session['page'] = int(request.args['_preload'])
session.modified = True
except KeyError:
return redirect(url_for('context_grid',
type=request.args.get('type'),
query=query, page=session.get('page', 1)))
terms = []
cur_type = request.args.get('type') or session.get('current_type')
if query:
terms.append(query)
cur_type = 'all'
if cur_type in ['activity']:
context_cursor = g.Activities
resource_label = _('activities')
elif cur_type in ['project']:
context_cursor = g.Projects
resource_label = _('projects')
else: # type in [None, '', 'all']:
cur_type = 'all'
context_cursor = g.Contexts
resource_label = _('contexts')
session['current_type'] = cur_type
full_query = join(terms, " AND ")
try:
r, total_pages, total, info = paginate(context_cursor, full_query,
page=page)
except KeyError:
if '_pjax' in request.args:
return ''
else:
return redirect(url_for('context_grid', resource_type='context',
query=query, page=1))
meta = _("browsing %(total)s %(label)s", total=total, label=resource_label)
if '_pjax' in request.args:
template = '_browser-grid.html'
else:
template = 'browser-view.html'
kwargs = {str(cur_type): 'tab_selected'}
return render_template(template, total=total, meta=meta,
resource_type='context', query=query,
total_pages=total_pages, browser_view='true',
result=r, type=cur_type, info=info, page=page, **kwargs)
@app.route('/user/search/')
@app.route('/user/search/')
@app.route('/user')
def users_grid(query=None):
"""
Users Grid
"""
result = g.client.User.cursor(query)
return render_template('users-grid.html', query=query,
result=result, type='user')
@app.route('/_comments/', methods=['DELETE'])
def del_comment(resource_guid):
g.client.Comment.delete(resource_guid)
return "true"
@app.route('/_artifacts/', methods=['DELETE'])
def del_artifact(resource_guid):
g.client.Artifact.delete(resource_guid)
return "true"
@app.route('/_comments/')
def comments_browser(resource_guid=None):
document = request.args['document']
g.Comments.filter(**{document: resource_guid})
result = g.Comments
if not result:
result = []
return render_template('_context-comment-list.html',
result=result, resource_guid=resource_guid, document=document)
@app.route('/article/')
def project_browser(context_guid=None):
inner_template = '_context-article-view.html'
if '_pjax' in request.args:
template = inner_template
context = None
else:
template = 'context-view.html'
context = g.client.Context(context_guid,
reply=['guid', 'title', 'description', 'author',
'summary', 'layer', 'type'])
try:
session['last_context_title'] = context['title']
except NotFound:
abort(404)
session['last_context'] = context['guid']
session.modified = True
return render_template(template, context=context,
inner_template=inner_template, article_view=True)
@app.route('/reload/')
def reload(href=None):
g.Solutions._reset()
g.Comments._reset()
g.Contexts._reset()
g.Questions._reset()
g.Problems._reset()
g.Ideas._reset()
g.Resources._reset()
g.Reviews._reset()
return redirect(href)
@app.route('/context/reviews/')
@app.route('/context/wikiwiki/')
#@app.route('/context/gallery/')
@app.route('/review/')
def reviews_browser(resource_guid=None, review_guid=None):
if review_guid:
r = g.client.Review(guid=review_guid, reply=['context'])
try:
resource_guid = r['context']
except NotFound:
abort(404)
return redirect('/context/reviews/' + resource_guid)
g.Reviews.filter(context=resource_guid)
inner_template = '_context-review-list.html'
if '_pjax' in request.args:
template = inner_template
context = None
else:
template = 'context-view.html'
context = g.client.Context(resource_guid,
reply=['guid', 'title', 'description', 'author',
'summary', 'layer', 'type'])
try:
session['last_context_title'] = context['title']
except NotFound:
abort(404)
session['last_context'] = context['guid']
session.modified = True
kwargs = {'reviews': 'button_selected'}
return render_template(template, context=context,
result=g.Reviews, inner_template=inner_template,
resource_label=_('reviews'), resource_type="review", **kwargs)
@app.route('/question/')
@app.route('/idea/')
@app.route('/problem/')
@app.route('/feedback/')
def solution_browser(resource_guid=None):
resource_type = split(request.path, "/")[1]
if resource_type == 'question':
resource_cursor = g.Questions
elif resource_type == 'problem':
resource_cursor = g.Problems
elif resource_type == 'idea':
resource_cursor = g.Ideas
offset = int(request.args.get('cursor_offset') or 0)
if offset:
resource = resource_cursor[offset]
else:
resource = g.client.Feedback(resource_guid,
reply=['guid', 'title', 'content', 'author', 'context',
'tags', 'mtime', 'type'])
g.Solutions.filter(feedback=resource['guid'])
inner_template = '_context-solution-list.html'
if '_pjax' in request.args:
template = inner_template
context = None
else:
template = 'context-view.html'
try:
context = g.client.Context(resource['context'],
reply=['guid', 'title', 'description', 'summary', 'author',
'layer', 'type'])
except:
return redirect(url_for('resource_list'))
return render_template(template, context=context,
result=g.Solutions, inner_template=inner_template,
resource=resource, resource_type=resource_type,
cursor_offset=offset)
@app.route('/context/view//')
@app.route('/context/view/')
@app.route('/context/questions/')
@app.route('/context/ideas/')
@app.route('/context/problems/')
@app.route('/context/artifacts/')
@app.route('/context/gallery/')
@app.route('/context/all/')
def context_resource_browser(context_guid=None, query=None):
"""
Context Resources View
"""
page = request.args.get('page')
if page:
page = int(page)
else:
return redirect(request.path + "?page=1")
resource_type = split(request.path, "/")[2]
if resource_type == 'questions':
resource_object = g.Questions
resource_label = _("questions")
elif resource_type == 'problems':
resource_object = g.Problems
resource_label = _("problems")
elif resource_type == 'ideas':
resource_object = g.Ideas
resource_label = _("ideas")
elif resource_type == 'artifacts' or resource_type == 'gallery':
resource_type = 'artifacts'
resource_object = g.Artifacts
resource_label = _("artifacts")
elif resource_type == 'all':
resource_object = g.Resources
resource_label = _("resources")
try:
r, total_pages, total, info = paginate(resource_object,
query, page=page, _PAGE_SIZE=3, context=context_guid)
except KeyError:
return redirect(url_for('context_resource_browser',
context_guid=context_guid, page=1))
meta = _("browsing %(total)s %(resource_label)s",
total=total, resource_label=resource_label)
offset = int(request.args.get('cursor_offset') or 0)
if offset:
context = resource_cursor[offset]
else:
context = g.client.Context(context_guid, reply=['guid', 'title',
'author', 'summary', 'description', 'layer', 'type'])
try:
session['last_context'] = context['guid']
session['last_context_title'] = context['title']
session.modified = True
except NotFound:
abort(404)
stroke, fill = get_colors()
if '_pjax' in request.args:
if resource_type == 'artifacts':
template = '_context-artifact-list.html'
else:
template = '_context-resource-list.html'
_pjax = True
else:
template = 'context-view.html'
_pjax = False
kwargs = {str(resource_type): 'button_selected'}
if resource_type == 'artifacts':
kwargs['inner_template'] = '_context-artifact-list.html'
return render_template(template, result=r, resource_type=resource_type,
total_pages=total_pages, info=info, meta=meta,
resource_label=resource_label, stroke=stroke,
context=context, page=page, fill=fill,
_pjax=_pjax, **kwargs)
@app.route('/submit_edit', methods=['POST'])
def edit_resource():
resource_type = request.form['resource_type']
resource_guid = request.form['edit_guid']
if resource_type == 'question':
resource = g.client.Feedback(resource_guid)
resource_cursor = g.Questions
elif resource_type == 'idea':
resource = g.client.Feedback(resource_guid)
resource_cursor = g.Ideas
elif resource_type == 'problem':
resource = g.client.Feedback(resource_guid)
resource_cursor = g.Problems
elif resource_type == 'review':
resource = g.client.Review(resource_guid)
resource_cursor = g.Reviews
elif resource_type == 'solution':
resource = g.client.Solution(resource_guid)
resource_cursor = g.Solutions
if request.form.get('title'):
resource['title'] = request.form['title']
if request.form.get('content'):
resource['content'] = request.form['content']
resource.post()
resource_cursor._reset()
return redirect(request.form.get('href'))
@app.route('/submit_resource', methods=['POST'])
def submit_resource():
for r_type in ('question', 'idea', 'problem'):
if request.form.get(r_type, None):
resource_type = r_type
if resource_type == 'question':
resource_cursor = g.Questions
elif resource_type == 'idea':
resource_cursor = g.Ideas
elif resource_type == 'problem':
resource_cursor = g.Problems
resource = g.client.Feedback()
resource['content'] = request.form['content']
resource['title'] = request.form['title']
resource['context'] = request.form['guid']
resource['type'] = resource_type
if resource['title']:
resource.post()
resource_cursor._reset()
return redirect('/context/%ss/%s' % (resource_type, resource['context']))
@app.route('/update_context', methods=['POST'])
def update_context():
context = g.client.Context(request.form['edit_guid'])
context['title'] = request.form['title']
context['layer'] = ['public', 'pilot']
context['summary'] = request.form['summary']
context['description'] = request.form['content']
if context['title'] and context['description'] and context['summary']:
context.post()
else:
abort(500)
return redirect('/article/%s' % request.form['edit_guid'])
@app.route('/submit_context', methods=['POST'])
def new_context():
context = g.client.Context()
context['type'] = ['project']
context['title'] = request.form['title']
context['layer'] = ['public', 'pilot']
context['summary'] = request.form['summary']
context['description'] = request.form['content']
if context['title'] and context['description'] and context['summary']:
context.post()
g.Contexts._reset()
else:
abort(500)
return redirect(url_for('project_browser', context_guid=context['guid']))
@app.route('/submit_solution', methods=['POST'])
def new_solution():
solution = g.client.Solution()
solution['content'] = request.form['solution']
solution['feedback'] = request.form['resource_guid']
if solution['content']:
solution.post()
g.Solutions._reset()
return redirect('/%s/%s' % (request.form['resource_type'],
solution['feedback']))
@app.route('/submit_review', methods=['POST'])
def new_review():
review = g.client.Review()
review['content'] = request.form['review']
review['title'] = ''
review['rating'] = 0
context = review['context'] = request.form['resource_guid']
if review['content']:
review.post()
g.Reviews._reset()
return redirect('/context/reviews/%s' % context)
@app.route('/submit_comment', methods=['POST'])
def new_comment():
document = request.form['document']
comment = g.client.Comment()
comment['message'] = request.form['comment']
comment[document] = request.form['resource_guid']
if comment['message']:
comment.post()
g.Comments._reset()
return redirect('_comments/%s?resource_type=%s&document=%s' %
(comment[document], request.form['resource_type'], document))
@app.route('/_shutdown')
def shutdown_server():
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the Werkzeug Server')
func()
return 'Goodbye'
@app.route('/_debug')
def debug():
raise Warning('All your base are belong to us.')
return 'Take off every Zig'
if __name__ == "__main__":
host = '0.0.0.0' # '0.0.0.0' for all
try:
port = int(sys.argv[1])
except IndexError:
port = 5000
app.debug = True
from gevent.wsgi import WSGIServer
http_server = WSGIServer((host, port), app)
http_server.serve_forever()
#app.run(host=host, port=port)