diff --git a/SE_API/API.py b/SE_API/API.py
index 91cfb16..f83e020 100644
--- a/SE_API/API.py
+++ b/SE_API/API.py
@@ -6,6 +6,7 @@ from GithubAPI.GithubAPI import GitHubAPI_Keys
from google.appengine.ext import db
import requests
import uuid
+import datetime
from flask import Flask, request, render_template, redirect, abort, Response
@@ -19,15 +20,23 @@ from models.Course import Course
from models.Project import Project
from models.Campus import Campus
-#Validation Utils Libs
+# All API
from SE_API.Validation_Utils import *
from SE_API.Respones_Utils import *
+from SE_API.Email_Utils import *
+
+from SE_API.UserRoutes import user_routes
+from SE_API.CampusRoutes import campus_routes
+from SE_API.CourseRoutes import course_routes
+from SE_API.ProjectRoutes import project_routes
app = Flask(__name__, static_folder='../templates')
+
+
githubKeys = GitHubAPI_Keys()
app.config['GITHUB_CLIENT_ID'] = githubKeys.getId()
@@ -35,6 +44,12 @@ app.config['GITHUB_CLIENT_SECRET'] = githubKeys.getSecret()
github = GitHub(app)
cross = CORS(app)
+
+app.register_blueprint(user_routes)
+app.register_blueprint(campus_routes)
+app.register_blueprint(course_routes)
+app.register_blueprint(project_routes)
+
auto = Autodoc(app)
@app.errorhandler(404)
@@ -108,23 +123,18 @@ def send_activation(token):
403 - Invalid Token
"""
if not request.data:
- return Response(response=json.dumps({'message': 'Bad Request'}),
- status=400,
- mimetype="application/json")
+ return bad_request()
payload = json.loads(request.data)
if not is_user_token_valid(token):
- return Response(response=json.dumps({'message': 'Not A Valid Token!'}),
- status=403,
- mimetype="application/json")
+ return forbidden("Not A Valid Token!")
+
query = User.all()
query.filter('seToken =', token)
for u in query.run(limit=1):
try:
send_validation_email(token=token, name=u.username, email=payload["email"])
except Exception:
- return Response(response=json.dumps({'message': 'Bad Request'}),
- status=400,
- mimetype="application/json")
+ return bad_request()
return Response(status=200)
@@ -132,6 +142,10 @@ def send_activation(token):
def documentation():
return auto.html()
+# @app.route('/api/help/campuses')
+# def documentation():
+# return auto.html()
+
@app.route('/home')
def returnHome():
try:
@@ -141,51 +155,6 @@ def returnHome():
-@app.route('/api/getUserByToken/', methods=["GET"])
-@auto.doc()
-def getUserByToken(token):
- '''
- This Function is will Activate a user and add tha campus to it
-
- Route Parameters
- - validation_token: 'seToken|email_suffix'
-
-
- Payload
- - NONE
-
-
- Response
-
- 200 - JSON Example:
-
- {
- 'username' : 'github_username',
- 'name' : 'Bob Dylan',
- 'email' : 'email@domain.com',
- 'isLecturer' : true,
- 'seToken' : 'dds2d-sfvvsf-qqq-fdf33-sfaa',
- 'avatar_url' : 'http://location.domain.com/image.jpg',
- 'isFirstLogin' : false,
- 'campuses_id_list': ['22314','243512',...,'356'],
- 'classes_id_list': ['22314','243512',...,'356']
- }
-
-
- 403 - Invalid Token
- '''
- query = User.all()
- query.filter("seToken = ", token)
-
- for u in query.run(limit=5):
- return Response(response=u.to_JSON(),
- status=201,
- mimetype="application/json") # Real response!
-
- return Response(response=json.dumps({'message' : 'No User Found'}),
- status=400,
- mimetype="application/json")
-
@app.route('/githubOAuth')
@@ -229,162 +198,6 @@ def oauth(oauth_token):
return cookieMonster(user.seToken)
-@app.route('/api/Campuses/create/', methods=['POST'])
-@auto.doc()
-def create_campus(token):
- """
- This call will create a new campus in the DB
-
- Route Parameters
- - seToken: 'seToken'
-
-
- Payload
- - JSON Object, Example:
- {
- 'title': 'Campus name',
- 'email_ending': '@campus.ac.com',
- 'avatar_url': 'http://location.domain.com/image.jpg'
- }
-
-
- Response
-
- 201 - Created
-
- 403 - Invalid Token/Forbidden
- """
- if not request.data:
- return Response(response=json.dumps({'message': 'Bad Request'}),
- status=400,
- mimetype="application/json")
- payload = json.loads(request.data)
- if not is_lecturer(token): #todo: change to lecturer id
- return Response(response=json.dumps({'message': 'Invalid token or not a lecturer!'}),
- status=403,
- mimetype="application/json")
-
- user = get_user_by_token(token)
-
- #todo: check legality
-
- try:
- campus = Campus(title=payload['title'], email_ending=payload['email_ending'], master_user_id=user.key().id(), avatar_url=payload['avatar_url'])
- except Exception:
- return Response(response=json.dumps({'message': 'Bad Request'}),
- status=400,
- mimetype="application/json")
-
- db.put(campus)
- db.save
- return Response(response=json.dumps(campus.to_JSON()),
- status=201,
- mimetype="application/json")
-
-
-
-# @app.route('/api/Courses/create/', methods=['POST'])
-# @auto.doc()
-# def create_course(token):
-# """
-# This call will create a new campus in the DB
-# :param token: user seToken
-# Payload
-# {
-# 'courseName': self.courseName,
-# 'campusName': self.campusName,
-# 'projects': self.projects
-# 'startDate': self.startDate
-# 'endDate': self.endDate
-# 'taskFlag': self.taskFlag
-# }
-#
-# :return:
-# code 200
-# """
-# if not request.data:
-# return Response(response=json.dumps({'message': 'Bad Request'}),
-# status=400,
-# mimetype="application/json")
-# payload = json.loads(request.data)
-# if not is_lecturer(token): #todo: change to lecturer id
-# return Response(response=json.dumps({'message': 'Invalid token or not a lecturer!'}),
-# status=403,
-# mimetype="application/json")
-#
-# user = get_user_by_token(token)
-#
-# #todo: check legality
-#
-#
-# try:
-# course = Course(courseName=payload['courseName'], campusName=payload['campusName'], projects=payload['projects'], startDate=payload['startDate'], endDate=payload['endDate'], taskFlag=payload['taskFlag'])
-# except Exception:
-# return Response(response=json.dumps({'message': 'Bad Request'}),
-# status=400,
-# mimetype="application/json")
-#
-# db.put(course)
-# db.save
-# return Response(response=json.dumps(course.to_JSON()),
-# status=200,
-# mimetype="application/json")
-#
-#
-#
-
-@app.route('/api/Campuses/', methods=['GET'])
-@auto.doc()
-def get_campuses(token):
- """
- This Call will return an array of all Campuses available
-
- Route Parameters
- - seToken: 'seToken'
-
-
- Payload
- - NONE
-
-
- Response
-
- 200 - JSON Array, Example:
- [
- {
- 'title': 'JCE',
- 'email_ending': '@post.jce.ac.il',
- 'master_user_id': 123453433341, (User that created the campus)
- 'avatar_url': 'http://some.domain.com/imagefile.jpg'
- },
- ....
- {
- ...
- }req
- ]
-
- 403 - Invalid Token
- 500 - Server Error
- """
- if is_user_token_valid(token):
- arr = []
- query = Campus.all()
- for c in query.run():
- arr.append(dict(json.loads(c.to_JSON())))
- print arr
- if len(arr) != 0:
- return Response(response=json.dumps(arr),
- status=200,
- mimetype="application/json")
- else:
- return Response(response=[],
- status=200,
- mimetype="application/json")
- else:
- return Response(response=json.dumps({'message': 'Invalid Token'}),
- status=403,
- mimetype="application/json")
-
@app.route('/login')
@@ -433,3 +246,5 @@ def cookieMonster(uid):
response = app.make_response(redirect_to_home )
response.set_cookie('com.sehub.www',value=uid)
return response
+
+
diff --git a/SE_API/CampusRoutes.py b/SE_API/CampusRoutes.py
new file mode 100644
index 0000000..8a5d5d9
--- /dev/null
+++ b/SE_API/CampusRoutes.py
@@ -0,0 +1,135 @@
+__author__ = 'Aran'
+
+from flask import Blueprint
+import json
+from GithubAPI.GithubAPI import GitHubAPI_Keys
+
+from google.appengine.ext import db
+import requests
+import datetime
+
+from flask import Flask, request, render_template, redirect, abort, Response
+
+from flask.ext.github import GitHub
+from flask.ext.cors import CORS, cross_origin
+from flask.ext.autodoc import Autodoc
+
+# DB Models
+from models.Campus import Campus
+
+#Validation Utils Libs
+from SE_API.Validation_Utils import *
+from SE_API.Respones_Utils import *
+
+
+
+campus_routes = Blueprint("campus_routes", __name__)
+auto = Autodoc()
+
+@campus_routes.route('/api/campuses/create/', methods=['POST'])
+@auto.doc()
+def create_campus(token):
+ """
+ This call will create a new campus in the DB
+
+ Route Parameters
+ - seToken: 'seToken'
+
+
+ Payload
+ - JSON Object, Example:
+ {
+ 'title': 'Campus name',
+ 'email_ending': '@campus.ac.com',
+ 'avatar_url': 'http://location.domain.com/image.jpg'
+ }
+
+
+ Response
+
+ 201 - Created
+
+ 403 - Invalid Token/Forbidden
+ """
+ if not request.data:
+ return bad_request()
+ payload = json.loads(request.data)
+ if not is_lecturer(token): #todo: change to lecturer id
+ return forbidden("Invalid token or not a lecturer!")
+
+ user = get_user_by_token(token)
+
+ #todo: check legality
+
+ try:
+ campus = Campus(title=payload['title'], email_ending=payload['email_ending'], master_user_id=user.key().id(), avatar_url=payload['avatar_url'])
+ except Exception:
+ return bad_request()
+
+ db.put(campus)
+ db.save
+ return Response(response=campus.to_JSON(),
+ status=201,
+ mimetype="application/json")
+
+
+
+
+
+@campus_routes.route('/api/campuses/', methods=['GET'])
+@auto.doc()
+def get_campuses(token):
+ """
+ This Call will return an array of all Campuses available
+
+ Route Parameters
+ - seToken: 'seToken'
+
+
+ Payload
+ - NONE
+
+
+ Response
+
+ 200 - JSON Array, Example:
+ [
+ {
+ 'title': 'JCE',
+ 'email_ending': '@post.jce.ac.il',
+ 'master_user_id': 123453433341, (User that created the campus)
+ 'avatar_url': 'http://some.domain.com/imagefile.jpg'
+ },
+ ....
+ {
+ ...
+ }req
+ ]
+
+ 403 - Invalid Token
+ 500 - Server Error
+ """
+
+ if is_user_token_valid(token):
+ arr = []
+ query = Campus.all()
+ for c in query.run():
+ arr.append(dict(json.loads(c.to_JSON())))
+ print arr
+ if len(arr) != 0:
+ return Response(response=json.dumps(arr),
+ status=200,
+ mimetype="application/json")
+ else:
+ return Response(response=[],
+ status=200,
+ mimetype="application/json")
+ else:
+ return forbidden("Invalid Token")
+
+
+
+
+@campus_routes.route('/api/campuses/help')
+def documentation():
+ return auto.html()
\ No newline at end of file
diff --git a/SE_API/CourseRoutes.py b/SE_API/CourseRoutes.py
new file mode 100644
index 0000000..6b76f3b
--- /dev/null
+++ b/SE_API/CourseRoutes.py
@@ -0,0 +1,88 @@
+__author__ = 'Aran'
+
+from flask import Blueprint
+import json
+from GithubAPI.GithubAPI import GitHubAPI_Keys
+
+from google.appengine.ext import db
+import requests
+import datetime
+
+from flask import Flask, request, render_template, redirect, abort, Response
+
+from flask.ext.github import GitHub
+from flask.ext.cors import CORS, cross_origin
+from flask.ext.autodoc import Autodoc
+
+# DB Models
+from models.Course import Course
+
+#Validation Utils Libs
+from SE_API.Validation_Utils import *
+from SE_API.Respones_Utils import *
+
+
+
+course_routes = Blueprint("course_routes", __name__)
+auto = Autodoc()
+
+
+@course_routes.route('/api/courses/create/', methods=['POST'])
+@auto.doc()
+def create_course(token):
+ """
+ This call will create a new campus in the DB
+ :param token: user seToken
+ Payload
+ {
+ 'courseName': self.courseName,
+ 'campusName': self.campusName,
+ 'projects': self.projects
+ 'startDate': self.startDate
+ 'endDate': self.endDate
+ 'taskFlag': self.taskFlag
+ }
+
+ :return:
+ code 200
+ """
+ if not request.data:
+ return bad_request()
+ payload = json.loads(request.data)
+ if not is_lecturer(token): #todo: change to lecturer id
+ return forbidden("Invalid token or not a lecturer!")
+
+ user = get_user_by_token(token)
+
+ #todo: check legality
+
+
+ try:
+ start_date = datetime.date(payload['startDate']['year'],payload['startDate']['month'],payload['startDate']['day'])
+ end_date = datetime.date(payload['endDate']['year'],payload['endDate']['month'],payload['endDate']['day'])
+
+ course = Course(courseName=payload['courseName'], campusName=payload['campusName'],
+ startDate=start_date, endDate=end_date)
+
+ try:
+ course.projects=payload['projects']
+ except Exception:
+ pass
+
+
+ except Exception:
+ return bad_request()
+
+
+
+ db.put(course)
+ db.save
+ return Response(response=course.to_JSON(),
+ status=201,
+ mimetype="application/json")
+
+
+
+@course_routes.route('/api/courses/help')
+def documentation():
+ return auto.html()
\ No newline at end of file
diff --git a/SE_API/Email_Utils.py b/SE_API/Email_Utils.py
new file mode 100644
index 0000000..683f7e8
--- /dev/null
+++ b/SE_API/Email_Utils.py
@@ -0,0 +1,127 @@
+__author__ = 'sagi'
+
+
+from google.appengine.api import mail
+
+
+def send_validation_email(token, email, name):
+ emailSuffix = str(email).split('@')[1]
+ message = mail.EmailMessage(sender="SE-Hub Support ",
+ subject="SE-Hub Activate Account")
+
+ message.to = email
+
+ message.body = """
+ Dear """+name+""":
+
+ To Activate your SE-Hub Account please click on the link below:
+ http://se-hub.appspot.com/api/validation/confirm/"""+token+"""|"""+emailSuffix+"""
+ to get access to your Campus :)
+
+ Please let us know if you have any questions.
+
+ SE-Hub (c) 2015 niptop Team.
+ """
+
+ message.html = """
+
+
+
+
+
+
+
+
+
Hey """+name+"""- Just one More Step...
+
Dear """+name+""":
+
+ To Activate your SE-Hub Account please click on the link below:
+ http://se-hub.appspot.com/api/validation/confirm/"""+token+"""|"""+emailSuffix+"""
+
+ to access you virtual class.
+
+
+ Please let us know if you have any questions.
+
+ SE-Hub (c) 2015 niptop Team.
+
+
+ """
+
+ message.send()
+
+def send_create_campus_request(email, name, campus_name):
+ message = mail.EmailMessage(sender="SE-Hub Support ",
+ subject="SE-Hub: "+campus_name+" Is Being Evaluated")
+
+ message.to = email
+
+ message.body = """
+ Dear """+name+""":
+
+ Thank You For Choosing SE-Hub!
+ Your Request for creating a new Campus named """+campus_name + """
+ is Being Evaluated.
+ You Will Receive an e-mail When We finish the process
+
+ Please let us know if you have any questions.
+
+ SE-Hub (c) 2015 niptop Team.
+ """
+
+ message.html = """
+
+
+
+
+
+
+
+
+
Thank You For Choosing SE-Hub!
+
Dear """+name+""":
+
+ Your Request for creating a new Campus named """+campus_name + """
+ is Being Evaluated.
+ You Will Receive an e-mail When We finish the process
+
+ to access you virtual class.
+
+
+ Please let us know if you have any questions.
+
+ SE-Hub (c) 2015 niptop Team.
+
+
+ """
+ message.send()
+
+
+def notify_se_hub_campus_request(campus, campus_name):
+ message = mail.EmailMessage(sender="SE-Hub Support ",
+ subject="SE-Hub: "+campus_name+" Is Being Evaluated")
+
+ message.to = 'se-hub@appspot.gserviceaccount.com'
+
+ message.body = """
+ a new Campus request
+ """+str(campus.to_JSON())
+
+ message.html = """
+
+
+
+
+
+
+
+
+
New Campus!
+
+ """ + str(campus.to_JSON()) + """
+
+ SE-Hub (c) 2015 niptop Team.
+
+
+ """
+ message.send()
diff --git a/SE_API/ProjectRoutes.py b/SE_API/ProjectRoutes.py
new file mode 100644
index 0000000..ae089df
--- /dev/null
+++ b/SE_API/ProjectRoutes.py
@@ -0,0 +1,141 @@
+__author__ = 'Aran'
+
+from flask import Blueprint
+import json
+from GithubAPI.GithubAPI import GitHubAPI_Keys
+
+from google.appengine.ext import db
+import requests
+import datetime
+
+from flask import Flask, request, render_template, redirect, abort, Response
+
+from flask.ext.github import GitHub
+from flask.ext.cors import CORS, cross_origin
+from flask.ext.autodoc import Autodoc
+
+# DB Models
+from models.Project import Project
+
+#Validation Utils Libs
+from SE_API.Validation_Utils import *
+from SE_API.Respones_Utils import *
+
+
+
+project_routes = Blueprint("project_routes", __name__)
+auto = Autodoc()
+
+@project_routes.route('/api/projects/Create//', methods=['POST'])
+@auto.doc()
+def create_project(token,id):
+ """
+ This call will create a new project in the DB
+
+ Route Parameters
+ - seToken: 'seToken'
+
+
+ Payload
+ - JSON Object, Example:
+ {
+ 'title': 'Campus name',
+ 'email_ending': '@campus.ac.com',
+ 'avatar_url': 'http://location.domain.com/image.jpg'
+ }
+
+
+ Response
+
+ 201 - Created
+
+ 403 - Invalid Token/Forbidden
+ """
+ if not request.data:
+ return bad_request()
+ payload = json.loads(request.data)
+ if not is_lecturer(token): #todo: change to lecturer id
+ return forbidden("Invalid token or not a lecturer!")
+
+ user = get_user_by_token(token)
+
+ #todo: check legality
+
+ try:
+ project = Project(projectName=payload['projectName'], masterId=user.key().id(), gitRepository=payload['gitRepository'], membersId=[token])
+ except Exception as e:
+ print e
+ return bad_request()
+
+ db.put(project)
+ db.save
+ return Response(response=project.to_JSON(),
+ status=201,
+ mimetype="application/json")
+
+
+
+
+
+@project_routes.route('/api/projects/get/', methods=['GET'])
+@auto.doc()
+def get_projects(token):
+ """
+ This Call will return an array of all projects available
+
+ Route Parameters
+ - seToken: 'seToken'
+
+
+ Payload
+ - NONE
+
+
+ Response
+
+ 200 - JSON Array, Example:
+ [
+ {
+ 'title': 'JCE',
+ 'email_ending': '@post.jce.ac.il',
+ 'master_user_id': 123453433341, (User that created the campus)
+ 'avatar_url': 'http://some.domain.com/imagefile.jpg'
+ },
+ ....
+ {
+ ...
+ }req
+ ]
+
+ 403 - Invalid Token
+ 500 - Server Error
+ """
+ if is_user_token_valid(token):
+ arr = []
+ query = Campus.all()
+ for c in query.run():
+ arr.append(dict(json.loads(c.to_JSON())))
+ print arr
+ if len(arr) != 0:
+ return Response(response=json.dumps(arr),
+ status=200,
+ mimetype="application/json")
+ else:
+ return Response(response=[],
+ status=200,
+ mimetype="application/json")
+ else:
+ return forbidden("Invalid Token")
+
+
+
+@project_routes.route('/api/Projects/', methods=['GET'])
+@auto.doc()
+def get_campuses(token):
+ pass
+
+
+
+@project_routes.route('/api/projects/help')
+def documentation():
+ return auto.html()
\ No newline at end of file
diff --git a/SE_API/Respones_Utils.py b/SE_API/Respones_Utils.py
index b789974..3fe0e3f 100644
--- a/SE_API/Respones_Utils.py
+++ b/SE_API/Respones_Utils.py
@@ -3,6 +3,29 @@ __author__ = 'Aran'
from flask import Response
import json
+
+def ok(message='OK'):
+ return Response(response=json.dumps({'message': message}),
+ status=200,
+ mimetype="application/json")
+
+def created(message='Created'):
+ return Response(response=json.dumps({'message': message}),
+ status=201,
+ mimetype="application/json")
+
+def accepted(message='Accepted'):
+ return Response(response=json.dumps({'message': message}),
+ status=202,
+ mimetype="application/json")
+
+
+def no_content(message='No Content'):
+ return Response(response=json.dumps({'message': message}),
+ status=204,
+ mimetype="application/json")
+
+
def bad_request(message='Bad Request'):
return Response(response=json.dumps({'message': message}),
status=400,
diff --git a/SE_API/UserRoutes.py b/SE_API/UserRoutes.py
new file mode 100644
index 0000000..ddb5202
--- /dev/null
+++ b/SE_API/UserRoutes.py
@@ -0,0 +1,75 @@
+__author__ = 'Aran'
+
+from flask import Blueprint
+import json
+from GithubAPI.GithubAPI import GitHubAPI_Keys
+
+from google.appengine.ext import db
+import requests
+import datetime
+
+from flask import Flask, request, render_template, redirect, abort, Response
+
+from flask.ext.github import GitHub
+from flask.ext.cors import CORS, cross_origin
+from flask.ext.autodoc import Autodoc
+
+# DB Models
+from models.User import User
+
+#Validation Utils Libs
+from SE_API.Validation_Utils import *
+from SE_API.Respones_Utils import *
+
+
+user_routes = Blueprint("user_routes", __name__)
+auto = Autodoc()
+
+
+@user_routes.route('/api/users/getUserByToken/', methods=["GET"])
+@auto.doc()
+def getUserByToken(token):
+ '''
+ This Function is will Activate a user and add tha campus to it
+
+ Route Parameters
+ - validation_token: 'seToken|email_suffix'
+
+
+ Payload
+ - NONE
+
+
+ Response
+
+ 200 - JSON Example:
+
+ {
+ 'username' : 'github_username',
+ 'name' : 'Bob Dylan',
+ 'email' : 'email@domain.com',
+ 'isLecturer' : true,
+ 'seToken' : 'dds2d-sfvvsf-qqq-fdf33-sfaa',
+ 'avatar_url' : 'http://location.domain.com/image.jpg',
+ 'isFirstLogin' : false,
+ 'campuses_id_list': ['22314','243512',...,'356'],
+ 'classes_id_list': ['22314','243512',...,'356']
+ }
+
+
+ 403 - Invalid Token
+ '''
+ query = User.all()
+ query.filter("seToken = ", token)
+
+ for u in query.run(limit=5):
+ return Response(response=u.to_JSON(),
+ status=200,
+ mimetype="application/json") # Real response!
+
+ return bad_request("No User Found")
+
+
+@user_routes.route('/api/users/help')
+def documentation():
+ return auto.html()
\ No newline at end of file
diff --git a/SE_API/Validation_Utils.py b/SE_API/Validation_Utils.py
index e99b2bd..90df681 100644
--- a/SE_API/Validation_Utils.py
+++ b/SE_API/Validation_Utils.py
@@ -2,7 +2,7 @@ __author__ = 'sagi'
from google.appengine.ext import db
from models.User import User
from models.Campus import Campus
-from google.appengine.api import mail
+
def get_user_by_token(token):
@@ -32,50 +32,3 @@ def is_lecturer(token):
if user is None:
return False
return user.isLecturer
-
-
-def send_validation_email(token, email, name):
- emailSuffix = str(email).split('@')[1]
- message = mail.EmailMessage(sender="SE-Hub Support ",
- subject="SE-Hub Activate Account")
-
- message.to = email
-
- message.body = """
- Dear """+name+""":
-
- To Activate your SE-Hub Account please click on the link below:
- http://se-hub.appspot.com/api/validation/confirm/"""+token+"""|"""+emailSuffix+"""
- to get access to your Campus :)
-
- Please let us know if you have any questions.
-
- SE-Hub (c) 2015 niptop Team.
- """
-
- message.html = """
-
-
-
-
-
-
-
-
-
Hey """+name+"""- Just one More Step...
-
Dear """+name+""":
-
- To Activate your SE-Hub Account please click on the link below:
- http://se-hub.appspot.com/api/validation/confirm/"""+token+"""|"""+emailSuffix+"""
-
- to access you virtual class.
-