text: """views.py creates routes and handles all logic based on user input.
  
  It uses variables generated by forms.py and models.py and renders them to
  Jinja2 templates stored in the templates folder.
  
  """
  
  
  from flask import render_template, url_for, request, redirect, flash, abort
  import utils
  from nimble import app
  from forms import *
  from models import *
  
  
  @app.route('/register')
  @app.route('/home')
  @app.route('/')
  @utils.login_required
  def to_start ():
      '''this is a switch to render the correct home page, taking a user to the
      start page if they have logged in and to the sign up page if they have not.
  
      The `@utils.login_required` decorator above will send a visitor that is not
      logged in to the login page, so nothing but the below is required.
      '''
  
      return redirect(url_for('starter',\
                                  username = utils.get_current_user().username))
  
  
  @app.route('/login', methods=['GET','POST'])
  def login():
      '''Standard login page that queries for the user, 
      and checks password against hash generated at sign up.
      '''
      form = LoginForm()
      if form.validate_on_submit():
          user = User.gql("WHERE username = :u", u = form.username.data).get()
          print 'user'
          print user
          if user is not None and user.check_password(form.password.data):
              utils.login_cookie_bake(user)
              print 'loginresult'
              utils.session
              # flash (' welcome {}, you can now edit posts.'.format(user.username))
              return redirect(url_for('starter', username = user.username))
          else:
              # vague error does not let the user know if username attempted exists.
              flash('!!: nope, pwd or username is wrong.')
  
      return render_template('login.html', 
          form=form, 
          hfour='welcome back yo.')
  
  
  @app.route('/logout')
  def logout():
      '''Independent logut page that simply strips the user from the session,
      working with the global configuration set in __init__.py
      '''
      flash(' thanks for hanging out! you are now in anon mode (view only).')
      utils.logout_user()
      return    redirect(request.args.get('next') or url_for('login'))
  
  
  @app.route('/sign_up', methods=['GET','POST'])
  def sign_up():
      ''' Standard sign up from, initiates and saves User obj to database.
      note line 65, which explains a check run on the database after any object
       is stored.
      '''
      form = SignupForm()
      if form.validate_on_submit():
          email = form.Email.data
          username = form.username.data
  
          # Form data us used to create an instance of User
          noob = User(email = form.Email.data,
                      username = form.username.data,
                      password_hash = \
                      utils.make_pw_hash_salt(\
                          form.username.data, form.password.data))
          # You will notice that all put functions contain code immediately 
          # printing the results of attempting to get the object from 
          # the database.
          # This is an explicit check on the success of the put, prompted by
          # errors I was receiving in testing - most page renders that occur
          # immediately after the put, and if the render is called before the
          # put can finish, a 500 error is thrown.
          # thus, since Python is synchronous, I run the most basic query 
          # for the object itself before proceeding.
          noob.put()
          print 'put result'
          print db.get(noob.key())
          print 'registred: ' + username + ' , ' + email
          flash(' welcome to the site %s' % username)
          utils.login_cookie_bake(noob)
          
          return redirect(url_for('starter', username = username))
          # if statement done here, user is directed to their start page
  
      # In Flask, this is where the page is built and renered before the form 
      # is filled out the top posts by number of likes are extracted here to 
      # populate the area below the form.
      ## This is not yet working as expected in production, but is not breaking
      ## functionality, will check in time permitting.
      top_posts_results = Post.gql("").fetch(limit = 3)
  
      top_posts = [post_result for post_result in top_posts_results]
  
      return render_template('register.html',
          top_posts = top_posts,
          form = form, 
          hfour ='>>\t \t \tsign up and start creating posts \t \t \t<<')
  
  
  @app.route('/start/<username>', methods=['GET','POST'])
  @utils.login_required
  def starter(username):
      print 'at starter!'
      '''The starter page shows a logged in user their posts, 
      and renders a single field to start a post by entering the post name.
      '''
      add_form = DataForm()
      add_form.tags.data = utils.get_current_user().username
      if add_form.validate_on_submit():
          # When the post name is entered, forward the user on to write the 
          # other peices of the post with the name prepopulated (but editable).
          # The route to do this is coded directly below this one
          return redirect(url_for('add',\
                          post = add_form.url.data.strip('/'),\
                          username = username))
  
      return  render_template('user.html',
          form = add_form,
          posts = Post.gql("WHERE author = :author",\
                          author = utils.get_current_user()).fetch(limit = None))
  
  
  @app.route('/edit/<username>/<post>', methods=['GET','POST'])
  @utils.login_required
  def add(post, username):
      '''This page allows for both the adding and editing of post.
      The first step is to detect if there is a post which has the same name
      and author
      '''
      edit_post = Post.gql("WHERE url = :url AND author = :author",\
                              url ='/' + post, \
                              author = utils.get_current_user()).get()
  
      if edit_post:
          # the post already exists in the database, and is rendered for editing
          post_edit_form = DataForm(obj=edit_post)
          # The form is pre-populate with existing data to be edited and
          # resumbited for validation.
          if post_edit_form.validate_on_submit():
              print "post_edit validated"
              post_edit_form.populate_obj(edit_post)
              tags = post_edit_form.tags.data.split(',')
              print 'tags'
              print tags
              print 'bef'
              print edit_post.get_tags()
  
              for old_tg in edit_post.get_tags():
                  # Tags already associated with this post are checked,
                  # to find deletions during the edit session.
                  print old_tg
                  print old_tg._posts
                  if old_tg.name not in tags:
                      # tag was deleted, remove post association with tag
                      print old_tg.name
                      print 'is not in tags'
                      old_tg._posts.remove(edit_post.key())
                      old_tg.put()
                      print 'put result for removed tag'
                      print db.get(old_tg.key())
  
              for tg in tags:
                  # In with the new
                  if tg:
                      # Tag is not none
                      tg_obj = Tag.get_or_create(tg)
                      # This function returns an existing tag if the tag exists,
                      # and creates a new tag if it does not.
                      if edit_post.key() not in tg_obj._posts:
                          tg_obj._posts.append(edit_post.key())
                      tg_obj.put()
                      print 'put result for tag'
                      print db.get(tg_obj.key())
  
              edit_post.put()
              print 'edit post put result'
              db.get(edit_post.key())
              flash(" the post'{}' has been edited".format(post_edit_form.url.data))
  
              return redirect(url_for('render_user_post', 
                  username = edit_post.author.username,
                  post_name = post_edit_form.url.data.strip('/')))
  
          return render_template ('edit.html',\
                                  form = post_edit_form, post = edit_post)
      else:
          # The post does not exists in the database, and is rendered for creation
          start_post = Post(url = post, 
                              author = utils.get_current_user())
  
          post_add_form = DataForm(obj=start_post)
          # A new post object is build from the Post class and the form is
          # pre-populated with the current user as the author and the name entered
          # on the start page as the name.
          if post_add_form.validate_on_submit():
              new_post = Post(url = post_add_form.url.data,
                              content = post_add_form.content.data,
                              author = utils.get_current_user())
              new_post.put()
              tags = post_add_form.tags.data.split(',')
  
              for tg in tags:
                  if tg:
                      tg_obj = Tag.get_or_create(tg)
                      if new_post.key() not in tg_obj._posts:
                          tg_obj._posts.append(new_post.key())
                      tg_obj.put()
                      print 'tag put result'
                      db.get(tg_obj.key())
  
              print 'new post put result'
              print db.get(new_post.key())
  
              flash(" the post'{}' has been created!".format(\
                      post_add_form.url.data))
              return redirect(url_for('render_user_post', 
                              username = new_post.author.username,
                              post_name = new_post.url.strip('/')))
  
          return render_template('add.html',\
                                  form = post_add_form,\
                                  post = start_post)
  
  @app.route('/delete/<post>', methods=['GET','POST'])
  @utils.login_required
  def kill_post(post):
      '''Page confirming the deletion of a post.
      Accessed by the user via a link at the top of the edit page.
  
      Only this code can delete post after validting the user has access.
      The database query is run using the same configuration that declares
      login status to insure user has needed access to delete a post. 
      '''
      target_post = Post.gql("WHERE url = :url AND author = :author",\
                              url ='/' + post, \
                              author = utils.get_current_user()).get()
  
      if not target_post:
          abort(401)
      if request.method == "POST":
          # Upon confirmation, empty post requet is sent.
          target_post.delete()
          print 'post delete confirmed'
          print target_post
          print 'no longer stored'
          flash(" '{}' is gone.".format(target_post.url))
          return redirect(url_for('starter', username = target_post.author.username))
      else:
          flash('!! this will delete post')
      return render_template('confirm_kill.html')
  
  @app.route('/<username>/<post_name>/', methods=['GET','POST'])
  def render_user_post(username, post_name):
      ''' This route displays posts retrieved from the database.
      comments are added using the Post class and db model and marked as comments
      to be rendered as such on the corresponding post rather than as independent
      posts.
      '''
      author = User.gql("WHERE  username = :username",\
                          username = username).get()
      post = Post.gql("WHERE url = :url AND author = :author",\
                       url ='/' + post_name, \
                       author = author).get()
      if author and post:
          # a post by this name and author exists 
  
          # A url string is set up for any comments based on the post name
          # and the comment form is retrieved and prepopulated 
          # with this name (url).
          comm_url = '/' + 'CommentOn__'+ post.url.strip('/')\
                      + '_Num' + str(len(post.comments) + 1)
  
          comment_form = DataForm(obj = Post(url = comm_url))
  
          if comment_form.validate_on_submit():
              # comment is ready to post
              comment = Post(url =  comm_url,
                      content = comment_form.content.data,
                      author = utils.get_current_user(),
                      _is_comment = True)
              comment.put()
              print 'comment put result'
              print db.get(comment.key())
              post.comments.append(comment.key())
              post.put()
              print 'post put result'
              print db.get(post.key())
  
              return render_template('user_post.html', 
                                          post = post,
                                          tags = post.get_tags(), 
                                          form = comment_form,
                                          comm_url = comm_url,
                                          empty_list = [])
  
  
          return render_template('user_post.html', 
                                      post = post,
                                      tags = post.get_tags(), 
                                      form = comment_form,
                                      comm_url = comm_url,
                                      empty_list = [])
      else:
          # this author and post combo is not in the database
          # the page doe not exist, ergo 404 is the response
          abort(404)
  
  @app.route('/<username>/<post_name>/edit/<edit_comment_url>', methods=['GET','POST'])
  def render_and_edit_comment(username, post_name, edit_comment_url):
      ''' This allows a user to edit a comment, and is modeled after the post
      editing route.
      '''
      author = User.gql("WHERE  username = :username",\
                          username = username).get()
      post = Post.gql("WHERE url = :url AND author = :author",\
                       url ='/' + post_name, \
                       author = author).get()
      edit_comment = Post.gql("WHERE _is_comment = True"+\
                                  " AND url = :url", url = '/' + edit_comment_url).get()
      # An explicit check for is_comment would be overkill, since comment urls
      # are predetermined.
  
      # Not only would a non-comment post with a comment URL be extremely 
      # unlikely, the in page routing in user_post.html would not allow a typical
      # user to use this route with a post that is not a comment by accident.
  
      if author and post and edit_comment:
          # author and post exist, and comment is retreivable by url
          comment_form = DataForm(obj = edit_comment)
          comm_url = edit_comment_url
  
          if comment_form.validate_on_submit():
              comment_form.populate_obj(edit_comment)
              edit_comment.put()
              print 'comment edited comment put result'
              print db.get(edit_comment.key())
  
              return render_template('user_post.html', 
                                          post = post,
                                          tags = post.get_tags(), 
                                          form = comment_form,
                                          comm_url = comm_url,
                                          empty_list = [])
  
  
          return render_template('user_post.html', 
                                      post = post,
                                      tags = post.get_tags(), 
                                      form = comment_form,
                                      comm_url = comm_url,
                                      empty_list = [])
      else:
          # this author and post combo is not in the database
          # the comment doe not exist, ergo 404 is the response
          abort(404)
  
  
  @app.route('/like/<username>/<post_name>')
  @utils.login_required
  def like(username, post_name):
      '''nothing is rendered here, this is simply the route for liking a page.
      once the post is stored with the liking user, the post is re-rendered.
      '''
      author = User.gql("WHERE  username = :username", username = username).get()
      like_post = Post.gql("WHERE url = :url AND author = :author",\
                      url ='/' + post_name,\
                      author = author).get()
      like_post._liked.append(utils.get_current_user().key())
      like_post.put()
      print 'post liked'
      print db.get(like_post.key())
      # As noted in the models, liked is a list of user objects, 
      # which is why append is used. the original attribute does not need to be 
      # overwritten.
      flash(" liked  '{}'".format(like_post.url))
      return redirect(url_for('render_user_post', 
          username = username,
          post_name = post_name))
  
  
  @app.route('/all_tags')
  def all_tags():
      '''Since there is no feed, user interaction is tag driven.
  
      This is the core route for exploring user content, rendering a list of tags
      from which a user can select interests.
      '''
      print Tag.all().fetch(limit = None)
      return render_template( 'All_Tags.html', tags = Tag.all().fetch(limit = None))
  
  
  @app.route('/tags/<tag>')
  def catch_tag(tag):
      '''When a tag link is clicked, either in the above route or on a user post,
      a page containing all affiliated posts is rendered with this route.
      '''
      target_tag=Tag.gql("WHERE name = :tag", tag = tag).get()
      print 'posts'
      print target_tag.get_posts()
      return render_template('tag_post.html', tag = target_tag)
  
  
  # error hanlders are standard.
  
  @app.errorhandler(404)
  def post_not_found(e):
      return render_template('404.html'), 404
  
  
  @app.errorhandler(401)
  def unauth(e):
      return render_template('401.html'), 401
  
  
  @app.errorhandler(500)
  @app.errorhandler(503)
  def internal_error(e):
      return render_template('500.html', error = e), 500
  
  
  @app.context_processor
  def inject_tags():
      # this final function is used to allow tags to be accessed by JavaScript.
      return dict(all_tags = Tag.all)