Merge branch 'master' of https://github.com/sagidayan/SE-Hub into UI

This commit is contained in:
Matan Bar Yosef 2015-08-02 17:51:21 +03:00
commit 7a6d71edcd
38 changed files with 221 additions and 13 deletions

View file

@ -125,3 +125,61 @@ def notify_se_hub_campus_request(campus, campus_name):
</html> </html>
""" """
message.send() message.send()
def send_task_reminder( email, name, task_name, course_name):
message = mail.EmailMessage(sender="SE-Hub Support <se-hub@appspot.gserviceaccount.com>",
subject="Hey! Due Date for task " + task_name + " is TOMORROW!")
message.to = email
message.body = """
Dear """+name+""":
You Have A Task that Due tomorrow! don't forget to submit the task.
Task name: """ + task_name + """ \n
In Course: """ + course_name + """ \n\n
http://se-hub.appspot.com
Please let us know if you have any questions.
SE-Hub (c) 2015 niptop Team.
"""
message.html = """
<html><head></head><body>
<div>
<center>
<img src='https://cloud.githubusercontent.com/assets/2984053/6825467/7c9d0402-d303-11e4-9827-62a6d66f937a.png'>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" type="text/css" rel="stylesheet" />
</center>
</div>
<div style='width:70%'>
<h3>Dear """+name+""":</h3>
<center>
<img src='https://pixabay.com/static/uploads/photo/2014/03/05/07/54/reminder-279903_640.png'>
</center>
<br>
You Have A Task that Due tomorrow! don't forget to submit the task.<br>
Task name: """ + task_name + """ <br>
In Course: """ + course_name + """ <br>
<br>
<a href='http://se-hub.appspot.com/'>SE - Hub</a>
</div>
<br><br>
Please let us know if you have any questions.
<br>
SE-Hub (c) 2015 niptop Team.
</body>
</html>
"""
message.send()

View file

@ -377,10 +377,10 @@ def deleteProject(token,projectId):
#remove all users related to project #remove all users related to project
for uId in p.membersId: for uId in p.membersId:
user = User.get_by_id(uId) user = User.get_by_id(int(uId))
if user is None: if user is None:
return bad_request("trying to remove a user from project failed") return bad_request("trying to remove a user from project failed")
user.projects_id_list.remove(p.key().id()) user.projects_id_list.remove(str(p.key().id()))
db.put(user) db.put(user)

View file

@ -20,10 +20,12 @@ from models.Task import Task
from models.Course import Course from models.Course import Course
from models.TaskComponent import TaskComponent from models.TaskComponent import TaskComponent
from models.TaskGrade import TaskGrade from models.TaskGrade import TaskGrade
from models.Project import Project
#Validation Utils Libs #Validation Utils Libs
from SE_API.Validation_Utils import * from SE_API.Validation_Utils import *
from SE_API.Respones_Utils import * from SE_API.Respones_Utils import *
from Email_Utils import *
task_routes = Blueprint("task_routes", __name__) task_routes = Blueprint("task_routes", __name__)
@ -200,13 +202,25 @@ def submitTask(token, taskId, ownerId):
payload = json.loads(request.data) payload = json.loads(request.data)
user = get_user_by_token(token) user = get_user_by_token(token)
if user is None:
bad_request("bad user Token")
task = Task.get_by_id(int(taskId)) task = Task.get_by_id(int(taskId))
if task is None:
bad_request("bad Task id")
if task.isPersonal:
if User.get_by_id(int(ownerId)) is None:
return bad_request("no such user")
else:
if Project.get_by_id(int(ownerId)) is None:
return bad_request("no such project")
#create components #create components
for c in payload['components']: for c in payload:
try: try:
component = TaskComponent(taskId=task.key().id(), userId=(int(ownerId)), type=c['type'], label=c['label'], isMandatory=c['isMandatory'], order=c['order']) component = TaskComponent(taskId=task.key().id(), userId=(int(ownerId)), type=c['type'], label=c['label'], value=c['value'], isMandatory=c['isMandatory'], order=c['order'])
except Exception as e: except Exception as e:
print e print e
return bad_request("Bad component") return bad_request("Bad component")
@ -678,6 +692,12 @@ def getTaskById(token, taskId, ownerId):
task = Task.get_by_id(int(taskId)) task = Task.get_by_id(int(taskId))
if task is None: if task is None:
return bad_request("Bad Task id") return bad_request("Bad Task id")
if task.isPersonal:
if User.get_by_id(int(ownerId)) is None:
return bad_request("no such user")
else:
if Project.get_by_id(int(ownerId)) is None:
return bad_request("no such project")
task = json.loads(task.to_JSON()) task = json.loads(task.to_JSON())
task['components'] = [] task['components'] = []
@ -847,6 +867,38 @@ def documentation():
return auto.html() return auto.html()
@task_routes.route('/api/tasks/sendTaskReminder', methods=['GET'])
def sendTaskReminder():
tasks = Task.all()
try:
for t in tasks.run():
if t.dueDate == datetime.date.today() + datetime.timedelta(days=1):
course = Course.get_by_id(int(t.courseId))
if t.isPersonal:
for uId in course.membersId:
tc = TaskComponent.all().filter("taskId = ", t.key().id()).filter("userId = ", int(uId))
if tc.count() == 0:
user = User.get_by_id(int(uId))
send_task_reminder(user.email, user.name, t.title, course.courseName)
print ""
else:
projects = Project.all().filter("courseId = ", course.key().id())
for p in projects.run():
tc = TaskComponent.all().filter("taskId = ", t.key().id()).filter("userId = ", p.key().id())
if tc.count() == 0:
for uId in p.membersId:
user = User.get_by_id(int(uId))
send_task_reminder(user.email, user.name, t.title, course.courseName)
return accepted()
except:
return bad_request()

View file

@ -2,3 +2,6 @@ cron:
- description: update project data - description: update project data
url: /api/projects/updateProjectsInfo url: /api/projects/updateProjectsInfo
schedule: every 1 hours schedule: every 1 hours
- description: send task notification mail
url: /api/tasks/sendTaskReminder
schedule: every day 03:00

View file

@ -10,6 +10,7 @@ class TaskComponent(db.Model):
userId = db.IntegerProperty(required=True, default = -1) userId = db.IntegerProperty(required=True, default = -1)
type = db.StringProperty(required=True,default=" ") type = db.StringProperty(required=True,default=" ")
label = db.StringProperty(required=True,default=" ") label = db.StringProperty(required=True,default=" ")
value = db.StringProperty(required=True, default=" ")
isMandatory = db.BooleanProperty(required=True, default=True) isMandatory = db.BooleanProperty(required=True, default=True)
order = db.IntegerProperty(required=True) order = db.IntegerProperty(required=True)
@ -18,6 +19,7 @@ class TaskComponent(db.Model):
'userId' : self.userId, 'userId' : self.userId,
'type' : self.type, 'type' : self.type,
'label' : self.label, 'label' : self.label,
'value' : self.value,
'isMandatory' : self.isMandatory, 'isMandatory' : self.isMandatory,
'order' : self.order, 'order' : self.order,
'id' : self.key().id() 'id' : self.key().id()

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
templates/apple-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="templates/ms-icon-70x70.png"/><square150x150logo src="templates/ms-icon-150x150.png"/><square310x310logo src="templates/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>

View file

@ -127,6 +127,10 @@ body.noscroll
margin-bottom: auto; margin-bottom: auto;
} }
.center_all{
margin:auto;
}
.mail_suffix .mail_suffix
{ {
margin-top: auto; margin-top: auto;

BIN
templates/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
templates/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
templates/favicon-96x96.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
templates/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,6 +1,23 @@
<html lang="en" > <html lang="en" >
<head> <head>
<link rel="apple-touch-icon" sizes="57x57" href="templates/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="templates/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="templates/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="templates/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="templates/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="templates/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="templates/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="templates/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="templates/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="templates/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="templates/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="templates/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="templates/favicon-16x16.png">
<link rel="manifest" href="templates/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="templates/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/0.8.3/angular-material.min.css"> <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/0.8.3/angular-material.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:300,400,500,700,400italic"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:300,400,500,700,400italic">

View file

@ -133,7 +133,7 @@ angular.module('SeHub').controller('newTasksController', ['$scope', 'apiService'
$scope.compDetails = [{ $scope.compDetails = [{
detail: "Label" detail: "Label"
}, { }, {
detail: "URL Path" detail: "URL Path (You Need To Include 'http://'"
}]; }];
if (type === 'radiobuttons') if (type === 'radiobuttons')

View file

@ -8,12 +8,14 @@ angular.module('SeHub')
var submitterId = $routeParams.submitterId; var submitterId = $routeParams.submitterId;
var token = $cookies['com.sehub.www']; var token = $cookies['com.sehub.www'];
var groupId = $routeParams.gId; var groupId = $routeParams.gId;
$scope.loading = true;
apiService.getTaskById(token, taskId, groupId).success(function(data) { apiService.getTaskById(token, taskId, groupId).success(function(data) {
$scope.task = data; $scope.task = data;
$scope.dateInit($scope.task.dueDate); $scope.dateInit($scope.task.dueDate);
$scope.loading = false;
}).error(function(err) { }).error(function(err) {
console.error('Error: ', err); $location.path('/tasks');
}) })
if (submitterId) { //In This Case we Only Want to show The Content of the Submitter if (submitterId) { //In This Case we Only Want to show The Content of the Submitter
@ -71,8 +73,23 @@ angular.module('SeHub')
$scope.submitTask = function(event) { //Dialog will pop-up if not all mandatory fields are filled $scope.submitTask = function(event) { //Dialog will pop-up if not all mandatory fields are filled
if (validateComponents()) { if (validateComponents()) {
alert('All Shit Are Filled'); apiService.submitTask(token, taskId, groupId, $scope.task.components).success(function(data) {
return; $mdDialog.show(
$mdDialog.alert()
.title('Submitted!')
.content('Your Task Was Successfully Submitted!')
.ariaLabel('ddd')
.ok('GoTo My Submitted Task')
.then(function(dd){
if($scope.task.isPersonal)
$location.path('/tasks/overview/'+taskId+'/'+groupId+'/'+groupId);
else
$location.path('/tasks/overview/'+taskId+'/'+groupId+'/'+groupId);
})
.targetEvent(event)
);
})
} }
$mdDialog.show( $mdDialog.show(
$mdDialog.alert() $mdDialog.alert()

View file

@ -262,6 +262,15 @@ service.factory('apiService', ['$http', function($http) {
}; };
return $http(req); return $http(req);
},
submitTask: function(token, taskId, ownerId, payload){
var url = (DEBUG ? "http://localhost:8080" : "http://se-hub.appspot.com") + "/api/tasks/submitTask/" + token + '/' + taskId + '/' + ownerId;
req = {
method: 'POST',
data: payload,
url: url
};
return $http(req);
} }
}; };
}]); }]);

41
templates/manifest.json Normal file
View file

@ -0,0 +1,41 @@
{
"name": "App",
"icons": [
{
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
templates/ms-icon-70x70.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -1,4 +1,7 @@
<div layout='row'> <div ng-if='loading' class='center_all'>
<md-progress-circular md-mode="indeterminate"></md-progress-circular>
</div>
<div layout='row' ng-if='!loading'>
<div flex='20'></div> <div flex='20'></div>
<div layout="coulumn" flex="60"> <div layout="coulumn" flex="60">
<md-card layout-padding style="width:100%"> <md-card layout-padding style="width:100%">