class ReviewStep(student_work.BaseEntity): """Object that represents a single state of a review.""" # Audit trail information. # Identifier for the kind of thing that did the assignment. Used to # distinguish between assignments done by humans and those done by the # review subsystem. assigner_kind = db.StringProperty(choices=domain.ASSIGNER_KINDS, required=True) # UTC last modification timestamp. change_date = db.DateTimeProperty(auto_now=True, required=True) # UTC create date. create_date = db.DateTimeProperty(auto_now_add=True, required=True) # Repeated data to allow filtering/ordering in queries. # Key of the submission being reviewed. submission_key = student_work.KeyProperty( kind=student_work.Submission.kind(), required=True) # Unit this review step is part of. unit_id = db.StringProperty(required=True) # State information. # State of this review step. state = db.StringProperty(choices=domain.REVIEW_STATES, required=True) # Whether or not the review has been removed. By default removed entities # are ignored for most queries. removed = db.BooleanProperty(default=False) # Pointers that tie the work and people involved together. # Key of the Review associated with this step. review_key = student_work.KeyProperty(kind=student_work.Review.kind()) # Key of the associated ReviewSummary. review_summary_key = student_work.KeyProperty(kind=ReviewSummary.kind()) # Key of the Student being reviewed. reviewee_key = student_work.KeyProperty(kind=models.Student.kind()) # Key of the Student doing this review. reviewer_key = student_work.KeyProperty(kind=models.Student.kind()) def __init__(self, *args, **kwargs): """Constructs a new ReviewStep.""" assert not kwargs.get('key_name'), ( 'Setting key_name manually not supported') reviewer_key = kwargs.get('reviewer_key') submission_key = kwargs.get('submission_key') assert reviewer_key, 'Missing required reviewer_key property' assert submission_key, 'Missing required submission_key property' kwargs['key_name'] = self.key_name(submission_key, reviewer_key) super(ReviewStep, self).__init__(*args, **kwargs) @classmethod def key_name(cls, submission_key, reviewer_key): """Creates a key_name string for datastore operations.""" return '(review_step:%s:%s)' % (submission_key.id_or_name(), reviewer_key.id_or_name())
class ValidatedReference(entities.BaseEntity): referenced_model_key = student_work.KeyProperty( kind=ReferencedModel.kind())
class UnvalidatedReference(entities.BaseEntity): referenced_model_key = student_work.KeyProperty()
class ReviewSummary(student_work.BaseEntity): """Object that tracks the aggregate state of reviews for a submission.""" # UTC last modification timestamp. change_date = db.DateTimeProperty(auto_now=True, required=True) # UTC create date. create_date = db.DateTimeProperty(auto_now_add=True, required=True) # Strong counters. Callers should never manipulate these directly. Instead, # use decrement|increment_count. # Count of ReviewStep entities for this submission currently in state # STATE_ASSIGNED. assigned_count = db.IntegerProperty(default=0, required=True) # Count of ReviewStep entities for this submission currently in state # STATE_COMPLETED. completed_count = db.IntegerProperty(default=0, required=True) # Count of ReviewStep entities for this submission currently in state # STATE_EXPIRED. expired_count = db.IntegerProperty(default=0, required=True) # Key of the student who wrote the submission being reviewed. reviewee_key = student_work.KeyProperty( kind=models.Student.kind(), required=True) # Key of the submission being reviewed. submission_key = student_work.KeyProperty( kind=student_work.Submission.kind(), required=True) # Identifier of the unit this review is a part of. unit_id = db.StringProperty(required=True) def __init__(self, *args, **kwargs): """Constructs a new ReviewSummary.""" assert not kwargs.get('key_name'), ( 'Setting key_name manually not supported') submission_key = kwargs.get('submission_key') assert submission_key, 'Missing required submission_key property' kwargs['key_name'] = self.key_name(submission_key) super(ReviewSummary, self).__init__(*args, **kwargs) @classmethod def key_name(cls, submission_key): """Creates a key_name string for datastore operations.""" return '(review_summary:%s)' % submission_key.id_or_name() @classmethod def safe_key(cls, db_key, transform_fn): _, _, unit_id, unsafe_reviewee_key_name = cls._split_key(db_key.name()) unsafe_reviewee_key = db.Key.from_path( models.Student.kind(), unsafe_reviewee_key_name) unsafe_submission_key = student_work.Submission.get_key( unit_id, unsafe_reviewee_key) safe_submission_key = student_work.Submission.safe_key( unsafe_submission_key, transform_fn) return db.Key.from_path(cls.kind(), cls.key_name(safe_submission_key)) def _check_count(self): count_sum = ( self.assigned_count + self.completed_count + self.expired_count) if count_sum >= domain.MAX_UNREMOVED_REVIEW_STEPS: COUNTER_INCREMENT_COUNT_COUNT_AGGREGATE_EXCEEDED_MAX.inc() raise db.BadValueError( 'Unable to increment %s to %s; max is %s' % ( self.kind(), count_sum, domain.MAX_UNREMOVED_REVIEW_STEPS)) def decrement_count(self, state): """Decrements the count for the given state enum; does not save. Args: state: string. State indicating counter to decrement; must be one of domain.REVIEW_STATES. Raises: ValueError: if state not in domain.REVIEW_STATES. """ if state == domain.REVIEW_STATE_ASSIGNED: self.assigned_count -= 1 elif state == domain.REVIEW_STATE_COMPLETED: self.completed_count -= 1 elif state == domain.REVIEW_STATE_EXPIRED: self.expired_count -= 1 else: raise ValueError('%s not in %s' % (state, domain.REVIEW_STATES)) def increment_count(self, state): """Increments the count for the given state enum; does not save. Args: state: string. State indicating counter to increment; must be one of domain.REVIEW_STATES. Raises: db.BadValueError: if incrementing the counter would cause the sum of all *_counts to exceed domain.MAX_UNREMOVED_REVIEW_STEPS. ValueError: if state not in domain.REVIEW_STATES """ if state not in domain.REVIEW_STATES: raise ValueError('%s not in %s' % (state, domain.REVIEW_STATES)) self._check_count() if state == domain.REVIEW_STATE_ASSIGNED: self.assigned_count += 1 elif state == domain.REVIEW_STATE_COMPLETED: self.completed_count += 1 elif state == domain.REVIEW_STATE_EXPIRED: self.expired_count += 1 def for_export(self, transform_fn): model = super(ReviewSummary, self).for_export(transform_fn) model.reviewee_key = models.Student.safe_key( model.reviewee_key, transform_fn) model.submission_key = student_work.Submission.safe_key( model.submission_key, transform_fn) return model
class ManualEvaluationStep(student_work.BaseEntity): """Object that represents a single state of a review.""" # Audit trail information. # Identifier for the kind of thing that did the assignment. Used to # distinguish between assignments done by humans and those done by the # review subsystem. assigner_kind = db.StringProperty(choices=ASSIGNER_KINDS, required=True) # UTC last modification timestamp. change_date = db.DateTimeProperty(auto_now=True, required=True) # UTC create date. create_date = db.DateTimeProperty(auto_now_add=True, required=True) # Repeated data to allow filtering/ordering in queries. # Key of the submission being reviewed. submission_key = student_work.KeyProperty( kind=student_work.Submission.kind(), required=True) # Unit this review step is part of. unit_id = db.StringProperty(required=True, indexed=True) # State information. # State of this review step. state = db.StringProperty(choices=REVIEW_STATES, required=True, indexed=True) # Whether or not the review has been removed. By default removed entities # are ignored for most queries. removed = db.BooleanProperty(default=False, indexed=True) # Pointers that tie the work and people involved together. # Key of the associated ManualEvaluationSummary. manual_evaluation_summary_key = student_work.KeyProperty( kind=ManualEvaluationSummary.kind()) # Key of the Student being reviewed. reviewee_key = student_work.KeyProperty(kind=models.Student.kind(), indexed=False) # Key of the Evaluator doing this review. evaluator = db.StringProperty(indexed=True, required=True) # Evaluation data comments = db.TextProperty(indexed=False) score = db.FloatProperty(default=0.0, indexed=False) drive_permission_list = db.StringProperty(indexed=False) def __init__(self, *args, **kwargs): """Constructs a new ManualEvaluationStep.""" assert not kwargs.get('key_name'), ( 'Setting key_name manually not supported') evaluator = kwargs.get('evaluator') submission_key = kwargs.get('submission_key') assert evaluator, 'Missing required evaluator property' assert submission_key, 'Missing required submission_key property' kwargs['key_name'] = self.key_name(submission_key, evaluator) super(ManualEvaluationStep, self).__init__(*args, **kwargs) @classmethod def key_name(cls, submission_key, evaluator): """Creates a key_name string for datastore operations.""" return '(evaluation_step:%s:%s)' % (submission_key.id_or_name(), evaluator) @classmethod def safe_key(cls, db_key, transform_fn): """Constructs a version of the entitiy's key that is safe for export.""" cls._split_key(db_key.name()) name = db_key.name().strip('()') unsafe_submission_key_name, unsafe_evaluator = name.split(':', 1)[1].rsplit( ':', 1) safe_evaluator = transform_fn(unsafe_evaluator) # Treating as module-protected. pylint: disable-msg=protected-access _, unit_id, unsafe_reviewee_key_name = ( student_work.Submission._split_key(unsafe_submission_key_name)) unsafe_reviewee_key = db.Key.from_path(models.Student.kind(), unsafe_reviewee_key_name) unsafe_submission_key = student_work.Submission.get_key( unit_id, unsafe_reviewee_key) safe_submission_key = student_work.Submission.safe_key( unsafe_submission_key, transform_fn) return db.Key.from_path( cls.kind(), cls.key_name(safe_submission_key, safe_evaluator)) def for_export(self, transform_fn): """Creates a version of the entity that is safe for export.""" model = super(ManualEvaluationStep, self).for_export(transform_fn) model.review_key = student_work.Review.safe_key( model.review_key, transform_fn) model.manual_evaluation_summary_key = ManualEvaluationSummary.safe_key( model.manual_evaluation_summary_key, transform_fn) model.reviewee_key = models.Student.safe_key(model.reviewee_key, transform_fn) model.evaluator = transform_fn(model.evaluator) model.submission_key = student_work.Submission.safe_key( model.submission_key, transform_fn) return model
class ReviewStep(student_work.BaseEntity): """Object that represents a single state of a review.""" # Audit trail information. # Identifier for the kind of thing that did the assignment. Used to # distinguish between assignments done by humans and those done by the # review subsystem. assigner_kind = db.StringProperty( choices=domain.ASSIGNER_KINDS, required=True) # UTC last modification timestamp. change_date = db.DateTimeProperty(auto_now=True, required=True) # UTC create date. create_date = db.DateTimeProperty(auto_now_add=True, required=True) # Repeated data to allow filtering/ordering in queries. # Key of the submission being reviewed. submission_key = student_work.KeyProperty( kind=student_work.Submission.kind(), required=True) # Unit this review step is part of. unit_id = db.StringProperty(required=True) # State information. # State of this review step. state = db.StringProperty(choices=domain.REVIEW_STATES, required=True) # Whether or not the review has been removed. By default removed entities # are ignored for most queries. removed = db.BooleanProperty(default=False) # Pointers that tie the work and people involved together. # Key of the Review associated with this step. review_key = student_work.KeyProperty(kind=student_work.Review.kind()) # Key of the associated ReviewSummary. review_summary_key = student_work.KeyProperty(kind=ReviewSummary.kind()) # Key of the Student being reviewed. reviewee_key = student_work.KeyProperty(kind=models.Student.kind()) # Key of the Student doing this review. reviewer_key = student_work.KeyProperty(kind=models.Student.kind()) def __init__(self, *args, **kwargs): """Constructs a new ReviewStep.""" assert not kwargs.get('key_name'), ( 'Setting key_name manually not supported') reviewer_key = kwargs.get('reviewer_key') reviewee_key = kwargs.get('reviewee_key') submission_key = kwargs.get('submission_key') assert reviewer_key, 'Missing required reviewer_key property' assert reviewee_key, 'Missing required reviewee_key property' assert submission_key, 'Missing required submission_key property' kwargs['key_name'] = self.key_name(submission_key, reviewer_key) super(ReviewStep, self).__init__(*args, **kwargs) @classmethod def key_name(cls, submission_key, reviewer_key): """Creates a key_name string for datastore operations.""" return '(review_step:%s:%s)' % ( submission_key.id_or_name(), reviewer_key.id_or_name()) @classmethod def safe_key(cls, db_key, transform_fn): """Constructs a version of the entitiy's key that is safe for export.""" cls._split_key(db_key.name()) name = db_key.name().strip('()') unsafe_submission_key_name, unsafe_reviewer_id_or_name = name.split( ':', 1)[1].rsplit(':', 1) unsafe_reviewer_key = db.Key.from_path( models.Student.kind(), unsafe_reviewer_id_or_name) safe_reviewer_key = models.Student.safe_key( unsafe_reviewer_key, transform_fn) # Treating as module-protected. pylint: disable=protected-access _, unit_id, unsafe_reviewee_key_name = ( student_work.Submission._split_key(unsafe_submission_key_name)) unsafe_reviewee_key = db.Key.from_path( models.Student.kind(), unsafe_reviewee_key_name) unsafe_submission_key = student_work.Submission.get_key( unit_id, unsafe_reviewee_key) safe_submission_key = student_work.Submission.safe_key( unsafe_submission_key, transform_fn) return db.Key.from_path( cls.kind(), cls.key_name(safe_submission_key, safe_reviewer_key)) def for_export(self, transform_fn): """Creates a version of the entity that is safe for export.""" model = super(ReviewStep, self).for_export(transform_fn) model.review_key = student_work.Review.safe_key( model.review_key, transform_fn) model.review_summary_key = ReviewSummary.safe_key( model.review_summary_key, transform_fn) model.reviewee_key = models.Student.safe_key( model.reviewee_key, transform_fn) model.reviewer_key = models.Student.safe_key( model.reviewer_key, transform_fn) model.submission_key = student_work.Submission.safe_key( model.submission_key, transform_fn) return model @classmethod def _get_student_key(cls, value): return db.Key.from_path(models.Student.kind(), value) @classmethod def delete_by_reviewee_id(cls, user_id): student_key = cls._get_student_key(user_id) query = ReviewStep.all(keys_only=True).filter( 'reviewee_key =', student_key) db.delete(query.run())