From e4789751051fbf22047a4a6ce4a4453a87c24aa0 Mon Sep 17 00:00:00 2001 From: Brian Mesick Date: Tue, 5 Nov 2024 12:21:29 -0500 Subject: [PATCH] chore: Add missing PII annotations, update safelist PII Annotations are very out of date, this commit adds most that were missing in edx-platform, and some additional annotations to the safelist. It is not comprehensive, several other upstream Open edX packages also need to be updated. It also does not include removing annotations that have been moved upstream, or been removed entirely. Those are separate follow-on tasks. --- .annotation_safe_list.yml | 96 ++++++++++++++++++- .../student/models/course_enrollment.py | 4 +- common/djangoapps/student/models/user.py | 4 + lms/djangoapps/course_goals/models.py | 2 + lms/djangoapps/course_home_api/models.py | 2 + lms/djangoapps/courseware/models.py | 2 + lms/djangoapps/experiments/models.py | 1 + lms/djangoapps/support/models.py | 4 + lms/djangoapps/user_tours/models.py | 2 + lms/djangoapps/verify_student/models.py | 10 +- openedx/core/djangoapps/agreements/models.py | 6 ++ .../content/learning_sequences/models.py | 24 +++++ .../djangoapps/content_libraries/models.py | 10 ++ .../djangoapps/content_tagging/models/base.py | 2 + openedx/core/djangoapps/course_live/models.py | 2 + openedx/core/djangoapps/discussions/models.py | 6 ++ openedx/core/djangoapps/programs/models.py | 6 ++ openedx/core/djangoapps/user_api/models.py | 2 + .../core/djangoapps/video_config/models.py | 3 +- openedx/features/announcements/models.py | 6 +- openedx/features/discounts/models.py | 4 + 21 files changed, 188 insertions(+), 10 deletions(-) diff --git a/.annotation_safe_list.yml b/.annotation_safe_list.yml index a5e997bf2f4a..c57eeeb2503d 100644 --- a/.annotation_safe_list.yml +++ b/.annotation_safe_list.yml @@ -9,13 +9,13 @@ # Via Django auth.Group: - ".. no_pii:" : "No PII" + ".. no_pii:": "No PII" auth.Permission: - ".. no_pii:" : "No PII" + ".. no_pii:": "No PII" auth.User: ".. pii:": "Contains username, password, and email address, retired in AccountRetirementView" - ".. pii_types:" : username, email_address, password - ".. pii_retirement:" : local_api + ".. pii_types:": username, email_address, password + ".. pii_retirement:": local_api contenttypes.ContentType: ".. no_pii:": "No PII" admin.LogEntry: @@ -27,6 +27,66 @@ sessions.Session: sites.Site: ".. no_pii:": "No PII" +# Automatically generated edx-platform models that can't be annotated +calendar_sync.HistoricalUserCalendarSyncConfig: + ".. no_pii:": "No PII" +certificates.HistoricalCertificateAllowlist: + ".. no_pii:": "No PII" +certificates.HistoricalCertificateDateOverride: + ".. no_pii:": "No PII" +certificates.HistoricalCertificateInvalidation: + ".. no_pii:": "No PII" +certificates.HistoricalGeneratedCertificate: + ".. 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" +course_apps.HistoricalCourseAppStatus: + ".. no_pii:": "No PII" +course_goals.HistoricalCourseGoal: + ".. no_pii:": "No PII" +course_live.HistoricalCourseLiveConfiguration: + ".. no_pii:": "No PII" +course_modes.HistoricalCourseMode: + ".. no_pii:": "No PII" +course_overviews.HistoricalCourseOverview: + ".. no_pii:": "No PII" +discussions.HistoricalDiscussionsConfiguration: + ".. no_pii:": "No PII" +entitlements.HistoricalCourseEntitlement: + ".. no_pii:": "No PII" +entitlements.HistoricalCourseEntitlementSupportDetail: + ".. no_pii:": "No PII" +experiments.HistoricalExperimentKeyValue: + ".. no_pii:": "No PII" +external_user_ids.HistoricalExternalId: + ".. no_pii:": "We store external_user_id here, but do not consider that PII under OEP-30." +external_user_ids.HistoricalExternalIdType: + ".. no_pii:": "No PII" +grades.HistoricalPersistentSubsectionGradeOverride: + ".. no_pii:": "No PII" +instructor_task.HistoricalInstructorTaskSchedule: + ".. no_pii:": "No PII" +program_enrollments.HistoricalProgramCourseEnrollment: + ".. no_pii:": "No PII" +program_enrollments.HistoricalProgramEnrollment: + ".. pii:": "PII is found in the external key for a program enrollment" + ".. pii_types:": "other" + ".. pii_retirement:": "local_api" +programs.HistoricalProgramDiscussionsConfiguration: + ".. no_pii:": "No PII" +programs.HistoricalProgramLiveConfiguration: + ".. no_pii:": "No PII" +schedules.HistoricalSchedule: + ".. no_pii:": "No PII" +split_modulestore_django.HistoricalSplitModulestoreCourseIndex: + ".. no_pii:": "No PII" +student.HistoricalCourseEnrollment: + ".. no_pii:": "No PII" +student.HistoricalManualEnrollmentAudit: + ".. pii:": "Contains enrolled_email, retired in LMSAccountRetirementView" + ".. pii_types:": "email_address" + ".. pii_retirement:": "local_api" + # Automatically generated models in edx-enterprise that can't be annotated there consent.HistoricalDataSharingConsent: ".. pii:": "The username field inherited from Consent contains PII." @@ -45,7 +105,7 @@ enterprise.HistoricalEnterpriseCustomerCatalog: enterprise.HistoricalEnterpriseCustomerEntitlement: ".. no_pii:": "No PII" -# Via ORA2 +# Via edx-ora2, these can be removed once the models are annotated for real assessment.Assessment: ".. no_pii:": "No PII" assessment.AssessmentFeedback: @@ -127,10 +187,24 @@ djcelery.TaskState: djcelery.WorkerState: ".. no_pii:": "No PII" +# Via django-celery-results +django_celery_results.ChordCounter: + ".. no_pii:": "No PII" +django_celery_results.GroupResult: + ".. no_pii:": "No PII" +django_celery_results.TaskResult: + ".. no_pii:": "No PII" + # Via edx-oauth2-provider https://github.com/edx/edx-oauth2-provider edx_oauth2_provider.TrustedClient: ".. no_pii:": "No PII" +# Via edx-name-affirmation, not part of the openedx org +edx_name_affirmation.HistoricalVerifiedName: + ".. pii:": "Contains name fields." + ".. pii_types:": "name" + ".. pii_retirement:": "local_api" + # Via VAL edxval.CourseVideo: ".. no_pii:": "No PII" @@ -149,6 +223,12 @@ edxval.VideoImage: edxval.VideoTranscript: ".. no_pii:": "No PII" +# Via PyLTI1p3 +lti1p3_tool_config.LtiTool: + ".. no_pii:": "No PII" +lti1p3_tool_config.LtiToolKey: + ".. no_pii:": "No PII" + # Via Milestones milestones.CourseContentMilestone: ".. no_pii:": "No PII" @@ -190,6 +270,10 @@ oauth2_provider.Grant: ".. pii:": "Contains 3rd party authentication secrets. Retired in DeactivateLogoutView." ".. pii_types:": password, other ".. pii_retirement:": local_api +oauth2_provider.IDToken: + ".. pii:": "Contains 3rd party authentication secrets, currently this is retained until the token times out, but should be retired explicitly with the other models from this package." + ".. pii_types:": password, other + ".. pii_retirement:": retained oauth2_provider.RefreshToken: ".. pii:": "Contains 3rd party authentication secrets. Retired in DeactivateLogoutView." ".. pii_types:": password, other @@ -250,6 +334,8 @@ submissions.StudentItem: ".. no_pii:": "No PII" submissions.Submission: ".. no_pii:": "No PII" +submissions.TeamSubmission: + ".. no_pii:": "No PII" # Via sorl-thumbnail https://github.com/jazzband/sorl-thumbnail thumbnail.KVStore: diff --git a/common/djangoapps/student/models/course_enrollment.py b/common/djangoapps/student/models/course_enrollment.py index 09862916e321..750ac66e38c0 100644 --- a/common/djangoapps/student/models/course_enrollment.py +++ b/common/djangoapps/student/models/course_enrollment.py @@ -1750,7 +1750,7 @@ def refund_window(self, refund_window): class BulkUnenrollConfiguration(ConfigurationModel): # lint-amnesty, pylint: disable=empty-docstring """ - + .. no_pii: """ csv_file = models.FileField( validators=[FileExtensionValidator(allowed_extensions=['csv'])], @@ -1763,6 +1763,8 @@ class BulkUnenrollConfiguration(ConfigurationModel): # lint-amnesty, pylint: di class BulkChangeEnrollmentConfiguration(ConfigurationModel): """ config model for the bulk_change_enrollment_csv command + + .. no_pii: """ csv_file = models.FileField( validators=[FileExtensionValidator(allowed_extensions=['csv'])], diff --git a/common/djangoapps/student/models/user.py b/common/djangoapps/student/models/user.py index aa3de546ef2b..9d979beb19cb 100644 --- a/common/djangoapps/student/models/user.py +++ b/common/djangoapps/student/models/user.py @@ -1685,6 +1685,8 @@ class AllowedAuthUser(TimeStampedModel): class AccountRecoveryConfiguration(ConfigurationModel): """ configuration model for recover account management command + + .. no_pii: """ csv_file = models.FileField( validators=[FileExtensionValidator(allowed_extensions=['csv'])], @@ -1824,6 +1826,8 @@ def perform_streak_updates(cls, user, course_key, browser_timezone=None): class UserPasswordToggleHistory(TimeStampedModel): """ Keeps track of user password disable/enable history + + .. no_pii: """ user = models.ForeignKey(User, related_name='password_toggle_history', on_delete=models.CASCADE) comment = models.CharField(max_length=255, help_text=_("Add a reason"), blank=True, null=True) diff --git a/lms/djangoapps/course_goals/models.py b/lms/djangoapps/course_goals/models.py index 73d69d6bd6ce..616966600040 100644 --- a/lms/djangoapps/course_goals/models.py +++ b/lms/djangoapps/course_goals/models.py @@ -79,6 +79,8 @@ class CourseGoalReminderStatus(TimeStampedModel): Tracks whether we've sent a reminder about a particular goal this week. See the management command goal_reminder_email for more detail about how this is used. + + .. no_pii: """ class Meta: verbose_name_plural = "Course goal reminder statuses" diff --git a/lms/djangoapps/course_home_api/models.py b/lms/djangoapps/course_home_api/models.py index 62d1e0fda36d..94c79b8760c8 100644 --- a/lms/djangoapps/course_home_api/models.py +++ b/lms/djangoapps/course_home_api/models.py @@ -11,6 +11,8 @@ class DisableProgressPageStackedConfig(StackedConfigurationModel): """ Stacked Config Model for disabling the frontend-app-learning progress page + + .. no_pii: """ STACKABLE_FIELDS = ('disabled',) diff --git a/lms/djangoapps/courseware/models.py b/lms/djangoapps/courseware/models.py index b5cd3839c351..eacf2424de6c 100644 --- a/lms/djangoapps/courseware/models.py +++ b/lms/djangoapps/courseware/models.py @@ -545,6 +545,8 @@ class Meta: class FinancialAssistanceConfiguration(ConfigurationModel): """ Manages configuration for connecting to Financial Assistance backend service and using its API. + + .. no_pii: """ api_base_url = models.URLField( diff --git a/lms/djangoapps/experiments/models.py b/lms/djangoapps/experiments/models.py index 2e69d185d384..049cd9108292 100644 --- a/lms/djangoapps/experiments/models.py +++ b/lms/djangoapps/experiments/models.py @@ -37,6 +37,7 @@ class ExperimentKeyValue(TimeStampedModel): """ ExperimentData stores any generic key-value associated with experiments identified by experiment_id. + .. no_pii: """ experiment_id = models.PositiveSmallIntegerField( diff --git a/lms/djangoapps/support/models.py b/lms/djangoapps/support/models.py index 32df989eeddd..d8f6e2b9a1d1 100644 --- a/lms/djangoapps/support/models.py +++ b/lms/djangoapps/support/models.py @@ -21,6 +21,8 @@ class CourseResetCourseOptIn(TimeStampedModel): """ Model that represents a course which has opted in to the course reset feature. + + .. no_pii: """ course_id = CourseKeyField(max_length=255, unique=True) active = BooleanField() @@ -40,6 +42,8 @@ def all_active_course_ids(): class CourseResetAudit(TimeStampedModel): """ Model which records the course reset action's status and metadata + + .. no_pii: """ class CourseResetStatus(TextChoices): IN_PROGRESS = "in_progress" diff --git a/lms/djangoapps/user_tours/models.py b/lms/djangoapps/user_tours/models.py index 34bd7b28de27..23baacc15e1c 100644 --- a/lms/djangoapps/user_tours/models.py +++ b/lms/djangoapps/user_tours/models.py @@ -30,6 +30,8 @@ class CourseHomeChoices(models.TextChoices): class UserDiscussionsTours(models.Model): """ Model to track which discussions tours a user has seen. + + .. no_pii: """ tour_name = models.CharField(max_length=255) show_tour = models.BooleanField(default=True) diff --git a/lms/djangoapps/verify_student/models.py b/lms/djangoapps/verify_student/models.py index 7d055ffc70ab..1c10cf7d0a83 100644 --- a/lms/djangoapps/verify_student/models.py +++ b/lms/djangoapps/verify_student/models.py @@ -1177,8 +1177,10 @@ def deadline_for_course(cls, course_key): class SSPVerificationRetryConfig(ConfigurationModel): # pylint: disable=model-missing-unicode, useless-suppression """ - SSPVerificationRetryConfig used to inject arguments - to retry_failed_photo_verifications management command + SSPVerificationRetryConfig used to inject arguments + to retry_failed_photo_verifications management command + + .. no_pii: """ class Meta: @@ -1201,6 +1203,10 @@ class VerificationAttempt(StatusModel): Plugins that implement forms of IDV can store information about IDV attempts in this model for use across the platform. + + .. pii: Contains the name of the user + .. pii_types: name + .. pii_retirement: local_api """ user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE) name = models.CharField(blank=True, max_length=255) diff --git a/openedx/core/djangoapps/agreements/models.py b/openedx/core/djangoapps/agreements/models.py index 461d7936c4bb..2672a4f47b24 100644 --- a/openedx/core/djangoapps/agreements/models.py +++ b/openedx/core/djangoapps/agreements/models.py @@ -27,6 +27,8 @@ class Meta: class LTIPIITool(TimeStampedModel): """ This model stores the relationship between a course and the LTI tools in the course that share PII. + + .. no_pii: """ course_key = CourseKeyField(max_length=255, unique=True, db_index=True) lti_tools = models.JSONField() @@ -39,6 +41,8 @@ class Meta: class LTIPIISignature(TimeStampedModel): """ This model stores a user's acknowledgement to share PII via LTI tools in a particular course. + + .. no_pii: """ user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE) course_key = CourseKeyField(max_length=255, db_index=True) @@ -57,6 +61,8 @@ class Meta: class ProctoringPIISignature(TimeStampedModel): """ This model stores a user's acknowledgment to share PII via proctoring in a particular course. + + .. no_pii: """ user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE) course_key = CourseKeyField(max_length=255, db_index=True) diff --git a/openedx/core/djangoapps/content/learning_sequences/models.py b/openedx/core/djangoapps/content/learning_sequences/models.py index de0881abe5f6..5d2ee34bbc9f 100644 --- a/openedx/core/djangoapps/content/learning_sequences/models.py +++ b/openedx/core/djangoapps/content/learning_sequences/models.py @@ -53,6 +53,8 @@ class LearningContext(TimeStampedModel): because this table can contain things that are not courses. It is okay to make a foreign key against this table. + + .. no_pii: """ id = models.BigAutoField(primary_key=True) context_key = LearningContextKeyField( @@ -74,6 +76,8 @@ def __str__(self): class CourseContext(TimeStampedModel): """ A model containing course specific information e.g course_visibility + + .. no_pii: """ learning_context = models.OneToOneField( LearningContext, on_delete=models.CASCADE, primary_key=True, related_name="course_context" @@ -106,6 +110,8 @@ class LearningSequence(TimeStampedModel): CourseSectionSequence. It is okay to make a foreign key against this table. + + .. no_pii: """ id = models.BigAutoField(primary_key=True) learning_context = models.ForeignKey( @@ -131,6 +137,8 @@ class CourseContentVisibilityMixin(models.Model): We keep the XBlock field names here, even if they're somewhat misleading. Please read the comments carefully for each field. + + .. no_pii: """ # This is an obscure, OLX-only flag (there is no UI for it in Studio) that # lets you define a Sequence that is reachable by direct URL but not shown @@ -174,6 +182,8 @@ class UserPartitionGroup(models.Model): UserPartitionGroups are not associated with LearningSequence directly because User Partitions often carry course-level assumptions (e.g. Enrollment Track) that don't make sense outside of a Course. + + .. no_pii: """ id = models.BigAutoField(primary_key=True) partition_id = models.BigIntegerField(null=False) @@ -191,6 +201,8 @@ class Meta: class CourseSection(CourseContentVisibilityMixin, TimeStampedModel): """ Course Section data, mapping to the 'chapter' block type. + + .. no_pii: """ id = models.BigAutoField(primary_key=True) course_context = models.ForeignKey( @@ -225,6 +237,8 @@ class SectionPartitionGroup(models.Model): Used for the user_partition_groups ManyToManyField field in the CourseSection model above. Adds a cascading delete which will delete these many-to-many relations whenever a UserPartitionGroup or CourseSection object is deleted. + + .. no_pii: """ class Meta: unique_together = [ @@ -249,6 +263,8 @@ class CourseSectionSequence(CourseContentVisibilityMixin, TimeStampedModel): Do NOT make a foreign key against this table, as the values are deleted and re-created on course publish. + + .. no_pii: """ id = models.BigAutoField(primary_key=True) course_context = models.ForeignKey( @@ -289,6 +305,8 @@ class SectionSequencePartitionGroup(models.Model): Used for the user_partition_groups ManyToManyField field in the CourseSectionSequence model above. Adds a cascading delete which will delete these many-to-many relations whenever a UserPartitionGroup or CourseSectionSequence object is deleted. + + .. no_pii: """ class Meta: unique_together = [ @@ -303,6 +321,8 @@ class CourseSequenceExam(TimeStampedModel): """ This model stores XBlock information that affects outline level information pertaining to special exams + + .. no_pii: """ course_section_sequence = models.OneToOneField(CourseSectionSequence, on_delete=models.CASCADE, related_name='exam') @@ -318,6 +338,8 @@ class PublishReport(models.Model): All these fields could be derived with aggregate SQL functions, but it would be slower and make the admin code more complex. Since we only write at publish time, keeping things in sync is less of a concern. + + .. no_pii: """ learning_context = models.OneToOneField( LearningContext, on_delete=models.CASCADE, related_name='publish_report' @@ -350,6 +372,8 @@ class ContentError(models.Model): freeform messages. It is quite possible that at some point we will come up with a more comprehensive taxonomy of error messages, at which point we could do a backfill to regenerate this data in a more normalized way. + + .. no_pii: """ id = models.BigAutoField(primary_key=True) publish_report = models.ForeignKey( diff --git a/openedx/core/djangoapps/content_libraries/models.py b/openedx/core/djangoapps/content_libraries/models.py index 4a210223cc29..61e28b944851 100644 --- a/openedx/core/djangoapps/content_libraries/models.py +++ b/openedx/core/djangoapps/content_libraries/models.py @@ -89,6 +89,8 @@ class ContentLibrary(models.Model): re-imported on another Open edX instance should be kept in Learning Core. This model in Studio should only be used to track settings specific to this Open edX instance, like who has permission to edit this content library. + + .. no_pii: """ objects: ContentLibraryManager[ContentLibrary] = ContentLibraryManager() @@ -183,6 +185,8 @@ def __str__(self): class ContentLibraryPermission(models.Model): """ Row recording permissions for a content library + + .. no_pii: """ library = models.ForeignKey(ContentLibrary, on_delete=models.CASCADE, related_name="permission_grants") # One of the following must be set (but not both): @@ -226,6 +230,8 @@ def __str__(self): class ContentLibraryBlockImportTask(models.Model): """ Model of a task to import blocks from an external source (e.g. modulestore). + + .. no_pii: """ library = models.ForeignKey( @@ -331,6 +337,8 @@ class LtiProfile(models.Model): Unless Anonymous, this should be a unique representation of the LTI subject (as per the client token ``sub`` identify claim) that initiated an LTI launch through Content Libraries. + + .. no_pii: """ objects = LtiProfileManager() @@ -453,6 +461,8 @@ class LtiGradedResource(models.Model): launch. This model links the profile that launched the resource with the resource itself, allowing identifcation of the link through its usage key string and user id. + + .. no_pii: """ objects = LtiGradedResourceManager() diff --git a/openedx/core/djangoapps/content_tagging/models/base.py b/openedx/core/djangoapps/content_tagging/models/base.py index 8a232d3a7bf4..d799d8795194 100644 --- a/openedx/core/djangoapps/content_tagging/models/base.py +++ b/openedx/core/djangoapps/content_tagging/models/base.py @@ -16,6 +16,8 @@ class TaxonomyOrg(models.Model): We keep this as a separate class from ContentTaxonomy so that class can remain a proxy for Taxonomy, keeping the data models and usage simple. + + .. no_pii: """ class RelType(models.TextChoices): diff --git a/openedx/core/djangoapps/course_live/models.py b/openedx/core/djangoapps/course_live/models.py index a871ec024447..c311c6fb3f2f 100644 --- a/openedx/core/djangoapps/course_live/models.py +++ b/openedx/core/djangoapps/course_live/models.py @@ -12,6 +12,8 @@ class CourseLiveConfiguration(TimeStampedModel): """ Associates a Course with a LTI provider and configuration + + .. no_pii: """ course_key = CourseKeyField(max_length=255, db_index=True, null=False) enabled = models.BooleanField( diff --git a/openedx/core/djangoapps/discussions/models.py b/openedx/core/djangoapps/discussions/models.py index 9d97c9051397..f47b661c9ad2 100644 --- a/openedx/core/djangoapps/discussions/models.py +++ b/openedx/core/djangoapps/discussions/models.py @@ -318,6 +318,8 @@ def get_supported_providers() -> List[str]: class ProviderFilter(StackedConfigurationModel): """ Associate allow/deny-lists of discussions providers with courses/orgs + + .. no_pii: """ allow = ListCharField( @@ -406,6 +408,8 @@ def get_available_providers(cls, course_key: CourseKey) -> List[str]: class DiscussionsConfiguration(TimeStampedModel): """ Associates a learning context with discussion provider and configuration + + .. no_pii: """ context_key = LearningContextKeyField( @@ -554,6 +558,8 @@ def lti_discussion_enabled(cls, course_key: CourseKey) -> bool: class DiscussionTopicLink(models.Model): """ A model linking discussion topics ids to the part of a course they are linked to. + + ..no_pii: """ context_key = LearningContextKeyField( db_index=True, diff --git a/openedx/core/djangoapps/programs/models.py b/openedx/core/djangoapps/programs/models.py index 302ad553f245..e708066813b8 100644 --- a/openedx/core/djangoapps/programs/models.py +++ b/openedx/core/djangoapps/programs/models.py @@ -72,8 +72,14 @@ def get(cls, program_uuid): class ProgramLiveConfiguration(AbstractProgramLTIConfiguration): + """ + .. no_pii: + """ history = HistoricalRecords() class ProgramDiscussionsConfiguration(AbstractProgramLTIConfiguration): + """ + .. no_pii: + """ history = HistoricalRecords() diff --git a/openedx/core/djangoapps/user_api/models.py b/openedx/core/djangoapps/user_api/models.py index 6c1dd832a38d..d776dac8fe2a 100644 --- a/openedx/core/djangoapps/user_api/models.py +++ b/openedx/core/djangoapps/user_api/models.py @@ -426,6 +426,8 @@ def __str__(self): class BulkUserRetirementConfig(ConfigurationModel): """ Configuration to store a csv file that will be used in retire_user management command. + + .. no_pii: """ # Timeout set to 0 so that the model does not read from cached config in case the config entry is deleted. cache_timeout = 0 diff --git a/openedx/core/djangoapps/video_config/models.py b/openedx/core/djangoapps/video_config/models.py index 9ea7f8f44d2d..9a54585475f2 100644 --- a/openedx/core/djangoapps/video_config/models.py +++ b/openedx/core/djangoapps/video_config/models.py @@ -98,7 +98,8 @@ class CourseYoutubeBlockedFlag(ConfigurationModel): Disables the playback of youtube videos for a given course. If the flag is present for the course, and set to "enabled", then youtube is disabled for that course. - .. no_pii + + .. no_pii: """ KEY_FIELDS = ('course_id',) diff --git a/openedx/features/announcements/models.py b/openedx/features/announcements/models.py index cb202ce521ba..f58f61165db6 100644 --- a/openedx/features/announcements/models.py +++ b/openedx/features/announcements/models.py @@ -7,7 +7,11 @@ class Announcement(models.Model): - """Site-wide announcements to be displayed on the dashboard""" + """ + Site-wide announcements to be displayed on the dashboard + + .. no_pii: + """ class Meta: app_label = 'announcements' diff --git a/openedx/features/discounts/models.py b/openedx/features/discounts/models.py index bca6ef7b37c1..8c7c5ff53ea0 100644 --- a/openedx/features/discounts/models.py +++ b/openedx/features/discounts/models.py @@ -15,6 +15,8 @@ class DiscountRestrictionConfig(StackedConfigurationModel): """ A ConfigurationModel used to manage restrictons for lms-controlled discounts + + .. no_pii: """ STACKABLE_FIELDS = ('disabled',) @@ -43,6 +45,8 @@ def __str__(self): class DiscountPercentageConfig(StackedConfigurationModel): """ A ConfigurationModel to configure the discount percentage for the first purchase discount + + .. no_pii: """ STACKABLE_FIELDS = ('percentage',) percentage = models.PositiveIntegerField()