Beispiel #1
0
class XModuleUserStateSummaryField(models.Model):
    """
    Stores data set in the Scope.user_state_summary scope by an xmodule field
    """
    class Meta:
        unique_together = (('usage_id', 'field_name'), )

    # The name of the field
    field_name = models.CharField(max_length=64, db_index=True)

    # The definition id for the module
    usage_id = LocationKeyField(max_length=255, db_index=True)

    # The value of the field. Defaults to None dumped as json
    value = models.TextField(default='null')

    created = models.DateTimeField(auto_now_add=True, db_index=True)
    modified = models.DateTimeField(auto_now=True, db_index=True)

    def __repr__(self):
        return 'XModuleUserStateSummaryField<%r>' % ({
            'field_name': self.field_name,
            'usage_id': self.usage_id,
            'value': self.value,
        }, )

    def __unicode__(self):
        return unicode(repr(self))
Beispiel #2
0
class VideoIntervals(models.Model):

    student = models.CharField(max_length=32, db_index=True)
    #course_key
    course_key = CourseKeyField(max_length=255, db_index=True)
    #OLD course_key = models.CharField(max_length=255, db_index=True)
    module_key = LocationKeyField(max_length=255, db_index=True)

    # Module's display name
    display_name = models.CharField(max_length=255, db_index=True)

    hist_xaxis = models.TextField(db_index=False)
    hist_yaxis = models.TextField(db_index=False)

    class Meta:
        unique_together = (('student', 'module_key'), )

    def __repr__(self):
        return 'VideoIntervals<%r>' % ({
            'student': self.student,
            'course_key': self.course_key,
            'module_key': self.module_key,
            'display_name': self.display_name,
            'hist_xaxis': self.hist_xaxis,
            'hist_yaxis': self.hist_yaxis,
        }, )

    def __unicode__(self):
        return unicode(repr(self))
Beispiel #3
0
class CourseStruct(models.Model):
    SECTION_TYPES = (
        ('chapter', 'chapter'),
        ('sequential', 'sequential'),
        ('vertical', 'vertical'),
    )
    # Section description
    course_id = CourseKeyField(max_length=255, db_index=True)
    module_state_key = LocationKeyField(max_length=255, db_column='module_id')
    name = models.CharField(max_length=255)
    section_type = models.CharField(max_length=32,
                                    choices=SECTION_TYPES,
                                    default='chapter',
                                    db_index=True)
    index = models.IntegerField()
    father = models.ForeignKey('self',
                               limit_choices_to={'section_type': 'chapter'},
                               blank=True,
                               null=True)
    # Data
    graded = models.BooleanField(default=False)
    released = models.BooleanField(default=False)

    class Meta:
        unique_together = (('module_state_key', 'course_id'), )

    # Set fahter to null if section_type is chapter
    def __init__(self, *args, **kwargs):
        super(CourseStruct, self).__init__(*args, **kwargs)
        self.section_type = self.module_state_key.category
Beispiel #4
0
class StudentTimeTracker(TimeStampedModel):
    """
    This records how many milliseconds did user spend on a particular unit in course.
    """
    course_id = CourseKeyField(db_index=True, max_length=255)
    unit_location = LocationKeyField(max_length=255, db_index=True)
    student = models.ForeignKey(User, db_index=True)
    time_duration = models.IntegerField(blank=True, null=True)

    class Meta(object):
        app_label = "courseware"
        unique_together = (('course_id', 'unit_location', 'student'), )

    @classmethod
    def update_time(cls, course_id, unit_location, student, time_duration):
        # Used to be a get_or_create() here, but Django's get_or_create()
        # has pecularities when unique_together is introduced so it is
        # broken down into a try-except block to omit those issues.
        try:
            student_time_tracker = cls.objects.get(course_id=course_id,
                                                   unit_location=unit_location,
                                                   student=student)
        except cls.DoesNotExist:
            student_time_tracker = cls.objects.create(
                course_id=course_id,
                unit_location=unit_location,
                student=student)

        if student_time_tracker.time_duration:
            student_time_tracker.time_duration = student_time_tracker.time_duration + long(
                time_duration)
        else:
            student_time_tracker.time_duration = time_duration

        student_time_tracker.save()
Beispiel #5
0
class XModuleUserStateSummaryField(XBlockFieldBase):
    """
    Stores data set in the Scope.user_state_summary scope by an xmodule field
    """
    class Meta(object):  # pylint: disable=missing-docstring
        unique_together = (('usage_id', 'field_name'), )

    # The definition id for the module
    usage_id = LocationKeyField(max_length=255, db_index=True)
Beispiel #6
0
class CourseVideos(models.Model):
    video_name = models.CharField(max_length=255)
    video_module_ids = LocationKeyField(max_length=255, db_index=True)
    video_duration = models.TextField(db_index=False)
    course_key = CourseKeyField(max_length=255, db_index=True, null=True)

    class Meta:
        app_label = 'learning_analytics'
        db_table = 'learning_analytics_coursevideos'
Beispiel #7
0
class XModuleUserStateSummaryField(XBlockFieldBase):
    """
    Stores data set in the Scope.user_state_summary scope by an xmodule field
    """
    class Meta(object):
        app_label = "courseware"
        unique_together = (('usage_id', 'field_name'), )

    # The definition id for the module
    usage_id = LocationKeyField(max_length=255, db_index=True)
Beispiel #8
0
class CcxFieldOverride(models.Model):
    """
    Field overrides for custom courses.
    """
    ccx = models.ForeignKey(CustomCourseForEdX, db_index=True)
    location = LocationKeyField(max_length=255, db_index=True)
    field = models.CharField(max_length=255)

    class Meta:  # pylint: disable=missing-docstring,old-style-class
        unique_together = (('ccx', 'location', 'field'),)

    value = models.TextField(default='null')
Beispiel #9
0
class LastKnownTrackingLog(models.Model):
    """Defines the fields that are stored in the last known tracking log database."""

    event_id = models.IntegerField()
    username = models.CharField(max_length=32, blank=True)
    course_key = CourseKeyField(max_length=255, db_index=True)
    indicator = models.CharField(max_length=32, blank=True)
    module_key = LocationKeyField(max_length=255, default=None, db_index=True)

    class Meta:
        app_label = 'learning_analytics'
        db_table = 'learning_analytics_lastknowntrackinglog'
Beispiel #10
0
class CcxFieldOverride(models.Model):
    """
    Field overrides for custom courses.
    """
    ccx = models.ForeignKey(CustomCourseForEdX, db_index=True)
    location = LocationKeyField(max_length=255, db_index=True)
    field = models.CharField(max_length=255)

    class Meta(object):
        unique_together = (('ccx', 'location', 'field'), )

    value = models.TextField(default='null')
Beispiel #11
0
class StudentFieldOverride(TimeStampedModel):
    """
    Holds the value of a specific field overriden for a student.  This is used
    by the code in the `courseware.student_field_overrides` module to provide
    overrides of xblock fields on a per user basis.
    """
    course_id = CourseKeyField(max_length=255, db_index=True)
    location = LocationKeyField(max_length=255, db_index=True)
    student = models.ForeignKey(User, db_index=True)

    class Meta(object):  # pylint: disable=missing-docstring
        unique_together = (('course_id', 'field', 'location', 'student'), )

    field = models.CharField(max_length=255)
    value = models.TextField(default='null')
Beispiel #12
0
class ConsumptionModule(models.Model):

    # This model for student is invalid as it does not allow for average and aggregate values.
    #student = models.ForeignKey(User, db_index=True)

    student = models.CharField(max_length=32, db_index=True)
    #course_key
    course_key = CourseKeyField(max_length=255, db_index=True)
    #OLD course_key = models.CharField(max_length=255, db_index=True)

    MODULE_TYPES = (
        ('problem', 'problem'),
        ('video', 'video'),
    )

    module_type = models.CharField(max_length=32,
                                   choices=MODULE_TYPES,
                                   default='video',
                                   db_index=True)
    module_key = LocationKeyField(max_length=255, db_index=True)
    # Module's display name
    display_name = models.CharField(max_length=255, db_index=True)

    total_time = models.FloatField(db_index=True)

    # For videos only. Time of non-overlapped video viewed in seconds
    percent_viewed = models.FloatField(null=True, blank=True, db_index=True)

    class Meta:
        unique_together = (('student', 'module_key'), )

    def __repr__(self):
        return 'ConsumptionModule<%r>' % (
            {
                'student': self.student,
                'course_key': self.course_key,
                'module_type': self.module_type,
                'module_key': self.module_key,
                'display_name': self.display_name,
                'total_time': self.total_time,
                'percent_viewed': self.percent_viewed,
            }, )

    def __unicode__(self):
        return unicode(repr(self))
Beispiel #13
0
class VideoTimeWatched(models.Model):

    # This model for student is invalid as it does not allow for average and aggregate values.
    #student = models.ForeignKey(User, db_index=True)

    student = models.CharField(max_length=32, db_index=True)
    #course_key
    course_key = CourseKeyField(max_length=255, db_index=True)
    #OLD course_key = models.CharField(max_length=255, db_index=True)

    module_key = LocationKeyField(max_length=255, db_index=True)
    # Module's display name
    display_name = models.CharField(max_length=255, db_index=True)

    total_time = models.FloatField(db_index=True)

    # Time of non-overlapped video viewed in seconds
    percent_viewed = models.FloatField(null=True, blank=True, db_index=True)

    disjointed_start = models.TextField(db_index=False)
    disjointed_end = models.TextField(db_index=False)

    class Meta:
        unique_together = (('student', 'module_key'), )

    def __repr__(self):
        return 'ConsumptionModule<%r>' % (
            {
                'student': self.student,
                'course_key': self.course_key,
                'module_key': self.module_key,
                'display_name': self.display_name,
                'total_time': self.total_time,
                'percent_viewed': self.percent_viewed,
                'disjointed_start': self.disjointed_start,
                'disjointed_end': self.disjointed_end,
            }, )

    def __unicode__(self):
        return unicode(repr(self))
Beispiel #14
0
class VideoEvents(models.Model):

    student = models.CharField(max_length=32, db_index=True)
    # Here the model field using ForeignKey makes sense since for this model '#average' student is not used
    # However, for similarity to the other models here, CharField has been used.
    #student = models.ForeignKey(User, db_index=True)
    #course_key
    course_key = CourseKeyField(max_length=255, db_index=True)
    #OLD course_key = models.CharField(max_length=255, db_index=True)
    module_key = LocationKeyField(max_length=255, db_index=True)

    # Module's display name
    display_name = models.CharField(max_length=255, db_index=True)

    play_events = models.TextField(db_index=False)
    pause_events = models.TextField(db_index=False)
    change_speed_events = models.TextField(db_index=False)
    seek_from_events = models.TextField(db_index=False)
    seek_to_events = models.TextField(db_index=False)

    class Meta:
        unique_together = (('student', 'module_key'), )

    def __repr__(self):
        return 'VideoEvents<%r>' % ({
            'student': self.student,
            'course_key': self.course_key,
            'module_key': self.module_key,
            'display_name': self.display_name,
            'play_events': self.play_events,
            'pause_events': self.pause_events,
            'change_speed_events': self.change_speed_events,
            'seek_from_events': self.seek_from_events,
            'seek_to_events': self.seek_to_events,
        }, )

    def __unicode__(self):
        return unicode(repr(self))
Beispiel #15
0
class StudentModuleMarketo(models.Model):
    """
    Keeps student state of "Marketo Completion" for a given Course module.
    We store this in edX db so we don't have to connect to Marketo API 
    every time something of interest happens with a Course.
    """
    MODEL_TAGS = ['course_id', 'module_type']

    # Key used to share state. This is the XBlock usage_id
    module_state_key = LocationKeyField(max_length=255,
                                        db_index=True,
                                        db_column='module_id')
    student = models.ForeignKey(User, db_index=True)

    course_id = CourseKeyField(max_length=255, db_index=True)

    class Meta:
        unique_together = (('student', 'module_state_key', 'course_id'), )

    # Internal state of the object
    marketo_complete = models.BooleanField(default=False)

    created = models.DateTimeField(auto_now_add=True, db_index=True)
    modified = models.DateTimeField(auto_now=True, db_index=True)

    def __repr__(self):
        return 'StudentModuleMarketo<%r>' % (
            {
                'course_id': self.course_id,
                'student': self.student.username,
                'module_state_key': self.module_state_key,
                'marketo_complete': self.marketo_complete,
            }, )

    def __unicode__(self):
        return unicode(repr(self))
Beispiel #16
0
class StudentModule(CallStackMixin, models.Model):
    """
    Keeps student state for a particular module in a particular course.
    """
    objects = ChunkingCallStackManager()
    MODEL_TAGS = ['course_id', 'module_type']

    # For a homework problem, contains a JSON
    # object consisting of state
    MODULE_TYPES = (('problem', 'problem'), ('video', 'video'),
                    ('html', 'html'), ('course', 'course'),
                    ('chapter', 'Section'), ('sequential', 'Subsection'),
                    ('library_content', 'Library Content'))
    ## These three are the key for the object
    module_type = models.CharField(max_length=32,
                                   choices=MODULE_TYPES,
                                   default='problem',
                                   db_index=True)

    # Key used to share state. This is the XBlock usage_id
    module_state_key = LocationKeyField(max_length=255,
                                        db_index=True,
                                        db_column='module_id')
    student = models.ForeignKey(User, db_index=True)

    course_id = CourseKeyField(max_length=255, db_index=True)

    class Meta(object):  # pylint: disable=missing-docstring
        unique_together = (('student', 'module_state_key', 'course_id'), )

    # Internal state of the object
    state = models.TextField(null=True, blank=True)

    # Grade, and are we done?
    grade = models.FloatField(null=True, blank=True, db_index=True)
    max_grade = models.FloatField(null=True, blank=True)
    DONE_TYPES = (
        ('na', 'NOT_APPLICABLE'),
        ('f', 'FINISHED'),
        ('i', 'INCOMPLETE'),
    )
    done = models.CharField(max_length=8,
                            choices=DONE_TYPES,
                            default='na',
                            db_index=True)

    created = models.DateTimeField(auto_now_add=True, db_index=True)
    modified = models.DateTimeField(auto_now=True, db_index=True)

    @classmethod
    def all_submitted_problems_read_only(cls, course_id):
        """
        Return all model instances that correspond to problems that have been
        submitted for a given course. So module_type='problem' and a non-null
        grade. Use a read replica if one exists for this environment.
        """
        queryset = cls.objects.filter(course_id=course_id,
                                      module_type='problem',
                                      grade__isnull=False)
        if "read_replica" in settings.DATABASES:
            return queryset.using("read_replica")
        else:
            return queryset

    def __repr__(self):
        return 'StudentModule<%r>' % (
            {
                'course_id': self.course_id,
                'module_type': self.module_type,
                # We use the student_id instead of username to avoid a database hop.
                # This can actually matter in cases where we're logging many of
                # these (e.g. on a broken progress page).
                'student_id': self.student_id,  # pylint: disable=no-member
                'module_state_key': self.module_state_key,
                'state': str(self.state)[:20],
            }, )

    def __unicode__(self):
        return unicode(repr(self))
Beispiel #17
0
class StudentModule(models.Model):
    """
    Keeps student state for a particular module in a particular course.
    """
    MODEL_TAGS = ['course_id', 'module_type']

    # For a homework problem, contains a JSON
    # object consisting of state
    MODULE_TYPES = (
        ('problem', 'problem'),
        ('video', 'video'),
        ('html', 'html'),
    )
    ## These three are the key for the object
    module_type = models.CharField(max_length=32,
                                   choices=MODULE_TYPES,
                                   default='problem',
                                   db_index=True)

    # Key used to share state. This is the XBlock usage_id
    module_state_key = LocationKeyField(max_length=255,
                                        db_index=True,
                                        db_column='module_id')
    student = models.ForeignKey(User, db_index=True)

    course_id = CourseKeyField(max_length=255, db_index=True)

    class Meta:
        unique_together = (('student', 'module_state_key', 'course_id'), )

    ## Internal state of the object
    state = models.TextField(null=True, blank=True)

    ## Grade, and are we done?
    grade = models.FloatField(null=True, blank=True, db_index=True)
    max_grade = models.FloatField(null=True, blank=True)
    DONE_TYPES = (
        ('na', 'NOT_APPLICABLE'),
        ('f', 'FINISHED'),
        ('i', 'INCOMPLETE'),
    )
    done = models.CharField(max_length=8,
                            choices=DONE_TYPES,
                            default='na',
                            db_index=True)

    created = models.DateTimeField(auto_now_add=True, db_index=True)
    modified = models.DateTimeField(auto_now=True, db_index=True)

    @classmethod
    def all_submitted_problems_read_only(cls, course_id):
        """
        Return all model instances that correspond to problems that have been
        submitted for a given course. So module_type='problem' and a non-null
        grade. Use a read replica if one exists for this environment.
        """
        queryset = cls.objects.filter(course_id=course_id,
                                      module_type='problem',
                                      grade__isnull=False)
        if "read_replica" in settings.DATABASES:
            return queryset.using("read_replica")
        else:
            return queryset

    def __repr__(self):
        return 'StudentModule<%r>' % ({
            'course_id': self.course_id,
            'module_type': self.module_type,
            'student': self.student.username,
            'module_state_key': self.module_state_key,
            'state': str(self.state)[:20],
        }, )

    def __unicode__(self):
        return unicode(repr(self))
Beispiel #18
0
class Bookmark(TimeStampedModel):
    """
    Bookmarks model.
    """
    user = models.ForeignKey(User, db_index=True)
    course_key = CourseKeyField(max_length=255, db_index=True)
    usage_key = LocationKeyField(max_length=255, db_index=True)
    _path = JSONField(db_column='path', help_text='Path in course tree to the block')

    xblock_cache = models.ForeignKey('bookmarks.XBlockCache')

    class Meta(object):
        """
        Bookmark metadata.
        """
        unique_together = ('user', 'usage_key')

    def __unicode__(self):
        return self.resource_id

    @classmethod
    def create(cls, data):
        """
        Create a Bookmark object.

        Arguments:
            data (dict): The data to create the object with.

        Returns:
            A Bookmark object.

        Raises:
            ItemNotFoundError: If no block exists for the usage_key.
        """
        data = dict(data)
        usage_key = data.pop('usage_key')

        with modulestore().bulk_operations(usage_key.course_key):
            block = modulestore().get_item(usage_key)

            xblock_cache = XBlockCache.create({
                'usage_key': usage_key,
                'display_name': block.display_name_with_default,
            })
            data['_path'] = prepare_path_for_serialization(Bookmark.updated_path(usage_key, xblock_cache))

        data['course_key'] = usage_key.course_key
        data['xblock_cache'] = xblock_cache

        user = data.pop('user')

        bookmark, created = cls.objects.get_or_create(usage_key=usage_key, user=user, defaults=data)
        return bookmark, created

    @property
    def resource_id(self):
        """
        Return the resource id: {username,usage_id}.
        """
        return "{0},{1}".format(self.user.username, self.usage_key)  # pylint: disable=no-member

    @property
    def display_name(self):
        """
        Return the display_name from self.xblock_cache.

        Returns:
            String.
        """
        return self.xblock_cache.display_name  # pylint: disable=no-member

    @property
    def path(self):
        """
        Return the path to the bookmark's block after checking self.xblock_cache.

        Returns:
            List of dicts.
        """
        if self.modified < self.xblock_cache.modified:  # pylint: disable=no-member
            path = Bookmark.updated_path(self.usage_key, self.xblock_cache)
            self._path = prepare_path_for_serialization(path)
            self.save()  # Always save so that self.modified is updated.
            return path

        return parse_path_data(self._path)

    @staticmethod
    def updated_path(usage_key, xblock_cache):
        """
        Return the update-to-date path.

        xblock_cache.paths is the list of all possible paths to a block
        constructed by doing a DFS of the tree. However, in case of DAGS,
        which section jump_to_id() takes the user to depends on the
        modulestore. If xblock_cache.paths has only one item, we can
        just use it. Otherwise, we use path_to_location() to get the path
        jump_to_id() will take the user to.
        """
        if xblock_cache.paths and len(xblock_cache.paths) == 1:
            return xblock_cache.paths[0]

        return Bookmark.get_path(usage_key)

    @staticmethod
    def get_path(usage_key):
        """
        Returns data for the path to the block in the course graph.

        Note: In case of multiple paths to the block from the course
        root, this function returns a path arbitrarily but consistently,
        depending on the modulestore. In the future, we may want to
        extend it to check which of the paths, the user has access to
        and return its data.

        Arguments:
            block (XBlock): The block whose path is required.

        Returns:
            list of PathItems
        """
        with modulestore().bulk_operations(usage_key.course_key):
            try:
                path = search.path_to_location(modulestore(), usage_key, full_path=True)
            except ItemNotFoundError:
                log.error(u'Block with usage_key: %s not found.', usage_key)
                return []
            except NoPathToItem:
                log.error(u'No path to block with usage_key: %s.', usage_key)
                return []

            path_data = []
            for ancestor_usage_key in path:
                if ancestor_usage_key != usage_key and ancestor_usage_key.block_type != 'course':  # pylint: disable=no-member
                    try:
                        block = modulestore().get_item(ancestor_usage_key)
                    except ItemNotFoundError:
                        return []  # No valid path can be found.
                    path_data.append(
                        PathItem(usage_key=block.location, display_name=block.display_name_with_default)
                    )

        return path_data
Beispiel #19
0
class XBlockCache(TimeStampedModel):
    """
    XBlockCache model to store info about xblocks.
    """

    course_key = CourseKeyField(max_length=255, db_index=True)
    usage_key = LocationKeyField(max_length=255, db_index=True, unique=True)

    display_name = models.CharField(max_length=255, default='')
    _paths = JSONField(
        db_column='paths', default=[], help_text='All paths in course tree to the corresponding block.'
    )

    def __unicode__(self):
        return unicode(self.usage_key)

    @property
    def paths(self):
        """
        Return paths.

        Returns:
            list of list of PathItems.
        """
        return [parse_path_data(path) for path in self._paths] if self._paths else self._paths

    @paths.setter
    def paths(self, value):
        """
        Set paths.

        Arguments:
            value (list of list of PathItems): The list of paths to cache.
        """
        self._paths = [prepare_path_for_serialization(path) for path in value] if value else value

    @classmethod
    def create(cls, data):
        """
        Create an XBlockCache object.

        Arguments:
            data (dict): The data to create the object with.

        Returns:
            An XBlockCache object.
        """
        data = dict(data)

        usage_key = data.pop('usage_key')
        usage_key = usage_key.replace(course_key=modulestore().fill_in_run(usage_key.course_key))

        data['course_key'] = usage_key.course_key
        xblock_cache, created = cls.objects.get_or_create(usage_key=usage_key, defaults=data)

        if not created:
            new_display_name = data.get('display_name', xblock_cache.display_name)
            if xblock_cache.display_name != new_display_name:
                xblock_cache.display_name = new_display_name
                xblock_cache.save()

        return xblock_cache