app/soc/modules/ghop/models/task.py
changeset 3085 ded7a67e7e0a
parent 3084 cac43a6cb986
equal deleted inserted replaced
3084:cac43a6cb986 3085:ded7a67e7e0a
    17 """This module contains the GHOP Task Model.
    17 """This module contains the GHOP Task Model.
    18 """
    18 """
    19 
    19 
    20 __authors__ = [
    20 __authors__ = [
    21   '"Madhusudan.C.S" <madhusudancs@gmail.com>',
    21   '"Madhusudan.C.S" <madhusudancs@gmail.com>',
       
    22   '"Daniel Hans" <daniel.m.hans@gmail.com>',
    22   '"Lennard de Rijk" <ljvderijk@gmail.com>',
    23   '"Lennard de Rijk" <ljvderijk@gmail.com>',
    23 ]
    24 ]
    24 
    25 
    25 
    26 
    26 from google.appengine.ext import db
    27 from google.appengine.ext import db
    41 
    42 
    42 class TaskTag(Tag):
    43 class TaskTag(Tag):
    43   """Model for storing all Task tags.
    44   """Model for storing all Task tags.
    44   """
    45   """
    45 
    46 
    46   #: Each task_type tag is scoped under the program.
       
    47 
       
    48   order = db.IntegerProperty(required=True, default=0)
    47   order = db.IntegerProperty(required=True, default=0)
    49 
       
    50   @classmethod
       
    51   def __key_name(cls, scope_path, tag_name):
       
    52     """Create the key_name from program key_name as scope_path and tag_name.
       
    53     """
       
    54     return scope_path + '/' + tag_name
       
    55 
       
    56   @classmethod
       
    57   def get_by_name(cls, tag_name):
       
    58     """Get the list of tag objects that has the given tag_name.
       
    59     """
       
    60     tags = db.Query(cls).filter('tag =', tag_name).fetch(1000)
       
    61     return tags
       
    62 
    48 
    63   @classmethod
    49   @classmethod
    64   def get_by_scope(cls, scope):
    50   def get_by_scope(cls, scope):
    65     """Get the list of tag objects that has the given scope.
    51     """Get the list of tag objects that has the given scope and sorts the
    66     """
    52        result by order values.
       
    53     """
       
    54 
    67     tags = db.Query(cls).filter('scope =', scope).order('order').fetch(1000)
    55     tags = db.Query(cls).filter('scope =', scope).order('order').fetch(1000)
    68     return tags
    56     return tags
    69 
    57 
    70   @classmethod
    58   @classmethod
    71   def get_highest_order(cls, scope):
    59   def get_highest_order(cls, scope):
    72     """Get a tag with highest order.
    60     """Get a tag with highest order.
    73     """
    61     """
    74     tags = db.Query(cls).filter('scope =', scope).order('-order').fetch(1)
    62 
    75     if tags:
    63     tag = db.Query(cls).filter('scope =', scope).order('-order').get()
    76       return tags[0].order
    64     if tag:
       
    65       return tag.order
    77     else:
    66     else:
    78       return -1
    67       return -1
    79 
       
    80   @classmethod
       
    81   def get_by_scope_and_name(cls, scope, tag_name):
       
    82     """Get a tag by scope and name.
       
    83 
       
    84     There can be only one such tag.
       
    85     """
       
    86 
       
    87     tags = db.Query(cls).filter(
       
    88         'scope =', scope).filter('tag =', tag_name).fetch(1)
       
    89     if tags:
       
    90       return tags[0]
       
    91     else:
       
    92       return None
       
    93 
    68 
    94   @classmethod
    69   @classmethod
    95   def update_order(cls, scope, tag_name, order):
    70   def update_order(cls, scope, tag_name, order):
    96     """Updates the order of the tag.
    71     """Updates the order of the tag.
    97     """
    72     """
   101       tag.order = order
    76       tag.order = order
   102       tag.put()
    77       tag.put()
   103 
    78 
   104     return tag
    79     return tag
   105 
    80 
   106   @classmethod
       
   107   def copy_tag(cls, scope, tag_name, new_tag_name):
       
   108     """Copy a tag with a given scope and tag_name to another tag with
       
   109     new tag_name.
       
   110     """
       
   111     tag = cls.get_by_scope_and_name(scope, tag_name)
       
   112 
       
   113     if tag:
       
   114       tag_key_name = cls.__key_name(scope.key().name(), new_tag_name)
       
   115       existing_tag = cls.get_by_key_name(tag_key_name)
       
   116 
       
   117       if existing_tag is None:
       
   118         new_tag = cls(key_name=tag_key_name, tag=new_tag_name, scope=scope, 
       
   119                       added=tag.added, tagged=tag.tagged,
       
   120                       tagged_count=tag.tagged_count)
       
   121         new_tag.put()
       
   122         tag.delete()
       
   123 
       
   124         return new_tag
       
   125 
       
   126       return existing_tag
       
   127 
       
   128     return None
       
   129 
       
   130   @classmethod
       
   131   def delete_tag(cls, scope, tag_name):
       
   132     """Delete a tag with a given scope and tag_name.
       
   133     """
       
   134     tag = cls.get_by_scope_and_name(scope, tag_name)
       
   135 
       
   136     if tag:
       
   137       tag.delete()
       
   138       return True
       
   139 
       
   140     return False
       
   141 
       
   142   @classmethod
       
   143   def get_or_create(cls, scope, tag_name, order=0):
       
   144     """Get the Tag object that has the tag value given by tag_value.
       
   145     """
       
   146 
       
   147     if not scope:
       
   148       return None
       
   149 
       
   150     tag_key_name = cls.__key_name(scope.key().name(), tag_name)
       
   151     existing_tag = cls.get_by_key_name(tag_key_name)
       
   152     if existing_tag is None:
       
   153       # the tag does not yet exist, so create it.
       
   154       if not order:
       
   155         order = cls.get_highest_order(scope=scope) + 1
       
   156       def create_tag_txn():
       
   157         new_tag = cls(key_name=tag_key_name, tag=tag_name,
       
   158                       scope=scope, order=order)
       
   159         new_tag.put()
       
   160         return new_tag
       
   161       existing_tag = db.run_in_transaction(create_tag_txn)
       
   162     return existing_tag
       
   163 
       
   164 
    81 
   165 class TaskTypeTag(TaskTag):
    82 class TaskTypeTag(TaskTag):
   166   """Model for storing of task type tags.
    83   """Model for storing of task type tags.
   167   """
    84   """
   168 
    85 
   200   title = db.StringProperty(required=True,
   117   title = db.StringProperty(required=True,
   201                             verbose_name=ugettext('Title'))
   118                             verbose_name=ugettext('Title'))
   202   title.help_text = ugettext('Title of the task')
   119   title.help_text = ugettext('Title of the task')
   203 
   120 
   204   #: Required field containing the description of the task
   121   #: Required field containing the description of the task
   205   description = db.TextProperty(required=True, 
   122   description = db.TextProperty(required=True,
   206                                 verbose_name=ugettext('Description'))
   123                                 verbose_name=ugettext('Description'))
   207   description.help_text = ugettext('Complete description of the task')
   124   description.help_text = ugettext('Complete description of the task')
   208 
   125 
   209   #: Field indicating the difficulty level of the Task. This is not
   126   #: Field indicating the difficulty level of the Task. This is not
   210   #: mandatory so the it can be assigned at any later stage. 
   127   #: mandatory so the it can be assigned at any later stage.
   211   #: The options are configured by a Program Admin.
   128   #: The options are configured by a Program Admin.
   212   difficulty = tag_property('difficulty')
   129   difficulty = tag_property('difficulty')
   213 
   130 
   214   #: Required field which contains the type of the task. These types are
   131   #: Required field which contains the type of the task. These types are
   215   #: configured by a Program Admin.
   132   #: configured by a Program Admin.
   258   #: Open: This Task is open and ready to be claimed.
   175   #: Open: This Task is open and ready to be claimed.
   259   #: Reopened: This Task has been claimed but never finished and has been
   176   #: Reopened: This Task has been claimed but never finished and has been
   260   #:   reopened.
   177   #:   reopened.
   261   #: ClaimRequested: A Student has requested to claim this task.
   178   #: ClaimRequested: A Student has requested to claim this task.
   262   #: Claimed: This Task has been claimed and someone is working on it.
   179   #: Claimed: This Task has been claimed and someone is working on it.
   263   #: ActionNeeded: Work on this Task must be submitted for review within 
   180   #: ActionNeeded: Work on this Task must be submitted for review within
   264   #:   24 hours.
   181   #:   24 hours.
   265   #: Closed: Work on this Task has been completed to the org's content.
   182   #: Closed: Work on this Task has been completed to the org's content.
   266   #: AwaitingRegistration: Student has completed work on this task, but
   183   #: AwaitingRegistration: Student has completed work on this task, but
   267   #:   needs to complete Student registration before this task is closed.
   184   #:   needs to complete Student registration before this task is closed.
   268   #: NeedsWork: This work on this Tasks needs a bit more brushing up. This
   185   #: NeedsWork: This work on this Tasks needs a bit more brushing up. This
   270   #: NeedsReview: Student has submitted work for this task and it should
   187   #: NeedsReview: Student has submitted work for this task and it should
   271   #:   be reviewed by a Mentor.
   188   #:   be reviewed by a Mentor.
   272   #: Invalid: The Task is deleted either by an Org Admin/Mentor
   189   #: Invalid: The Task is deleted either by an Org Admin/Mentor
   273   status = db.StringProperty(
   190   status = db.StringProperty(
   274       required=True, verbose_name=ugettext('Status'),
   191       required=True, verbose_name=ugettext('Status'),
   275       choices=['Unapproved', 'Unpublished', 'Open', 'Reopened', 
   192       choices=['Unapproved', 'Unpublished', 'Open', 'Reopened',
   276                'ClaimRequested', 'Claimed', 'ActionNeeded', 
   193                'ClaimRequested', 'Claimed', 'ActionNeeded',
   277                'Closed', 'AwaitingRegistration', 'NeedsWork',
   194                'Closed', 'AwaitingRegistration', 'NeedsWork',
   278                'NeedsReview', 'Invalid'],
   195                'NeedsReview', 'Invalid'],
   279       default='Unapproved')
   196       default='Unapproved')
   280 
   197 
   281   #: This field is set to the next deadline that will have consequences for
   198   #: This field is set to the next deadline that will have consequences for
   316   #:                   "student": Student reference
   233   #:                   "student": Student reference
   317   #:                   ...
   234   #:                   ...
   318   #:                   "state": "Unapproved"
   235   #:                   "state": "Unapproved"
   319   #:                   ...
   236   #:                   ...
   320   #:                   "edited_by": Role reference
   237   #:                   "edited_by": Role reference
   321   #:                   
   238   #:
   322   #:               }
   239   #:               }
   323   #:    timestamp2: {
   240   #:    timestamp2: {
   324   #:                   "state": "Unpublished"
   241   #:                   "state": "Unpublished"
   325   #:               }
   242   #:               }
   326   #: }
   243   #: }
   328   #: model. The subsequent items hold the properties that changed at the
   245   #: model. The subsequent items hold the properties that changed at the
   329   #: timestamp given by the key.
   246   #: timestamp given by the key.
   330   #: Reference properties will be stored by calling str() on their Key.
   247   #: Reference properties will be stored by calling str() on their Key.
   331   history = db.TextProperty(required=False, default='')
   248   history = db.TextProperty(required=False, default='')
   332 
   249 
   333   def __init__(self, parent=None, key_name=None, 
   250   def __init__(self, parent=None, key_name=None,
   334                app=None, **entity_values):
   251                app=None, **entity_values):
   335     """Constructor for GHOPTask Model.
   252     """Constructor for GHOPTask Model.
   336     
   253 
   337     Args:
   254     Args:
   338         See Google App Engine APIs.
   255         See Google App Engine APIs.
   339     """
   256     """
   340 
   257 
   341     # explicitly call the AppEngine datastore Model constructor
   258     # explicitly call the AppEngine datastore Model constructor
   342     db.Model.__init__(self, parent, key_name, app, **entity_values)
   259     db.Model.__init__(self, parent, key_name, app, **entity_values)
   343 
   260 
   344     # call the Taggable constructor to initialize the tags specified as
   261     # call the Taggable constructor to initialize the tags specified as
   345     # keyword arguments
   262     # keyword arguments
   346     Taggable.__init__(self, task_type=TaskTypeTag, 
   263     Taggable.__init__(self, task_type=TaskTypeTag,
   347                       difficulty=TaskDifficultyTag,
   264                       difficulty=TaskDifficultyTag,
   348                       arbit_tag=TaskArbitraryTag)
   265                       arbit_tag=TaskArbitraryTag)