コード例 #1
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def claimRequestTask(task, student):
  """Used when a student requests to claim a task.

  Updates the status of the tasks and places a comment notifying the org
  that someone wants to work on this task.

  Args:
    task: The task to claim.
    student: Profile of the student that wants to claim the task.
  """
  task.status = 'ClaimRequested'
  task.student = student.key.to_old_key()

  if student.key.to_old_key() not in task.subscribers:
    task.subscribers.append(student.key.to_old_key())

  comment_props = {
      'parent': task,
      'title': DEF_CLAIM_REQUEST_TITLE,
      'content': DEF_CLAIM_REQUEST,
      'created_by': student.key.parent().to_old_key()
  }
  comment = GCIComment(**comment_props)

  comment_txn = comment_logic.storeAndNotifyTxn(comment)

  def claimRequestTaskTxn():
    task.put()
    comment_txn()

  return db.run_in_transaction(claimRequestTaskTxn)
コード例 #2
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def unpublishTask(task, unpublisher):
  """Unpublishes the task.

  This will put the task in the Unpublished state. A comment will also be
  generated to record this event.

  Args:
    task: GCITask entity.
    publisher: GCIProfile of the user that unpublishes the task.
  """
  task.status = task_model.UNPUBLISHED

  comment_props = {
      'parent': task,
      'title': DEF_UNPUBLISHED_TITLE,
      'content': DEF_UNPUBLISHED,
      'created_by': unpublisher.key.parent().to_old_key(),
  }
  comment = GCIComment(**comment_props)

  comment_txn = comment_logic.storeAndNotifyTxn(comment)

  def unpublishTaskTxn():
    task.put()
    comment_txn()
    _spawnUpdateTask(task, transactional=True)

  return db.run_in_transaction(unpublishTaskTxn)
コード例 #3
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def unassignTask(task, profile):
  """Unassigns a task.

  This will put the task in the Reopened state and reset the student and
  deadline property. A comment will also be generated to record this event.

  Args:
    task: task_model.GCITask entity.
    profile: GCIProfile of the user that unassigns the task.
  """
  task.student = None
  task.status = task_model.REOPENED
  task.deadline = None

  comment_props = {
      'parent': task,
      'title': DEF_UNASSIGNED_TITLE,
      'content': DEF_UNASSIGNED,
      'created_by': profile.key.parent().to_old_key()
  }
  comment = GCIComment(**comment_props)

  comment_txn = comment_logic.storeAndNotifyTxn(comment)

  def unassignTaskTxn():
    task.put()
    comment_txn()

  return db.run_in_transaction(unassignTaskTxn)
コード例 #4
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def assignTask(task, student_key, assigner):
  """Assigns the task to the student.

  This will put the task in the Claimed state and set the student and deadline
  property. A comment will also be generated to record this event.

  Args:
    task: task_model.GCITask entity.
    student_key: Key of the student to assign
    assigner: GCIProfile of the user that assigns the student.
  """
  task.student = student_key.to_old_key()
  task.status = 'Claimed'
  task.deadline = datetime.datetime.now() + \
      datetime.timedelta(hours=task.time_to_complete)

  student = student_key.get()
  comment_props = {
      'parent': task,
      'title': DEF_ASSIGNED_TITLE,
      'content': DEF_ASSIGNED %(
          student.public_name, task.time_to_complete),
      'created_by': assigner.key.parent().to_old_key(),
  }
  comment = GCIComment(**comment_props)

  comment_txn = comment_logic.storeAndNotifyTxn(comment)

  def assignTaskTxn():
    task.put()
    comment_txn()
    _spawnUpdateTask(task, transactional=True)

  return db.run_in_transaction(assignTaskTxn)
コード例 #5
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def extendDeadline(task, delta, profile):
  """Extends the deadline of a task.

  Args:
    task: The task to extend the deadline for.
    delta: The timedelta object to be added to the current deadline.
    profile: GCIProfile of the user that extends the deadline.
  """
  if task.deadline:
    deadline = task.deadline + delta
  else:
    deadline = datetime.datetime.utcnow() + delta

  task.deadline = deadline

  comment_props = {
      'parent': task,
      'title': DEF_EXTEND_DEADLINE_TITLE,
      'content': DEF_EXTEND_DEADLINE %(delta.days, delta.seconds/3600),
      'created_by': profile.key.parent().to_old_key()
  }
  comment = GCIComment(**comment_props)

  comment_txn = comment_logic.storeAndNotifyTxn(comment)

  def extendDeadlineTxn():
    task.put()
    comment_txn()

  return db.run_in_transaction(extendDeadlineTxn)
コード例 #6
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def unclaimTask(task):
  """Used when a student requests to unclaim a task.

  Args:
    task: The task to unclaim.
  """
  student_key = task_model.GCITask.student.get_value_for_datastore(task)

  task.student = None
  task.status = task_model.REOPENED
  task.deadline = None

  comment_props = {
      'parent': task,
      'title': DEF_UNCLAIMED_TITLE,
      'content': DEF_UNCLAIMED,
      'created_by': student_key.parent()
  }
  comment = GCIComment(**comment_props)

  comment_txn = comment_logic.storeAndNotifyTxn(comment)

  def unclaimTaskTxn():
    task.put()
    comment_txn()

  return db.run_in_transaction(unclaimTaskTxn)
コード例 #7
0
ファイル: task.py プロジェクト: adviti/melange
  def comments(self):
    """Returns the GCIComments that have the given task as parent.

    The results are sorted by the date on which they have been created.
    """
    q = GCIComment.all()
    q.ancestor(self)
    q.order('created_on')
    return q.fetch(1000)
コード例 #8
0
    def comments(self):
        """Returns the GCIComments that have the given task as parent.

    The results are sorted by the date on which they have been created.
    """
        q = GCIComment.all()
        q.ancestor(self)
        q.order('created_on')
        return q.fetch(1000)
コード例 #9
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
    def task_delete_txn(task):
        """Performs all necessary operations in a single transaction when a task
    is deleted.
    """
        to_delete = []
        to_delete += GCIComment.all(keys_only=True).ancestor(task)
        to_delete += GCIWorkSubmission.all(keys_only=True).ancestor(task)
        to_delete += [task.key()]

        db.delete(to_delete)
コード例 #10
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
  def task_delete_txn(task):
    """Performs all necessary operations in a single transaction when a task
    is deleted.
    """
    to_delete = []
    to_delete += GCIComment.all(keys_only=True).ancestor(task)
    to_delete += GCIWorkSubmission.all(keys_only=True).ancestor(task)
    to_delete += [task.key()]

    db.delete(to_delete)
コード例 #11
0
ファイル: convert_gci_task.py プロジェクト: adviti/melange
def process_task(task):
  """Conversion to make GCI Tasks ID based and getting rid of unnecessary
  properties.
  """
  if task.key().name():
    # Get the values for all the properties in the GCITask model from the
    # old entity to create the new entity.
    new_task_properties = {}
    for prop in TASK_PROPERTIES:
      new_task_properties[prop] = getattr(task, prop)
      new_task_properties['org'] = task.scope

    new_task = GCITask(**new_task_properties)
    new_task_key = new_task.put()

    if new_task_key:
      # Update all the comments with the new task as the parent
      comments = GCIComment.all().ancestor(task).fetch(1000)
      for c in comments:
        new_comm_properties = {}
        for c_prop in COMMENT_PROPERTIES:
          new_comm_properties[c_prop] = getattr(c, c_prop)
        new_comment = GCIComment(parent=new_task_key, **new_comm_properties)

        # set these fields to behave like last_modified_on/by
        new_comment.modified_on = new_comment.created_on
        new_comment.modified_by = new_comment.created_by

        yield operation.db.Put(new_comment)
        yield operation.counters.Increment("comment_updated")

      # Update all the work submission entities with the new task as the parent
      work_submissions = GCIWorkSubmission.all().ancestor(task).fetch(1000)
      for ws in work_submissions:
        new_ws_properties = {}
        for ws_prop in WORK_SUBMISSION_PROPERTIES:
          new_ws_properties[ws_prop] = getattr(ws, ws_prop)
        new_ws = GCIWorkSubmission(parent=new_task_key, **new_ws_properties)
        yield operation.db.Put(new_ws)
        yield operation.counters.Increment("work_submission_updated")

      yield operation.counters.Increment("task_updated")
コード例 #12
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def closeTask(task, profile):
  """Closes the task.

  Args:
    task: task_model.GCITask entity.
    profile: GCIProfile of the user that closes the task.
  """
  from soc.modules.gci.tasks.ranking_update import startUpdatingTask

  task.status = 'Closed'
  task.closed_on = datetime.datetime.now()
  task.deadline = None

  comment_props = {
      'parent': task,
      'title': DEF_CLOSED_TITLE,
      'content': DEF_CLOSED,
      'created_by': profile.key.parent().to_old_key()
  }
  comment = GCIComment(**comment_props)

  comment_txn = comment_logic.storeAndNotifyTxn(comment)

  student_key = ndb.Key.from_old_key(
      task_model.GCITask.student.get_value_for_datastore(task))
  student = student_key.get()

  # student, who worked on the task, should receive a confirmation
  # having submitted his or her first task
  query = queryAllTasksClosedByStudent(student, keys_only=True)
  if query.get() is None: # this is the first task
    confirmation = profile_logic.sendFirstTaskConfirmationTxn(student, task)
  else:
    confirmation = lambda: None

  org_score_txn = org_score_logic.updateOrgScoreTxn(task, student)

  @db.transactional(xg=True)
  def closeTaskTxn():
    task.put()
    comment_txn()
    startUpdatingTask(task, transactional=True)
    confirmation()
    org_score_txn()

  # TODO(daniel): move this to a transaction when other models are NDB
  student = student_key.get()
  student.student_data.number_of_completed_tasks += 1
  student.put()

  return closeTaskTxn()
コード例 #13
0
def turnaroundTime(task):
  from soc.modules.gci.logic import task as task_logic
  from soc.modules.gci.models.comment import GCIComment

  q = GCIComment.all()
  q.ancestor(task)
  q.filter('modified_by', None)
  q.filter('title', task_logic.DEF_ASSIGNED_TITLE)
  comments = sorted(q, key=lambda x: x.created_on)
  started = comments[-1]

  q = GCIComment.all()
  q.ancestor(task)
  q.filter('modified_by', None)
  q.filter('title', task_logic.DEF_SEND_FOR_REVIEW_TITLE)
  comments = sorted(q, key=lambda x: x.created_on)
  finished = comments[-1]

  q = GCIComment.all()
  q.ancestor(task)
  q.filter('modified_by', None)
  q.filter('title', task_logic.DEF_CLOSED_TITLE)
  approved = q.get() # there can only be one

  implementation = finished.created_on - started.created_on
  turnaround = approved.created_on - finished.created_on
  url = "http://www.google-melange.com/gci/task/view/google/gci2011/%d"
  return (url % task.key().id(),
          str(started.created_on),
          str(finished.created_on),
          str(approved.created_on),
          str(implementation),
          str(turnaround),
          task.difficulty_level,
          finished.created_by.name,
          approved.created_by.name,
         )
コード例 #14
0
ファイル: stats.py プロジェクト: adviti/melange
def turnaroundTime(task):
  from soc.modules.gci.logic import task as task_logic
  from soc.modules.gci.models.comment import GCIComment

  q = GCIComment.all()
  q.ancestor(task)
  q.filter('modified_by', None)
  q.filter('title', task_logic.DEF_ASSIGNED_TITLE)
  comments = sorted(q, key=lambda x: x.created_on)
  started = comments[-1]

  q = GCIComment.all()
  q.ancestor(task)
  q.filter('modified_by', None)
  q.filter('title', task_logic.DEF_SEND_FOR_REVIEW_TITLE)
  comments = sorted(q, key=lambda x: x.created_on)
  finished = comments[-1]

  q = GCIComment.all()
  q.ancestor(task)
  q.filter('modified_by', None)
  q.filter('title', task_logic.DEF_CLOSED_TITLE)
  approved = q.get() # there can only be one

  implementation = finished.created_on - started.created_on
  turnaround = approved.created_on - finished.created_on
  url = "http://www.google-melange.com/gci/task/view/google/gci2011/%d"
  return (url % task.key().id(),
          str(started.created_on),
          str(finished.created_on),
          str(approved.created_on),
          str(implementation),
          str(turnaround),
          task.difficulty_level,
          finished.created_by.name,
          approved.created_by.name,
         )
コード例 #15
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def transitFromClaimed(task):
  """Makes a state transition of a GCI Task from Claimed state
  to ActionNeeded.

  Args:
    task: The task_model.GCITask entity
  """
  # deadline is extended by 24 hours.
  task.status = 'ActionNeeded'
  task.deadline = task.deadline + datetime.timedelta(hours=24)

  comment_props = {
      'parent': task,
      'title': DEF_ACTION_NEEDED_TITLE,
      'content': DEF_ACTION_NEEDED,
  }
  comment = GCIComment(**comment_props)

  return task, comment
コード例 #16
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def transitFromActionNeeded(task):
  """Makes a state transition of a GCI Task from ActionNeeded state
  to Reopened state.

  Args:
    task: The task_model.GCITask entity
  """
  # reopen the task
  task.student = None
  task.status = task_model.REOPENED
  task.deadline = None

  comment_props = {
      'parent': task,
      'title': DEF_REOPENED_TITLE,
      'content': DEF_REOPENED,
  }
  comment = GCIComment(**comment_props)

  return task, comment
コード例 #17
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def transitFromNeedsWork(task):
  """Makes a state transition of a GCI Task from NeedsWork state
  to Reopened state.

  A task that has been marked as Needs(more)Work will NOT get a deadline
  extension and will be reopened immediately.

  Args:
    task: The task_model.GCITask entity
  """
  task.student = None
  task.status = task_model.REOPENED
  task.deadline = None

  comment_props = {
      'parent': task,
      'title': DEF_REOPENED_TITLE,
      'content': DEF_REOPENED,
  }
  comment = GCIComment(**comment_props)

  return task, comment
コード例 #18
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def transitFromNeedsReview(task):
  """Makes a state transition of a GCI Task that is in NeedsReview state.

  This state transition is special since it actually only clears the deadline
  field and does not change value of the state field. A Task is in this state
  when work has been submitted and it has not been reviewed before the original
  deadline runs out.

  Args:
    task: The task_model.GCITask entity
  """
  # Clear the deadline since mentors are not forced to review work within a
  # certain period.
  task.deadline = None

  comment_props = {
      'parent': task,
      'title': DEF_NO_MORE_WORK_TITLE,
      'content': DEF_NO_MORE_WORK,
  }
  comment = GCIComment(**comment_props)

  return task, comment
コード例 #19
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def needsWorkTask(task, profile):
  """Closes the task.

  Args:
    task: task_model.GCITask entity.
    profile: GCIProfile of the user that marks this task as needs more work.
  """
  task.status = 'NeedsWork'

  comment_props = {
      'parent': task,
      'title': DEF_NEEDS_WORK_TITLE,
      'content': DEF_NEEDS_WORK,
      'created_by': profile.key.parent().to_old_key()
  }
  comment = GCIComment(**comment_props)

  comment_txn = comment_logic.storeAndNotifyTxn(comment)

  def needsWorkTaskTxn():
    task.put()
    comment_txn()

  return db.run_in_transaction(needsWorkTaskTxn)
コード例 #20
0
ファイル: task.py プロジェクト: rhyolight/nupic.son
def sendForReview(task, student):
  """Send in a task for review.

  Args:
    task: The task to send for review.
    student: Profile of the student that is sending in the work.
  """
  task.status = 'NeedsReview'

  comment_props = {
      'parent': task,
      'title': DEF_SEND_FOR_REVIEW_TITLE,
      'content': DEF_SEND_FOR_REVIEW,
      'created_by': student.key.parent().to_old_key(),
  }
  comment = GCIComment(**comment_props)

  comment_txn = comment_logic.storeAndNotifyTxn(comment)

  def sendForReviewTxn():
    task.put()
    comment_txn()

  return db.run_in_transaction(sendForReviewTxn)
コード例 #21
0
ファイル: task_update.py プロジェクト: adviti/melange
    def sendCommentNotificationMail(self, request, *args, **kwargs):
        """Appengine task that sends mail to the subscribed users.

    Expects the following to be present in the POST dict:
      comment_key: Specifies the comment id for which to send the notifications

    Args:
      request: Django Request object
    """
        # TODO(ljvderijk): If all mails are equal we can sent one big bcc mail

        # set default batch size
        batch_size = 10

        post_dict = request.POST

        comment_key = post_dict.get("comment_key")

        if not comment_key:
            # invalid task data, log and return OK
            return error_handler.logErrorAndReturnOK("Invalid createNotificationMail data: %s" % post_dict)

        comment_key = db.Key(comment_key)
        comment = GCIComment.get(comment_key)

        if not comment:
            # invalid comment specified, log and return OK
            return error_handler.logErrorAndReturnOK("Invalid comment specified: %s" % (comment_key))

        task = comment.parent()
        subscription = GCITaskSubscription.all().ancestor(task).fetch(1)

        # check and retrieve the subscriber_start_key that has been done last
        idx = int(post_dict.get("subscriber_start_index", 0))
        subscribers = db.get(subscription.subscribers[idx : idx + batch_size])

        url_kwargs = {
            "sponsor": task.program.scope_path,
            "program": task.program.link_id,
            "id": task.key().id_or_name(),
        }
        task_url = "http://%(host)s%(task)s" % {
            "host": system.getHostname(),
            "task": reverse("gci_view_task", kwargs=url_kwargs),
        }

        # create the data for the mail to be sent
        message_properties = {
            "task_url": task_url,
            "redirect_url": "%(task_url)s#c%(cid)d" % {"task_url": task_url, "cid": comment.key().id_or_name()},
            "comment_entity": comment,
            "task_entity": task,
        }

        subject = self.DEF_TASK_UPDATE_SUBJECT % {"program_name": task.program.short_name, "title": task.title}

        for subscriber in subscribers:
            # TODO(ljvderijk): enable sending of mail after template fixes
            # gci_notifications.sendTaskUpdateMail(subscriber, subject,
            #                                      message_properties)
            pass

        if len(subscribers) == batch_size:
            # spawn task for sending out notifications to next set of subscribers
            next_start = idx + batch_size

            task_params = {"comment_key": str(comment_key), "subscriber_start_index": next_start}
            task_url = "/tasks/gci/task/mail/comment"

            new_task = taskqueue.Task(params=task_params, url=task_url)
            new_task.add("mail")

        # return OK
        return http.HttpResponse()
コード例 #22
0
ファイル: convert_gci_task.py プロジェクト: adviti/melange
from soc.modules.gci.models.comment import GCIComment
from soc.modules.gci.models.mentor import GCIMentor
from soc.modules.gci.models.org_admin import GCIOrgAdmin
from soc.modules.gci.models.profile import GCIProfile
from soc.modules.gci.models.student import GCIStudent
from soc.modules.gci.models.task import GCITask
from soc.modules.gci.models.task_subscription import GCITaskSubscription
from soc.modules.gci.models.work_submission import GCIWorkSubmission


TASK_PROPERTIES = GCITask.properties()
# We do not want history property in the new entities in any more because
# we are not using it
TASK_PROPERTIES.pop('scope', 'history')

COMMENT_PROPERTIES = GCIComment.properties()
WORK_SUBMISSION_PROPERTIES = GCIWorkSubmission.properties()


def process_task(task):
  """Conversion to make GCI Tasks ID based and getting rid of unnecessary
  properties.
  """
  if task.key().name():
    # Get the values for all the properties in the GCITask model from the
    # old entity to create the new entity.
    new_task_properties = {}
    for prop in TASK_PROPERTIES:
      new_task_properties[prop] = getattr(task, prop)
      new_task_properties['org'] = task.scope