class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='Bookmark', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)), ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)), ('course_key', CourseKeyField(max_length=255, db_index=True)), ('usage_key', LocationKeyField(max_length=255, db_index=True)), ('_path', jsonfield.fields.JSONField(help_text=b'Path in course tree to the block', db_column=b'path')), ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( name='XBlockCache', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)), ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)), ('course_key', CourseKeyField(max_length=255, db_index=True)), ('usage_key', LocationKeyField(unique=True, max_length=255, db_index=True)), ('display_name', models.CharField(default=b'', max_length=255)), ('_paths', jsonfield.fields.JSONField(default=[], help_text=b'All paths in course tree to the corresponding block.', db_column=b'paths')), ], options={ 'abstract': False, }, ), migrations.AddField( model_name='bookmark', name='xblock_cache', field=models.ForeignKey(to='bookmarks.XBlockCache'), ), migrations.AlterUniqueTogether( name='bookmark', unique_together=set([('user', 'usage_key')]), ), ]
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)
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): app_label = 'ccx' unique_together = (('ccx', 'location', 'field'),) value = models.TextField(default='null')
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): app_label = "courseware" unique_together = (('course_id', 'field', 'location', 'student'), ) field = models.CharField(max_length=255) value = models.TextField(default='null')
class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='CcxFieldOverride', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('location', LocationKeyField(max_length=255, db_index=True)), ('field', models.CharField(max_length=255)), ('value', models.TextField(default=b'null')), ], ), migrations.CreateModel( name='CustomCourseForEdX', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('course_id', CourseKeyField(max_length=255, db_index=True)), ('display_name', models.CharField(max_length=255)), ('coach', models.ForeignKey(to=settings.AUTH_USER_MODEL)), ], ), migrations.AddField( model_name='ccxfieldoverride', name='ccx', field=models.ForeignKey(to='ccx.CustomCourseForEdX'), ), migrations.AlterUniqueTogether( name='ccxfieldoverride', unique_together=set([('ccx', 'location', 'field')]), ), ]
class StudentModule(models.Model): """ Keeps student state for a particular module in a particular course. """ objects = ChunkingManager() 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): app_label = "courseware" 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, 'module_state_key': self.module_state_key, 'state': str(self.state)[:20], }, ) def __unicode__(self): return unicode(repr(self)) @classmethod def get_state_by_params(cls, course_id, module_state_keys, student_id=None): """ Return all model instances that correspond to a course and module keys. Student ID is optional keyword argument, if provided it narrows down the instances. """ module_states = cls.objects.filter( course_id=course_id, module_state_key__in=module_state_keys) if student_id: module_states = module_states.filter(student_id=student_id) return module_states
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 u"{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
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
class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='OfflineComputedGrade', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('course_id', CourseKeyField(max_length=255, db_index=True)), ('created', models.DateTimeField(db_index=True, auto_now_add=True, null=True)), ('updated', models.DateTimeField(auto_now=True, db_index=True)), ('gradeset', models.TextField(null=True, blank=True)), ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( name='OfflineComputedGradeLog', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('course_id', CourseKeyField(max_length=255, db_index=True)), ('created', models.DateTimeField(db_index=True, auto_now_add=True, null=True)), ('seconds', models.IntegerField(default=0)), ('nstudents', models.IntegerField(default=0)), ], options={ 'ordering': ['-created'], 'get_latest_by': 'created', }, ), migrations.CreateModel( name='StudentFieldOverride', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('created', model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, verbose_name='created', editable=False)), ('modified', model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, verbose_name='modified', editable=False)), ('course_id', CourseKeyField(max_length=255, db_index=True)), ('location', LocationKeyField(max_length=255, db_index=True)), ('field', models.CharField(max_length=255)), ('value', models.TextField(default=b'null')), ('student', models.ForeignKey(to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( name='StudentModule', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('module_type', models.CharField(default=b'problem', max_length=32, db_index=True, choices=[(b'problem', b'problem'), (b'video', b'video'), (b'html', b'html'), (b'course', b'course'), (b'chapter', b'Section'), (b'sequential', b'Subsection'), (b'library_content', b'Library Content')])), ('module_state_key', LocationKeyField(max_length=255, db_column=b'module_id', db_index=True)), ('course_id', CourseKeyField(max_length=255, db_index=True)), ('state', models.TextField(null=True, blank=True)), ('grade', models.FloatField(db_index=True, null=True, blank=True)), ('max_grade', models.FloatField(null=True, blank=True)), ('done', models.CharField(default=b'na', max_length=8, db_index=True, choices=[(b'na', b'NOT_APPLICABLE'), (b'f', b'FINISHED'), (b'i', b'INCOMPLETE')])), ('created', models.DateTimeField(auto_now_add=True, db_index=True)), ('modified', models.DateTimeField(auto_now=True, db_index=True)), ('student', models.ForeignKey(to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( name='StudentModuleHistory', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('version', models.CharField(db_index=True, max_length=255, null=True, blank=True)), ('created', models.DateTimeField(db_index=True)), ('state', models.TextField(null=True, blank=True)), ('grade', models.FloatField(null=True, blank=True)), ('max_grade', models.FloatField(null=True, blank=True)), ('student_module', models.ForeignKey(to='courseware.StudentModule')), ], options={ 'get_latest_by': 'created', }, ), migrations.CreateModel( name='XModuleStudentInfoField', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('field_name', models.CharField(max_length=64, db_index=True)), ('value', models.TextField(default=b'null')), ('created', models.DateTimeField(auto_now_add=True, db_index=True)), ('modified', models.DateTimeField(auto_now=True, db_index=True)), ('student', models.ForeignKey(to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( name='XModuleStudentPrefsField', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('field_name', models.CharField(max_length=64, db_index=True)), ('value', models.TextField(default=b'null')), ('created', models.DateTimeField(auto_now_add=True, db_index=True)), ('modified', models.DateTimeField(auto_now=True, db_index=True)), ('module_type', BlockTypeKeyField(max_length=64, db_index=True)), ('student', models.ForeignKey(to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( name='XModuleUserStateSummaryField', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('field_name', models.CharField(max_length=64, db_index=True)), ('value', models.TextField(default=b'null')), ('created', models.DateTimeField(auto_now_add=True, db_index=True)), ('modified', models.DateTimeField(auto_now=True, db_index=True)), ('usage_id', LocationKeyField(max_length=255, db_index=True)), ], ), migrations.AlterUniqueTogether( name='xmoduleuserstatesummaryfield', unique_together=set([('usage_id', 'field_name')]), ), migrations.AlterUniqueTogether( name='xmodulestudentprefsfield', unique_together=set([('student', 'module_type', 'field_name')]), ), migrations.AlterUniqueTogether( name='xmodulestudentinfofield', unique_together=set([('student', 'field_name')]), ), migrations.AlterUniqueTogether( name='studentmodule', unique_together=set([('student', 'module_state_key', 'course_id') ]), ), migrations.AlterUniqueTogether( name='studentfieldoverride', unique_together=set([('course_id', 'field', 'location', 'student') ]), ), migrations.AlterUniqueTogether( name='offlinecomputedgrade', unique_together=set([('user', 'course_id')]), ), ]