Merge pull request #19675 from edx/juliasq/add_pii_annotations
Add PII annotations, un-pin stevedore, upgrade deps.
This commit is contained in:
@@ -17,6 +17,7 @@ from opaque_keys.edx.django.models import CourseKeyField
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from badges.utils import deserialize_count_specs
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
|
||||
@@ -47,6 +48,8 @@ class CourseBadgesDisabledError(Exception):
|
||||
class BadgeClass(models.Model):
|
||||
"""
|
||||
Specifies a badge class to be registered with a backend.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
slug = models.SlugField(max_length=255, validators=[validate_lowercase])
|
||||
issuing_component = models.SlugField(max_length=50, default='', blank=True, validators=[validate_lowercase])
|
||||
@@ -59,8 +62,8 @@ class BadgeClass(models.Model):
|
||||
image = models.ImageField(upload_to='badge_classes', validators=[validate_badge_image])
|
||||
|
||||
def __unicode__(self):
|
||||
return u"<Badge '{slug}' for '{issuing_component}'>".format(
|
||||
slug=self.slug, issuing_component=self.issuing_component
|
||||
return HTML(u"<Badge '{slug}' for '{issuing_component}'>").format(
|
||||
slug=HTML(self.slug), issuing_component=HTML(self.issuing_component)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -140,6 +143,8 @@ class BadgeClass(models.Model):
|
||||
class BadgeAssertion(TimeStampedModel):
|
||||
"""
|
||||
Tracks badges on our side of the badge baking transaction
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
badge_class = models.ForeignKey(BadgeClass, on_delete=models.CASCADE)
|
||||
@@ -149,9 +154,10 @@ class BadgeAssertion(TimeStampedModel):
|
||||
assertion_url = models.URLField()
|
||||
|
||||
def __unicode__(self):
|
||||
return u"<{username} Badge Assertion for {slug} for {issuing_component}".format(
|
||||
username=self.user.username, slug=self.badge_class.slug,
|
||||
issuing_component=self.badge_class.issuing_component,
|
||||
return HTML(u"<{username} Badge Assertion for {slug} for {issuing_component}").format(
|
||||
username=HTML(self.user.username),
|
||||
slug=HTML(self.badge_class.slug),
|
||||
issuing_component=HTML(self.badge_class.issuing_component),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -174,6 +180,8 @@ BadgeAssertion._meta.get_field('created').db_index = True
|
||||
class CourseCompleteImageConfiguration(models.Model):
|
||||
"""
|
||||
Contains the icon configuration for badges for a specific course mode.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
mode = models.CharField(
|
||||
max_length=125,
|
||||
@@ -197,9 +205,9 @@ class CourseCompleteImageConfiguration(models.Model):
|
||||
)
|
||||
|
||||
def __unicode__(self):
|
||||
return u"<CourseCompleteImageConfiguration for '{mode}'{default}>".format(
|
||||
mode=self.mode,
|
||||
default=u" (default)" if self.default else u''
|
||||
return HTML(u"<CourseCompleteImageConfiguration for '{mode}'{default}>").format(
|
||||
mode=HTML(self.mode),
|
||||
default=HTML(u" (default)") if self.default else HTML(u'')
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
@@ -228,6 +236,8 @@ class CourseEventBadgesConfiguration(ConfigurationModel):
|
||||
"""
|
||||
Determines the settings for meta course awards-- such as completing a certain
|
||||
number of courses or enrolling in a certain number of them.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
courses_completed = models.TextField(
|
||||
blank=True, default='',
|
||||
@@ -256,7 +266,9 @@ class CourseEventBadgesConfiguration(ConfigurationModel):
|
||||
)
|
||||
|
||||
def __unicode__(self):
|
||||
return u"<CourseEventBadgesConfiguration ({})>".format(u"Enabled" if self.enabled else u"Disabled")
|
||||
return HTML(u"<CourseEventBadgesConfiguration ({})>").format(
|
||||
Text(u"Enabled") if self.enabled else Text(u"Disabled")
|
||||
)
|
||||
|
||||
@property
|
||||
def completed_settings(self):
|
||||
|
||||
@@ -24,6 +24,8 @@ class BrandingInfoConfig(ConfigurationModel):
|
||||
"logo_tag": "Video hosted by XuetangX.com"
|
||||
}
|
||||
}
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(ConfigurationModel.Meta):
|
||||
app_label = "branding"
|
||||
@@ -57,6 +59,8 @@ class BrandingApiConfig(ConfigurationModel):
|
||||
When this flag is disabled, the api will return 404.
|
||||
|
||||
When the flag is enabled, the api will returns the valid reponse.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(ConfigurationModel.Meta):
|
||||
app_label = "branding"
|
||||
|
||||
@@ -27,6 +27,8 @@ log = logging.getLogger(__name__)
|
||||
class Email(models.Model):
|
||||
"""
|
||||
Abstract base class for common information for an email.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
sender = models.ForeignKey(User, default=1, blank=True, null=True, on_delete=models.CASCADE)
|
||||
slug = models.CharField(max_length=128, db_index=True)
|
||||
@@ -65,6 +67,8 @@ class Target(models.Model):
|
||||
SEND_TO_COHORT), then explicitly call the method on self.cohorttarget, which is created
|
||||
by django as part of this inheritance setup. These calls require pylint disable no-member in
|
||||
several locations in this class.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
target_type = models.CharField(max_length=64, choices=EMAIL_TARGET_CHOICES)
|
||||
|
||||
@@ -138,6 +142,8 @@ class Target(models.Model):
|
||||
class CohortTarget(Target):
|
||||
"""
|
||||
Subclass of Target, specifically referring to a cohort.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
cohort = models.ForeignKey('course_groups.CourseUserGroup', on_delete=models.CASCADE)
|
||||
|
||||
@@ -181,6 +187,8 @@ class CohortTarget(Target):
|
||||
class CourseModeTarget(Target):
|
||||
"""
|
||||
Subclass of Target, specifically for course modes.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
track = models.ForeignKey('course_modes.CourseMode', on_delete=models.CASCADE)
|
||||
|
||||
@@ -226,6 +234,8 @@ class CourseModeTarget(Target):
|
||||
class CourseEmail(Email):
|
||||
"""
|
||||
Stores information for an email to a course.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "bulk_email"
|
||||
@@ -302,6 +312,8 @@ class CourseEmail(Email):
|
||||
class Optout(models.Model):
|
||||
"""
|
||||
Stores users that have opted out of receiving emails from a course.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
# Allowing null=True to support data migration from email->user.
|
||||
# We need to first create the 'user' column with some sort of default in order to run the data migration,
|
||||
@@ -327,6 +339,8 @@ class CourseEmailTemplate(models.Model):
|
||||
Initialization takes place in a migration that in turn loads a fixture.
|
||||
The admin console interface disables add and delete operations.
|
||||
Validation is handled in the CourseEmailTemplateForm class.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "bulk_email"
|
||||
@@ -407,6 +421,8 @@ class CourseEmailTemplate(models.Model):
|
||||
class CourseAuthorization(models.Model):
|
||||
"""
|
||||
Enable the course email feature on a course-by-course basis.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "bulk_email"
|
||||
@@ -442,6 +458,8 @@ class BulkEmailFlag(ConfigurationModel):
|
||||
Staff can only send bulk email for a course if all the following conditions are true:
|
||||
1. BulkEmailFlag is enabled.
|
||||
2. Course-specific authorization not required, or course authorized to use bulk email.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
# boolean field 'enabled' inherited from parent ConfigurationModel
|
||||
require_course_email_auth = models.BooleanField(default=True)
|
||||
|
||||
@@ -23,6 +23,8 @@ log = logging.getLogger("edx.ccx")
|
||||
class CustomCourseForEdX(models.Model):
|
||||
"""
|
||||
A Custom Course.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
course_id = CourseKeyField(max_length=255, db_index=True)
|
||||
display_name = models.CharField(max_length=255)
|
||||
@@ -106,6 +108,8 @@ class CustomCourseForEdX(models.Model):
|
||||
class CcxFieldOverride(models.Model):
|
||||
"""
|
||||
Field overrides for custom courses.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
ccx = models.ForeignKey(CustomCourseForEdX, db_index=True, on_delete=models.CASCADE)
|
||||
location = UsageKeyField(max_length=255, db_index=True)
|
||||
|
||||
@@ -130,6 +130,8 @@ class CertificateWhitelist(models.Model):
|
||||
regardless of their grade unless they are on the
|
||||
embargoed country restriction list
|
||||
(allow_certificate set to False in userprofile).
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "certificates"
|
||||
@@ -213,6 +215,10 @@ class EligibleCertificateManager(models.Manager):
|
||||
class GeneratedCertificate(models.Model):
|
||||
"""
|
||||
Base model for generated certificates
|
||||
|
||||
.. pii: PII can exist in the generated certificate linked to in this model. Certificate data is currently retained.
|
||||
.. pii_types: name, username
|
||||
.. pii_retirement: retained
|
||||
"""
|
||||
# Import here instead of top of file since this module gets imported before
|
||||
# the course_modes app is loaded, resulting in a Django deprecation warning.
|
||||
@@ -374,6 +380,8 @@ class GeneratedCertificate(models.Model):
|
||||
class CertificateGenerationHistory(TimeStampedModel):
|
||||
"""
|
||||
Model for storing Certificate Generation History.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
course_id = CourseKeyField(max_length=255)
|
||||
@@ -436,6 +444,8 @@ class CertificateGenerationHistory(TimeStampedModel):
|
||||
class CertificateInvalidation(TimeStampedModel):
|
||||
"""
|
||||
Model for storing Certificate Invalidation.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
generated_certificate = models.ForeignKey(GeneratedCertificate, on_delete=models.CASCADE)
|
||||
invalidated_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
@@ -527,7 +537,7 @@ def certificate_status_for_student(student, course_id):
|
||||
|
||||
|
||||
def certificate_status(generated_certificate):
|
||||
'''
|
||||
"""
|
||||
This returns a dictionary with a key for status, and other information.
|
||||
The status is one of the following:
|
||||
|
||||
@@ -554,7 +564,7 @@ def certificate_status(generated_certificate):
|
||||
|
||||
If the student has been graded, the dictionary also contains their
|
||||
grade for the course with the key "grade".
|
||||
'''
|
||||
"""
|
||||
# Import here instead of top of file since this module gets imported before
|
||||
# the course_modes app is loaded, resulting in a Django deprecation warning.
|
||||
from course_modes.models import CourseMode
|
||||
@@ -610,7 +620,8 @@ def certificate_info_for_user(user, course_id, grade, user_is_whitelisted, user_
|
||||
|
||||
|
||||
class ExampleCertificateSet(TimeStampedModel):
|
||||
"""A set of example certificates.
|
||||
"""
|
||||
A set of example certificates.
|
||||
|
||||
Example certificates are used to verify that certificate
|
||||
generation is working for a particular course.
|
||||
@@ -619,6 +630,7 @@ class ExampleCertificateSet(TimeStampedModel):
|
||||
(e.g. honor and verified), in which case we generate
|
||||
multiple example certificates for the course.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
course_key = CourseKeyField(max_length=255, db_index=True)
|
||||
|
||||
@@ -701,7 +713,8 @@ def _make_uuid():
|
||||
|
||||
|
||||
class ExampleCertificate(TimeStampedModel):
|
||||
"""Example certificate.
|
||||
"""
|
||||
Example certificate.
|
||||
|
||||
Example certificates are used to verify that certificate
|
||||
generation is working for a particular course.
|
||||
@@ -717,6 +730,7 @@ class ExampleCertificate(TimeStampedModel):
|
||||
|
||||
3) We use dummy values.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "certificates"
|
||||
@@ -870,12 +884,15 @@ class ExampleCertificate(TimeStampedModel):
|
||||
|
||||
|
||||
class CertificateGenerationCourseSetting(TimeStampedModel):
|
||||
"""Enable or disable certificate generation for a particular course.
|
||||
"""
|
||||
Enable or disable certificate generation for a particular course.
|
||||
|
||||
In general, we should only enable self-generated certificates
|
||||
for a course once we successfully generate example certificates
|
||||
for the course. This is enforced in the UI layer, but
|
||||
not in the data layer.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
course_key = CourseKeyField(max_length=255, db_index=True)
|
||||
|
||||
@@ -973,6 +990,7 @@ class CertificateGenerationConfiguration(ConfigurationModel):
|
||||
will appear for courses that have enabled self-generated
|
||||
certificates.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(ConfigurationModel.Meta):
|
||||
app_label = "certificates"
|
||||
@@ -993,6 +1011,8 @@ class CertificateHtmlViewConfiguration(ConfigurationModel):
|
||||
"logo_src": "http://www.edx.org/static/images/honor-logo.png"
|
||||
}
|
||||
}
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(ConfigurationModel.Meta):
|
||||
app_label = "certificates"
|
||||
@@ -1029,6 +1049,7 @@ class CertificateTemplate(TimeStampedModel):
|
||||
A particular course may have several kinds of certificate templates
|
||||
(e.g. honor and verified).
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
name = models.CharField(
|
||||
max_length=255,
|
||||
@@ -1071,7 +1092,8 @@ class CertificateTemplate(TimeStampedModel):
|
||||
max_length=2,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=u'Only certificates for courses in the selected language will be rendered using this template. Course language is determined by the first two letters of the language code.'
|
||||
help_text=u'Only certificates for courses in the selected language will be rendered using this template. '
|
||||
u'Course language is determined by the first two letters of the language code.'
|
||||
)
|
||||
|
||||
def __unicode__(self):
|
||||
@@ -1104,6 +1126,7 @@ class CertificateTemplateAsset(TimeStampedModel):
|
||||
This model stores assets used in custom web certificate templates
|
||||
such as image, css files.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
description = models.CharField(
|
||||
max_length=255,
|
||||
|
||||
@@ -7,7 +7,11 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class CommerceConfiguration(ConfigurationModel):
|
||||
""" Commerce configuration """
|
||||
"""
|
||||
Commerce configuration
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
app_label = "commerce"
|
||||
|
||||
@@ -25,6 +25,8 @@ GOAL_KEY_CHOICES = Choices(
|
||||
class CourseGoal(models.Model):
|
||||
"""
|
||||
Represents a course goal set by a user on the course home page.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
user = models.ForeignKey(User, blank=False, on_delete=models.CASCADE)
|
||||
course_key = CourseKeyField(max_length=255, db_index=True)
|
||||
|
||||
@@ -27,6 +27,8 @@ from six import text_type
|
||||
import coursewarehistoryextended
|
||||
from opaque_keys.edx.django.models import BlockTypeKeyField, CourseKeyField, UsageKeyField
|
||||
|
||||
from openedx.core.djangolib.markup import HTML
|
||||
|
||||
log = logging.getLogger("edx.courseware")
|
||||
|
||||
|
||||
@@ -74,6 +76,8 @@ class ChunkingManager(models.Manager):
|
||||
class StudentModule(models.Model):
|
||||
"""
|
||||
Keeps student state for a particular module in a particular course.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
objects = ChunkingManager()
|
||||
MODEL_TAGS = ['course_id', 'module_type']
|
||||
@@ -175,8 +179,11 @@ class StudentModule(models.Model):
|
||||
|
||||
|
||||
class BaseStudentModuleHistory(models.Model):
|
||||
"""Abstract class containing most fields used by any class
|
||||
storing Student Module History"""
|
||||
"""
|
||||
Abstract class containing most fields used by any class storing Student Module History
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
objects = ChunkingManager()
|
||||
HISTORY_SAVING_TYPES = {'problem'}
|
||||
|
||||
@@ -265,6 +272,8 @@ class StudentModuleHistory(BaseStudentModuleHistory):
|
||||
class XBlockFieldBase(models.Model):
|
||||
"""
|
||||
Base class for all XBlock field storage.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
objects = ChunkingManager()
|
||||
|
||||
@@ -283,7 +292,10 @@ class XBlockFieldBase(models.Model):
|
||||
|
||||
def __unicode__(self):
|
||||
keys = [field.name for field in self._meta.get_fields() if field.name not in ('created', 'modified')]
|
||||
return u'{}<{!r}'.format(self.__class__.__name__, {key: getattr(self, key) for key in keys})
|
||||
return HTML(u'{}<{!r}').format(
|
||||
HTML(self.__class__.__name__),
|
||||
{key: HTML(getattr(self, key)) for key in keys}
|
||||
)
|
||||
|
||||
|
||||
class XModuleUserStateSummaryField(XBlockFieldBase):
|
||||
@@ -329,6 +341,8 @@ class XModuleStudentInfoField(XBlockFieldBase):
|
||||
class OfflineComputedGrade(models.Model):
|
||||
"""
|
||||
Table of grades computed offline for a given user and course.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
|
||||
course_id = CourseKeyField(max_length=255, db_index=True)
|
||||
@@ -350,6 +364,8 @@ class OfflineComputedGradeLog(models.Model):
|
||||
"""
|
||||
Log of when offline grades are computed.
|
||||
Use this to be able to show instructor when the last computed grades were done.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
@@ -371,6 +387,8 @@ class StudentFieldOverride(TimeStampedModel):
|
||||
Holds the value of a specific field overriden for a student. This is used
|
||||
by the code in the `lms.djangoapps.courseware.student_field_overrides` module to provide
|
||||
overrides of xblock fields on a per user basis.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
course_id = CourseKeyField(max_length=255, db_index=True)
|
||||
location = UsageKeyField(max_length=255, db_index=True)
|
||||
@@ -385,9 +403,12 @@ class StudentFieldOverride(TimeStampedModel):
|
||||
|
||||
|
||||
class DynamicUpgradeDeadlineConfiguration(ConfigurationModel):
|
||||
""" Dynamic upgrade deadline configuration.
|
||||
"""
|
||||
Dynamic upgrade deadline configuration.
|
||||
|
||||
This model controls the behavior of the dynamic upgrade deadline for self-paced courses.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = 'courseware'
|
||||
@@ -418,6 +439,8 @@ class CourseDynamicUpgradeDeadlineConfiguration(OptOutDynamicUpgradeDeadlineMixi
|
||||
|
||||
This model controls dynamic upgrade deadlines on a per-course run level, allowing course runs to
|
||||
have different deadlines or opt out of the functionality altogether.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
KEY_FIELDS = ('course_id',)
|
||||
|
||||
@@ -440,6 +463,8 @@ class OrgDynamicUpgradeDeadlineConfiguration(OptOutDynamicUpgradeDeadlineMixin,
|
||||
|
||||
This model controls dynamic upgrade deadlines on a per-org level, allowing organizations to
|
||||
have different deadlines or opt out of the functionality altogether.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
KEY_FIELDS = ('org_id',)
|
||||
|
||||
|
||||
@@ -7,7 +7,11 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class EmailMarketingConfiguration(ConfigurationModel):
|
||||
""" Email marketing configuration """
|
||||
"""
|
||||
Email marketing configuration
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
app_label = "email_marketing"
|
||||
|
||||
@@ -4,6 +4,9 @@ from model_utils.models import TimeStampedModel
|
||||
|
||||
|
||||
class ExperimentData(TimeStampedModel):
|
||||
"""
|
||||
.. no_pii:
|
||||
"""
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||
experiment_id = models.PositiveSmallIntegerField(
|
||||
null=False, blank=False, db_index=True, verbose_name='Experiment ID'
|
||||
@@ -23,6 +26,9 @@ class ExperimentData(TimeStampedModel):
|
||||
|
||||
|
||||
class ExperimentKeyValue(TimeStampedModel):
|
||||
"""
|
||||
.. no_pii:
|
||||
"""
|
||||
experiment_id = models.PositiveSmallIntegerField(
|
||||
null=False, blank=False, db_index=True, verbose_name='Experiment ID'
|
||||
)
|
||||
|
||||
@@ -17,6 +17,8 @@ class PersistentGradesEnabledFlag(ConfigurationModel):
|
||||
When this feature flag is set to true, individual courses
|
||||
must also have persistent grades enabled for the
|
||||
feature to take effect.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
# this field overrides course-specific settings to enable the feature for all courses
|
||||
enabled_for_all_courses = BooleanField(default=False)
|
||||
@@ -58,6 +60,8 @@ class CoursePersistentGradesFlag(ConfigurationModel):
|
||||
Enables persistent grades for a specific
|
||||
course. Only has an effect if the general
|
||||
flag above is set to True.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
KEY_FIELDS = ('course_id',)
|
||||
|
||||
@@ -76,7 +80,7 @@ class CoursePersistentGradesFlag(ConfigurationModel):
|
||||
|
||||
class ComputeGradesSetting(ConfigurationModel):
|
||||
"""
|
||||
...
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "grades"
|
||||
|
||||
@@ -131,6 +131,8 @@ class VisibleBlocks(models.Model):
|
||||
This state is represented using an array of BlockRecord, stored
|
||||
in the blocks_json field. A hash of this json array is used for lookup
|
||||
purposes.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
blocks_json = models.TextField()
|
||||
hashed = models.CharField(max_length=100, unique=True)
|
||||
@@ -259,6 +261,8 @@ class VisibleBlocks(models.Model):
|
||||
class PersistentSubsectionGrade(TimeStampedModel):
|
||||
"""
|
||||
A django model tracking persistent grades at the subsection level.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
@@ -492,6 +496,8 @@ class PersistentSubsectionGrade(TimeStampedModel):
|
||||
class PersistentCourseGrade(TimeStampedModel):
|
||||
"""
|
||||
A django model tracking persistent course grades.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
@@ -626,6 +632,8 @@ class PersistentCourseGrade(TimeStampedModel):
|
||||
class PersistentSubsectionGradeOverride(models.Model):
|
||||
"""
|
||||
A django model tracking persistent grades overrides at the subsection level.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "grades"
|
||||
@@ -732,6 +740,8 @@ class PersistentSubsectionGradeOverride(models.Model):
|
||||
class PersistentSubsectionGradeOverrideHistory(models.Model):
|
||||
"""
|
||||
A django model tracking persistent grades override audit records.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
PROCTORING = 'PROCTORING'
|
||||
GRADEBOOK = 'GRADEBOOK'
|
||||
|
||||
@@ -10,5 +10,7 @@ class GradeReportSetting(ConfigurationModel):
|
||||
"""
|
||||
Sets the batch size used when running grade reports
|
||||
with multiple celery workers.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
batch_size = IntegerField(default=100)
|
||||
|
||||
@@ -58,6 +58,8 @@ class InstructorTask(models.Model):
|
||||
`requester` stores id of user who submitted the task
|
||||
`created` stores date that entry was first created
|
||||
`updated` stores date that entry was last modified
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "instructor_task"
|
||||
|
||||
@@ -14,6 +14,8 @@ from xblock.core import XBlockAside
|
||||
class XBlockAsidesConfig(ConfigurationModel):
|
||||
"""
|
||||
Configuration for XBlockAsides.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(ConfigurationModel.Meta):
|
||||
|
||||
@@ -25,6 +25,8 @@ class LtiConsumer(models.Model):
|
||||
Database model representing an LTI consumer. This model stores the consumer
|
||||
specific settings, such as the OAuth key/secret pair and any LTI fields
|
||||
that must be persisted.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
consumer_name = models.CharField(max_length=255, unique=True)
|
||||
consumer_key = models.CharField(max_length=32, unique=True, db_index=True, default=short_token)
|
||||
@@ -91,6 +93,8 @@ class OutcomeService(models.Model):
|
||||
Some LTI-specified fields use the prefix lis_; this refers to the IMS
|
||||
Learning Information Services standard from which LTI inherits some
|
||||
properties
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
lis_outcome_service_url = models.CharField(max_length=255, unique=True)
|
||||
lti_consumer = models.ForeignKey(LtiConsumer, on_delete=models.CASCADE)
|
||||
@@ -109,6 +113,8 @@ class GradedAssignment(models.Model):
|
||||
Some LTI-specified fields use the prefix lis_; this refers to the IMS
|
||||
Learning Information Services standard from which LTI inherits some
|
||||
properties
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
|
||||
course_key = CourseKeyField(max_length=255, db_index=True)
|
||||
@@ -127,6 +133,8 @@ class LtiUser(models.Model):
|
||||
The LTI user_id field is guaranteed to be unique per LTI consumer (per
|
||||
to the LTI spec), so we guarantee a unique mapping from LTI to edX account
|
||||
by using the lti_consumer/lti_user_id tuple.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
lti_consumer = models.ForeignKey(LtiConsumer, on_delete=models.CASCADE)
|
||||
lti_user_id = models.CharField(max_length=255)
|
||||
|
||||
@@ -14,6 +14,8 @@ class MobileApiConfig(ConfigurationModel):
|
||||
|
||||
The order in which the comma-separated list of names of profiles are given
|
||||
is in priority order.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
video_profiles = models.TextField(
|
||||
blank=True,
|
||||
@@ -34,6 +36,8 @@ class MobileApiConfig(ConfigurationModel):
|
||||
class AppVersionConfig(models.Model):
|
||||
"""
|
||||
Configuration for mobile app versions available.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
PLATFORM_CHOICES = tuple([
|
||||
(platform, platform)
|
||||
@@ -90,6 +94,8 @@ class IgnoreMobileAvailableFlagConfig(ConfigurationModel): # pylint: disable=W5
|
||||
Enabling this configuration will cause the mobile_available flag check in
|
||||
access.py._is_descriptor_mobile_available to ignore the mobile_available
|
||||
flag.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
|
||||
@@ -10,6 +10,14 @@ from six import text_type
|
||||
|
||||
|
||||
class Note(models.Model):
|
||||
"""
|
||||
Stores user Notes for the LMS local Notes service.
|
||||
|
||||
.. pii: Legacy model for an app that edx.org hasn't used since 2013
|
||||
.. pii_types: other
|
||||
.. pii_retirement: retained
|
||||
"""
|
||||
|
||||
user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
|
||||
course_id = CourseKeyField(max_length=255, db_index=True)
|
||||
uri = models.CharField(max_length=255, db_index=True)
|
||||
|
||||
@@ -9,6 +9,8 @@ class WhitelistedRssUrl(TimeStampedModel):
|
||||
"""
|
||||
Model for persisting RSS feed URLs which are whitelisted
|
||||
for proxying via this rss_proxy djangoapp.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
url = models.CharField(max_length=255, unique=True, db_index=True)
|
||||
|
||||
|
||||
@@ -110,6 +110,10 @@ class Order(models.Model):
|
||||
This is the model for an order. Before purchase, an Order and its related OrderItems are used
|
||||
as the shopping cart.
|
||||
FOR ANY USER, THERE SHOULD ONLY EVER BE ZERO OR ONE ORDER WITH STATUS='cart'.
|
||||
|
||||
.. pii: Contains many PII fields in an app edx.org does not currently use. "other" data is payment information.
|
||||
.. pii_types: name, location, email_address, other
|
||||
.. pii_retirement: retained
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -639,6 +643,8 @@ class OrderItem(TimeStampedModel):
|
||||
|
||||
Each implementation of OrderItem should provide its own purchased_callback as
|
||||
a method.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -824,6 +830,10 @@ class Invoice(TimeStampedModel):
|
||||
This table capture all the information needed to support "invoicing"
|
||||
which is when a user wants to purchase Registration Codes,
|
||||
but will not do so via a Credit Card transaction.
|
||||
|
||||
.. pii: Contains many PII fields in an app edx.org does not currently use
|
||||
.. pii_types: name, location, email_address
|
||||
.. pii_retirement: retained
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -996,6 +1006,7 @@ class InvoiceTransaction(TimeStampedModel):
|
||||
create a transaction with a negative amount to represent
|
||||
the refund.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -1086,6 +1097,8 @@ class InvoiceItem(TimeStampedModel):
|
||||
there might be an invoice item representing 10 registration
|
||||
codes for the DemoX course.
|
||||
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -1130,6 +1143,7 @@ class CourseRegistrationCodeInvoiceItem(InvoiceItem):
|
||||
This is an invoice item that represents a payment for
|
||||
a course registration.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -1166,6 +1180,7 @@ class InvoiceHistory(models.Model):
|
||||
transaction, so the history record is created only
|
||||
if the invoice change is successfully persisted.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
timestamp = models.DateTimeField(auto_now_add=True, db_index=True)
|
||||
invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE)
|
||||
@@ -1225,6 +1240,8 @@ class CourseRegistrationCode(models.Model):
|
||||
"""
|
||||
This table contains registration codes
|
||||
With registration code, a user can register for a course for free
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -1263,6 +1280,8 @@ class CourseRegistrationCode(models.Model):
|
||||
class RegistrationCodeRedemption(models.Model):
|
||||
"""
|
||||
This model contains the registration-code redemption info
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -1323,6 +1342,8 @@ class Coupon(models.Model):
|
||||
"""
|
||||
This table contains coupon codes
|
||||
A user can get a discount offer on course if provide coupon code
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -1350,6 +1371,8 @@ class Coupon(models.Model):
|
||||
class CouponRedemption(models.Model):
|
||||
"""
|
||||
This table contain coupon redemption info
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -1466,6 +1489,8 @@ class CouponRedemption(models.Model):
|
||||
class PaidCourseRegistration(OrderItem):
|
||||
"""
|
||||
This is an inventory item for paying for a course registration
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -1660,6 +1685,8 @@ class CourseRegCodeItem(OrderItem):
|
||||
"""
|
||||
This is an inventory item for paying for
|
||||
generating course registration codes
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -1827,6 +1854,8 @@ class CourseRegCodeItemAnnotation(models.Model):
|
||||
generates report for the paid courses, each report item must contain the payment account associated with a course.
|
||||
And unfortunately we didn't have the concept of a "SKU" or stock item where we could keep this association,
|
||||
so this is to retrofit it.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -1844,6 +1873,8 @@ class PaidCourseRegistrationAnnotation(models.Model):
|
||||
generates report for the paid courses, each report item must contain the payment account associated with a course.
|
||||
And unfortunately we didn't have the concept of a "SKU" or stock item where we could keep this association,
|
||||
so this is to retrofit it.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -1858,6 +1889,8 @@ class PaidCourseRegistrationAnnotation(models.Model):
|
||||
class CertificateItem(OrderItem):
|
||||
"""
|
||||
This is an inventory item for purchasing certificates
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "shoppingcart"
|
||||
@@ -2082,16 +2115,23 @@ class CertificateItem(OrderItem):
|
||||
|
||||
|
||||
class DonationConfiguration(ConfigurationModel):
|
||||
"""Configure whether donations are enabled on the site."""
|
||||
"""
|
||||
Configure whether donations are enabled on the site.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(ConfigurationModel.Meta):
|
||||
app_label = "shoppingcart"
|
||||
|
||||
|
||||
class Donation(OrderItem):
|
||||
"""A donation made by a user.
|
||||
"""
|
||||
A donation made by a user.
|
||||
|
||||
Donations can be made for a specific course or to the organization as a whole.
|
||||
Users can choose the donation amount.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
|
||||
@@ -24,6 +24,8 @@ class SurveyForm(TimeStampedModel):
|
||||
that is presented to the end user. A SurveyForm is not tied to
|
||||
a particular run of a course, to allow for sharing of Surveys
|
||||
across courses
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
name = models.CharField(max_length=255, db_index=True, unique=True)
|
||||
form = models.TextField()
|
||||
@@ -162,8 +164,13 @@ class SurveyForm(TimeStampedModel):
|
||||
|
||||
|
||||
class SurveyAnswer(TimeStampedModel):
|
||||
# pylint: disable=line-too-long
|
||||
"""
|
||||
Model for the answers that a user gives for a particular form in a course
|
||||
|
||||
.. pii: These are free-form questions asked by course authors. Types below are current as of Feb 2019, new ones could be added. "other" PII currently includes "company", "job title", and "work experience".
|
||||
.. pii_types: name, location, other
|
||||
.. pii_retirement: retained
|
||||
"""
|
||||
user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
|
||||
form = models.ForeignKey(SurveyForm, db_index=True, on_delete=models.CASCADE)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""Django models related to teams functionality."""
|
||||
"""
|
||||
Django models related to teams functionality.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
@@ -39,16 +41,20 @@ from .errors import AlreadyOnTeamInCourse, ImmutableMembershipFieldException, No
|
||||
@receiver(comment_voted)
|
||||
@receiver(comment_created)
|
||||
def post_create_vote_handler(sender, **kwargs): # pylint: disable=unused-argument
|
||||
"""Update the user's last activity date upon creating or voting for a
|
||||
post."""
|
||||
"""
|
||||
Update the user's last activity date upon creating or voting for a
|
||||
post.
|
||||
"""
|
||||
handle_activity(kwargs['user'], kwargs['post'])
|
||||
|
||||
|
||||
@receiver(thread_followed)
|
||||
@receiver(thread_unfollowed)
|
||||
def post_followed_unfollowed_handler(sender, **kwargs): # pylint: disable=unused-argument
|
||||
"""Update the user's last activity date upon followed or unfollowed of a
|
||||
post."""
|
||||
"""
|
||||
Update the user's last activity date upon followed or unfollowed of a
|
||||
post.
|
||||
"""
|
||||
handle_activity(kwargs['user'], kwargs['post'])
|
||||
|
||||
|
||||
@@ -57,21 +63,26 @@ def post_followed_unfollowed_handler(sender, **kwargs): # pylint: disable=unuse
|
||||
@receiver(comment_edited)
|
||||
@receiver(comment_deleted)
|
||||
def post_edit_delete_handler(sender, **kwargs): # pylint: disable=unused-argument
|
||||
"""Update the user's last activity date upon editing or deleting a
|
||||
post."""
|
||||
"""
|
||||
Update the user's last activity date upon editing or deleting a
|
||||
post.
|
||||
"""
|
||||
post = kwargs['post']
|
||||
handle_activity(kwargs['user'], post, long(post.user_id))
|
||||
|
||||
|
||||
@receiver(comment_endorsed)
|
||||
def comment_endorsed_handler(sender, **kwargs): # pylint: disable=unused-argument
|
||||
"""Update the user's last activity date upon endorsing a comment."""
|
||||
"""
|
||||
Update the user's last activity date upon endorsing a comment.
|
||||
"""
|
||||
comment = kwargs['post']
|
||||
handle_activity(kwargs['user'], comment, long(comment.thread.user_id))
|
||||
|
||||
|
||||
def handle_activity(user, post, original_author_id=None):
|
||||
"""Handle user activity from django_comment_client and discussion_api
|
||||
"""
|
||||
Handle user activity from django_comment_client and discussion_api
|
||||
and update the user's last activity date. Checks if the user who
|
||||
performed the action is the original author, and that the
|
||||
discussion has the team context.
|
||||
@@ -83,7 +94,11 @@ def handle_activity(user, post, original_author_id=None):
|
||||
|
||||
|
||||
class CourseTeam(models.Model):
|
||||
"""This model represents team related info."""
|
||||
"""
|
||||
This model represents team related info.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
app_label = "teams"
|
||||
@@ -165,7 +180,11 @@ class CourseTeam(models.Model):
|
||||
|
||||
|
||||
class CourseTeamMembership(models.Model):
|
||||
"""This model represents the membership of a single user in a single team."""
|
||||
"""
|
||||
This model represents the membership of a single user in a single team.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
app_label = "teams"
|
||||
|
||||
@@ -93,6 +93,10 @@ class IDVerificationAttempt(StatusModel):
|
||||
Each IDVerificationAttempt represents a Student's attempt to establish
|
||||
their identity through one of several methods that inherit from this Model,
|
||||
including PhotoVerification and SSOVerification.
|
||||
|
||||
.. pii: The User's name is stored in this and sub-models
|
||||
.. pii_types: name
|
||||
.. pii_retirement: retained
|
||||
"""
|
||||
STATUS = Choices('created', 'ready', 'submitted', 'must_retry', 'approved', 'denied')
|
||||
user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
|
||||
@@ -142,6 +146,10 @@ class ManualVerification(IDVerificationAttempt):
|
||||
"""
|
||||
Each ManualVerification represents a user's verification that bypasses the need for
|
||||
any other verification.
|
||||
|
||||
.. pii: The User's name is stored in the parent model
|
||||
.. pii_types: name
|
||||
.. pii_retirement: retained
|
||||
"""
|
||||
|
||||
reason = models.CharField(
|
||||
@@ -173,6 +181,8 @@ class SSOVerification(IDVerificationAttempt):
|
||||
Each SSOVerification represents a Student's attempt to establish their identity
|
||||
by signing in with SSO. ID verification through SSO bypasses the need for
|
||||
photo verification.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
OAUTH2 = 'third_party_auth.models.OAuth2ProviderConfig'
|
||||
@@ -254,6 +264,10 @@ class PhotoVerification(IDVerificationAttempt):
|
||||
attempt.status == PhotoVerification.STATUS.created
|
||||
attempt.status == "created"
|
||||
pending_requests = PhotoVerification.submitted.all()
|
||||
|
||||
.. pii: The User's name is stored in the parent model, this one stores links to face and photo ID images
|
||||
.. pii_types: name, image
|
||||
.. pii_retirement: retained
|
||||
"""
|
||||
######################## Fields Set During Creation ########################
|
||||
# See class docstring for description of status states
|
||||
@@ -526,6 +540,10 @@ class SoftwareSecurePhotoVerification(PhotoVerification):
|
||||
|
||||
Note: this model handles *inital* verifications (which you must perform
|
||||
at the time you register for a verified cert).
|
||||
|
||||
.. pii: The User's name is stored in the parent model, this one stores links to face and photo ID images
|
||||
.. pii_types: name, image
|
||||
.. pii_retirement: retained
|
||||
"""
|
||||
# This is a base64.urlsafe_encode(rsa_encrypt(photo_id_aes_key), ss_pub_key)
|
||||
# So first we generate a random AES-256 key to encrypt our photo ID with.
|
||||
@@ -909,6 +927,8 @@ class VerificationDeadline(TimeStampedModel):
|
||||
If no verification deadline record exists for a course,
|
||||
then that course does not have a deadline. This means that users
|
||||
can submit photos at any time.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
app_label = "verify_student"
|
||||
|
||||
Reference in New Issue
Block a user