Wed Nov 09 2016
Copied to clipboard! Copy reply
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
#import sys
#sys.path.append('./')
import os
from json import dumps as jsonify
from json import loads as unjsonify
#import datetime


import webapp2
#from google.appengine.api import memcache, app_identity

from webapp2_extras import sessions, sessions_memcache, sessions_ndb
import jinja2
import MySQLdb

# Bucket ops
import cloudstorage as gcs
from google.appengine.ext import ndb
from google.appengine.api import memcache, app_identity
from imghdr import what as getmime

from slugify import slugify

from models.db import DB
from models.tables import Tables












from os import path, getenv, environ
CLOUDSQL_DB_NAME = 'fcheck1'
CLOUDSQL_CONNECTION_NAME = environ.get('CLOUDSQL_CONNECTION_NAME')
CLOUDSQL_USER = environ.get('CLOUDSQL_USER')
CLOUDSQL_PASSWORD = environ.get('CLOUDSQL_PASSWORD')

raise ValueError('A very specific bad thing happened')


# Simply initialize our SQLAlchemy models
tables = Tables()

#CLOUDSQL_PROJECT = 'fcheckus'
#CLOUDSQL_INSTANCE = 'fcheckus:us-east1:fcheck'

def nl2br(value): 
  split = value.split('\n')
  return jinja2.Markup('<br>').join(split)
  


jinja_env = jinja2.Environment(
  loader=jinja2.FileSystemLoader(os.path.dirname(__file__) + '/templates/'),
  extensions=['jinja2.ext.autoescape'],
  autoescape=True)

jinja_env.filters['nl2br'] = nl2br





class BaseHandler(webapp2.RequestHandler):
  def dispatch(self):
    self.session_store = sessions.get_store(request=self.request)
    try:
      webapp2.RequestHandler.dispatch(self)
    finally:
      self.session_store.save_sessions(self.response)

  @webapp2.cached_property
  def session(self):
    return self.session_store.get_session(name='fcheck', max_age=31104000, backend='datastore')


  def render(self, template, templatedata = {}, spfdata = {}, spf=''):
    template = jinja_env.get_template(template + '.html')
    
    if self.session.get('name'):
      templatedata['first_name'] = self.session.get('name').split(' ')[0]
      print '----'
      print templatedata['first_name'] 

    if spf:
      templatedata['full'] = False
      spfdata['body'] = {'content': template.render(**templatedata) }
      return jsonify(spfdata)
    else:
      return template.render(**templatedata)


class Home(BaseHandler):

  def get(self):
    templatedata, spfdata  =  ({'full' : True}, {})
    spfdata['title'] = '$ fcheck /'
    
    db = DB(tables)
    sets = db.set_get_all()
    
    templatedata['sets'] = [
      {
        "id": set[0],
        "name": set[1],
        "slug": set[2]
      } 
      for set in sets
    ]

    self.response.write(self.render('index', templatedata, spfdata, self.request.get('spf')))


class SetCreate(BaseHandler):
  def get(self):
    templatedata, spfdata  =  ({'full' : True}, {})
    spfdata['title'] = 'Create a set of QA tasks'

    if not self.session.get('email'):
      webapp2.redirect('/', abort=True)
      return

    self.response.write(self.render('set-create', templatedata, spfdata, self.request.get('spf')))


class SetView(BaseHandler):
  def get(self, id, slug): #TODO remove these args
    templatedata, spfdata  =  ({'full' : True}, {})
    
    db = DB(tables)
    set = db.set_get_one(id)
    urls = db.urls_get_by_set_id(id)
    #issues = db.issues_get_by_url_id(url_id)
    #comments = db.comments_get_by_issue_id(issue_id)
    urls = [
    {
      'id': url['id'], 
      'url': url['url'], 
      'issues': [
      {
        'id': issue['id'], 
        'description': issue['description'], 
        'status': issue['status'], 
        'author': issue['author'],
        'insert_date': issue['insert_date'],
        #'comments': [
        #  {
        #    'id': comment['id'],
        #    'comment': comment['comment'],
        #    'author': comment['author'],
        #    'insert_date': comment['insert_date']
        #  } for comment in db.comments_get_by_issue_id(issue['id'])
        #]
      } for issue in db.issues_get_by_url_id(url['id'])
    ]} for url in urls]
    
    #[{'id': str(result[0]), 'url':result[1]} for result in results.fetchall()]
    

    
    templatedata['name'] = set[1]
    templatedata['description'] = set[3]
    templatedata['urls'] = urls
    
    self.response.write(self.render('set-view', templatedata, spfdata, self.request.get('spf')))

  def post(self):
    pass


class Dashboard(BaseHandler):

  def get(self):
    templatedata, spfdata  =  ({'full' : True}, {})
    spfdata['title'] = '$ fcheck : Dashboard'
    self.response.write(self.render('dashboard', templatedata, spfdata, self.request.get('spf')))

class APISetCreate(BaseHandler):
  def get(self):
    pass

  def post(self):
    

    if not self.session.get('email'):
      
      return

    try:
      name = self.request.get('name')
      urls = self.request.get('urls')
      description = self.request.get('description')
      qaers = self.request.get('qaers')            
    except:
      self.response.write( jsonify({'result':'error', 'message': 'couldnt create set'}) )
      return

    db = DB(tables)
    
    slug_name = slugify(name)
    last_id = db.set_insert(name, slug_name , description, qaers, self.session.get('name'))
    if last_id:
      urls_list = urls.split('\n')
      urls = []
      for url in urls_list:
        urls.append({'url' : url.strip(), 'set_id' : last_id})

      db.urls_insert(last_id, urls)   

    self.response.write( jsonify({'result':'success', 'set_id': last_id, 'slug': slug_name}) )


class APIUrlFetch(BaseHandler):
  def get(self, set_id):
    """
    Fetches urls and their issuses (with their comments).
    @param set_id: The Set's ID the urls belong to
    """    
    db = DB(tables)
    all_urls = db.urls_get_by_set_id(set_id)
    
    
    #issues = db.issues_get_by_url_id(url_id)
    #comments = db.comments_get_by_issue_id(issue_id)
    urls = [
    {
      'id': url['id'], 
      'url': url['url'], 
      'issues': [
      {
        'id': issue['id'], 
        'description': issue['description'], 
        'status': issue['status'], 
        'author': issue['author'],
        'comments': db.comments_get_by_issue_id(issue['id'])
        #'insert_date': issue['insert_date'],
        
      } for issue in db.issues_get_by_url_id(url['id'])
    ]} for url in all_urls]
    
    self.response.write( jsonify({'result': 'success', 'payload':urls}) )

  def post(self):
    pass


class APIIssueCreate(BaseHandler):
  def get(self):
    pass

  def post(self):
    url_id = self.request.get('urlid')
    description = self.request.get('description')
    files = self.request.POST.getall('files[]')    
    
    # Store bucket filespaths here - will go into database
    db_files = []

    # TODO sanitize file name
    if files:
      bucket = os.environ.get('BUCKET_NAME', app_identity.get_default_gcs_bucket_name())
      

      for f in files:
        filename = f.filename
        content = f.file.getvalue()      
        bucket_filepath = '/%s/%s' % (bucket, filename)  
        gcs_file = gcs.open(bucket_filepath, 'w', options={'x-goog-acl': 'public-read'})
        gcs_file.write(content)
        gcs_file.close()
        gcs_url = 'https://storage.googleapis.com/%(bucket)s/%(file)s' % {'bucket':bucket, 'file':f.filename}
        db_files.append(gcs_url)

    
    # TODO validate above
    if len(description) < 5:
      self.response.write( jsonify({'result':'error', 'payload': 'description is too short'}) )
      return
    
    db = DB(tables)
    if db.issues_insert(url_id, description):
      self.response.write( jsonify({'result':'success', 'payload': {
        'url_id': url_id, 'description': description, 'author': 'mmanii', 'comments': []
      }}) )
    else:
      self.response.write( jsonify({'result':'error', 'payload': 'couldnt create issue'}) )


class APICommentCreate(BaseHandler):
  def get(self):
    pass

  def post(self):
    issue_id = self.request.get('issueId')
    comment = self.request.get('comment')    

    if len(comment) < 2:
      self.response.write( jsonify({'result':'error', 'payload': 'description is too short'}) )
      return

    if issue_id == None:
      return

    db = DB(tables)
    if db.comment_insert(issue_id, comment, 'mani'):
      self.response.write(jsonify({
        'result':'success',
        'payload': {
          'issue_id': issue_id,
          'comment': comment,
          'author': 'mmanii'
          }
        })
      )
      
    else:
      self.response.write(jsonify({
        'result':'error',
        'payload': 'could not add your comment'
      }))


class APIStatusUpdate(BaseHandler):
  def get(self):
    pass

  def post(self):

    issue_id = self.request.get('issueid')
    status = self.request.get('status')
    print '-----'
    print issue_id
    print status
    print '------'
    valid_statuses = ['open', 'ignore', 'fixed', 'worked on', 'not fixed']
    if status not in valid_statuses or issue_id == None or not issue_id.isdigit():
      self.response.write( jsonify({'result':'error', 'payload': 'invalid status'}) )
      return

    


    db = DB(tables)
    if db.status_update(issue_id, status):
      self.response.write( jsonify({'result':'success'}) )
      
    else:
      self.response.write( jsonify({'result':'error', 'payload': 'could not add your comment'}) )


class Login(BaseHandler):
  def get(self):
    templatedata, spfdata  =  ({'full' : True}, {})
    spfdata['title'] = 'Create a set of QA tasks'
    self.response.write(self.render('login', templatedata, spfdata, self.request.get('spf')))
    

  def post(self):
    pass


class APIUserLogin(BaseHandler):
  def get(self):
    pass    

  def post(self):
    email = self.request.get('email')
    password = self.request.get('password')
    # TODO validate
    if email == None or email == '':
      return
    if password == None or password == '':
      return
    
    
    db = DB(tables)
    row = db.user_login(email, password)

    if  row is None:
      self.response.write(jsonify({
        'result':'error',
        'payload': 'Wrong email & password'          
      }))
      return
    else:
      self.session['name'] = row[1]
      self.session['email'] = email
      self.response.write(jsonify({'result':'success'}))
      return
    

class APIUserRegister(BaseHandler):
  def get(self):
    pass    

  def post(self):
    name = self.request.get('name')
    email = self.request.get('email')
    password = self.request.get('password')

    # TODO validate
    if name == None or name == '' or len(name.split(' ')) != 2:
      return
    if email == None or email == '':
      return
    if password == None or password == '':
      return

    # TODO make sure email doesnt exist
    
    
    db = DB(tables)
    if  db.user_create(name, email, password) is None:
      self.response.write(jsonify({
        'result':'error',
        'payload': 'An error happened'          
      }))
      return
    else:
      self.session['name'] = name
      self.session['email'] = email
      self.response.write(jsonify({'result':'success'}))
      return


class APIFetchQaersList(BaseHandler):
  def get(self):
    
    db = DB(tables)
    qaers = [ [qa[0], qa[1]] for qa in db.qaers_fetch_all() ]   
    
    if qaers:
      self.response.write( jsonify({'result': 'success', 'payload':qaers}) )   
    

class Logout(BaseHandler):
  def get(self):
    self.session.clear()
    webapp2.redirect('/', abort=True)
    return
  

routes = [
  ('/', Home),
  ('/login', Login),
  ('/logout', Logout),
  ('/create-set', SetCreate),
  ('/set/(\d+)/(.+)', SetView),
  ('/dashboard', Dashboard),
  ('/api/create-set', APISetCreate),
  ('/api/create-issue', APIIssueCreate),
  ('/api/create-comment', APICommentCreate),
  ('/api/fetch-url/(\d+)', APIUrlFetch),
  ('/api/update-status', APIStatusUpdate),
  ('/api/user-login', APIUserLogin),
  ('/api/user-register', APIUserRegister),
  ('/api/qaers-list', APIFetchQaersList),
]

conf = {
   'webapp2_extras.sessions': {
     'secret_key': 'A0Zr98j/3yX~$HH!jmN]-LWX/,?RT',
     'cookie_args': {
     'max_age': 31104000
     }
   }
}


app = webapp2.WSGIApplication(routes=routes, config=conf, debug=True)