blob: d1b23ce0635756575e9f1e24b5582664255d7fff [file] [log] [blame]
# Copyright 2012 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""GCIOrgScore logic methods and queries."""
from google.appengine.ext import db
from google.appengine.ext import ndb
from soc.modules.gci.models.score import GCIOrgScore
from soc.modules.gci.models.task import GCITask
# TODO(daniel): this should be a parameter of the program
# determines the maximal position from which the student may be
# proposed to be a winner by organization admins
POSSIBLE_WINNER_MAX_POSITION = 5
def updateOrgScoreTxn(task, student):
"""Returns a transactional function that updates GCIOrgScore for
the specified task.
"""
org_key = GCITask.org.get_value_for_datastore(task)
def txn():
org_score_query = queryForAncestorAndOrg(student.key.to_old_key(), org_key)
org_score = org_score_query.get()
if not org_score:
org_score = GCIOrgScore(parent=student.key.to_old_key(), org=org_key)
org_score.tasks.append(task.key())
org_score.put()
return txn
#: TODO(daniel): implement unit tests for this function
def updateOrgScoresTxn(tasks):
"""Returns a transaction function that updates GCIOrgScore for the
specified list of tasks that belong to the same student.
"""
if not tasks:
return lambda: None
student_key = GCITask.student.get_value_for_datastore(tasks[0])
tasks_by_org = {}
for task in tasks:
# check if all the tasks belong to the same student
if GCITask.student.get_value_for_datastore(task) != student_key:
raise ValueError("Specified tasks belong to more than one student")
# check if the task is actually closed
if task.status != 'Closed':
raise ValueError("The task %d is not closed" % task.key().id())
org_key = GCITask.org.get_value_for_datastore(task)
if org_key not in tasks_by_org:
tasks_by_org[org_key] = [task]
else:
tasks_by_org[org_key].append(task)
def txn():
to_put = []
for org_key, tasks in tasks_by_org.iteritems():
query = queryForAncestorAndOrg(student_key, org_key)
org_score = query.get()
if not org_score:
org_score = GCIOrgScore(parent=student_key, org=org_key)
for task in tasks:
org_score.tasks.append(task.key())
to_put.append(org_score)
student = ndb.Key.from_old_key(student_key)
student.student_data.number_of_completed_tasks += len(tasks)
student.put()
db.put(to_put)
return txn
# TODO(daniel): add unit tests
def clearOrgScoresTxn(profile_key):
"""Clears all OrgScore entities for the student with the specified
profile key.
"""
def txn():
org_scores = queryForAncestor(profile_key).fetch(1000)
db.delete(org_scores)
profile = profile_key.get()
profile.student_data.number_of_completed_tasks = 0
profile.put()
return txn
def getPossibleWinners(org):
"""Returns the possible winners for the specified organization which can
be chosen by the organization admins.
"""
# TODO(daniel): this should not retrieve all the students and sort them.
# Currently it is necessary, as it is not possible to order the entities
# by the length of the list. Instead, number of completed tasks should
# be explicitly added to OrgScore model.
org_scores = queryForOrg(org).fetch(1000)
org_scores = sorted(org_scores, key=lambda e: len(e.tasks), reverse=True)
profiles = [
ndb.Key.from_old_key(org_score.parent_key()).get()
for org_score in org_scores]
return profiles[:POSSIBLE_WINNER_MAX_POSITION]
def queryForOrg(org, keys_only=False):
"""Return the query to fetch OrgScore entities for the specified
organization.
"""
return GCIOrgScore.all(keys_only=keys_only).filter('org', org)
def queryForAncestorAndOrg(ancestor, org, keys_only=False):
"""Returns the query to fetch OrgScore entities for the specified
ancestor and organization.
"""
return GCIOrgScore.all(keys_only=keys_only).ancestor(
ancestor).filter('org', org)
def queryForAncestor(ancestor, keys_only=False):
"""Returns the query to fetch OrgScore entities for the specified
ancestor.
"""
return GCIOrgScore.all(keys_only=keys_only).ancestor(ancestor)