Merge pull request #19675 from edx/juliasq/add_pii_annotations

Add PII annotations, un-pin stevedore, upgrade deps.
This commit is contained in:
Brian Mesick
2019-02-19 13:46:59 -05:00
committed by GitHub
82 changed files with 1115 additions and 98 deletions

View File

@@ -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):

View File

@@ -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"

View File

@@ -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)

View File

@@ -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)

View File

@@ -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,

View File

@@ -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"

View File

@@ -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)

View File

@@ -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',)

View File

@@ -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"

View File

@@ -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'
)

View File

@@ -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"

View File

@@ -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'

View File

@@ -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)

View File

@@ -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"

View File

@@ -14,6 +14,8 @@ from xblock.core import XBlockAside
class XBlockAsidesConfig(ConfigurationModel):
"""
Configuration for XBlockAsides.
.. no_pii:
"""
class Meta(ConfigurationModel.Meta):

View File

@@ -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)

View File

@@ -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):

View File

@@ -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)

View File

@@ -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)

View File

@@ -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):

View File

@@ -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)

View File

@@ -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"

View File

@@ -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"