Merge branch 'master' of https://github.com/sagidayan/SE-Hub into UI
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
cron:
|
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
|
|
@ -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()
|
||||||
|
|
BIN
templates/android-icon-144x144.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
templates/android-icon-192x192.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
templates/android-icon-36x36.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
templates/android-icon-48x48.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
templates/android-icon-72x72.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
templates/android-icon-96x96.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
templates/apple-icon-114x114.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
templates/apple-icon-120x120.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
templates/apple-icon-144x144.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
templates/apple-icon-152x152.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
templates/apple-icon-180x180.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
templates/apple-icon-57x57.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
templates/apple-icon-60x60.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
templates/apple-icon-72x72.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
templates/apple-icon-76x76.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
templates/apple-icon-precomposed.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
templates/apple-icon.png
Normal file
After Width: | Height: | Size: 21 KiB |
2
templates/browserconfig.xml
Normal 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>
|
|
@ -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
After Width: | Height: | Size: 1.2 KiB |
BIN
templates/favicon-32x32.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
templates/favicon-96x96.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
templates/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
|
@ -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">
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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);
|
||||||
}).error(function(err){
|
$scope.loading = false;
|
||||||
console.error('Error: ', err);
|
}).error(function(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()
|
||||||
|
@ -119,7 +136,7 @@ angular.module('SeHub')
|
||||||
// }]
|
// }]
|
||||||
// };
|
// };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$scope.dueTime = function() {
|
$scope.dueTime = function() {
|
||||||
if (!$scope.task.date || $scope.task.date === '')
|
if (!$scope.task.date || $scope.task.date === '')
|
||||||
|
|
|
@ -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
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
templates/ms-icon-144x144.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
templates/ms-icon-150x150.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
templates/ms-icon-310x310.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
templates/ms-icon-70x70.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
|
@ -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%">
|
||||||
|
|