# Generated by Django 5.2.6 on 2025-12-24 15:18 import django.db.models.deletion from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ ('rbac', '0003_alter_rbacgroup_members'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='LTIPlatform', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(help_text="Platform name (e.g., 'Moodle Production')", max_length=255, unique=True)), ('platform_id', models.URLField(help_text="Platform's issuer URL (iss claim, e.g., https://moodle.example.com)")), ('client_id', models.CharField(help_text='Client ID provided by the platform', max_length=255)), ('auth_login_url', models.URLField(help_text='OIDC authentication endpoint URL')), ('auth_token_url', models.URLField(help_text='OAuth2 token endpoint URL')), ('auth_audience', models.URLField(blank=True, help_text='OAuth2 audience (optional)', null=True)), ('key_set_url', models.URLField(help_text="Platform's public JWK Set URL")), ('key_set', models.JSONField(blank=True, help_text='Cached JWK Set (auto-fetched)', null=True)), ('key_set_updated', models.DateTimeField(blank=True, help_text='Last time JWK Set was fetched', null=True)), ('deployment_ids', models.JSONField(default=list, help_text='List of deployment IDs for this platform')), ('enable_nrps', models.BooleanField(default=True, help_text='Enable Names and Role Provisioning Service')), ('enable_deep_linking', models.BooleanField(default=True, help_text='Enable Deep Linking 2.0')), ('auto_create_categories', models.BooleanField(default=True, help_text='Automatically create categories for courses')), ('auto_create_users', models.BooleanField(default=True, help_text='Automatically create users on first launch')), ('auto_sync_roles', models.BooleanField(default=True, help_text='Automatically sync user roles from LTI')), ('remove_from_groups_on_unenroll', models.BooleanField(default=False, help_text="Remove users from RBAC groups when they're no longer in the course")), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('active', models.BooleanField(default=True, help_text='Whether this platform is currently active')), ], options={ 'verbose_name': 'LTI Platform', 'verbose_name_plural': 'LTI Platforms', 'unique_together': {('platform_id', 'client_id')}, }, ), migrations.CreateModel( name='LTIResourceLink', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('context_id', models.CharField(db_index=True, help_text='LTI context ID (typically course ID)', max_length=255)), ('context_title', models.CharField(blank=True, help_text='Course title', max_length=255)), ('context_label', models.CharField(blank=True, help_text='Course short name/code', max_length=100)), ('resource_link_id', models.CharField(db_index=True, help_text='LTI resource link ID', max_length=255)), ('resource_link_title', models.CharField(blank=True, help_text='Resource link title', max_length=255)), ( 'category', models.ForeignKey( blank=True, help_text='Mapped MediaCMS category', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lti_resource_links', to='files.category' ), ), ('platform', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='resource_links', to='lti.ltiplatform')), ( 'rbac_group', models.ForeignKey( blank=True, help_text='RBAC group for course members', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lti_resource_links', to='rbac.rbacgroup' ), ), ], options={ 'verbose_name': 'LTI Resource Link', 'verbose_name_plural': 'LTI Resource Links', }, ), migrations.CreateModel( name='LTILaunchLog', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('launch_type', models.CharField(choices=[('resource_link', 'Resource Link Launch'), ('deep_linking', 'Deep Linking')], default='resource_link', max_length=50)), ('success', models.BooleanField(db_index=True, default=True, help_text='Whether the launch was successful')), ('error_message', models.TextField(blank=True, help_text='Error message if launch failed')), ('claims', models.JSONField(help_text='Sanitized LTI claims from the launch')), ('ip_address', models.GenericIPAddressField(blank=True, help_text='IP address of the user', null=True)), ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ( 'user', models.ForeignKey( blank=True, help_text='MediaCMS user (null if launch failed before user creation)', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lti_launch_logs', to=settings.AUTH_USER_MODEL, ), ), ('platform', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='launch_logs', to='lti.ltiplatform')), ('resource_link', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='launch_logs', to='lti.ltiresourcelink')), ], options={ 'verbose_name': 'LTI Launch Log', 'verbose_name_plural': 'LTI Launch Logs', 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='LTIRoleMapping', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('lti_role', models.CharField(help_text="LTI role URI or short name (e.g., 'Instructor', 'Learner')", max_length=255)), ( 'global_role', models.CharField( blank=True, choices=[ ('user', 'Authenticated User'), ('advancedUser', 'Advanced User'), ('editor', 'MediaCMS Editor'), ('manager', 'MediaCMS Manager'), ('admin', 'MediaCMS Administrator'), ], help_text='MediaCMS global role to assign', max_length=20, ), ), ( 'group_role', models.CharField(blank=True, choices=[('member', 'Member'), ('contributor', 'Contributor'), ('manager', 'Manager')], help_text='RBAC group role to assign', max_length=20), ), ('platform', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='role_mappings', to='lti.ltiplatform')), ], options={ 'verbose_name': 'LTI Role Mapping', 'verbose_name_plural': 'LTI Role Mappings', }, ), migrations.CreateModel( name='LTIUserMapping', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('lti_user_id', models.CharField(db_index=True, help_text="LTI 'sub' claim (unique user identifier from platform)", max_length=255)), ('created_at', models.DateTimeField(auto_now_add=True)), ('last_login', models.DateTimeField(auto_now=True)), ('platform', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_mappings', to='lti.ltiplatform')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lti_mappings', to=settings.AUTH_USER_MODEL)), ], options={ 'verbose_name': 'LTI User Mapping', 'verbose_name_plural': 'LTI User Mappings', }, ), migrations.AddIndex( model_name='ltiresourcelink', index=models.Index(fields=['platform', 'context_id'], name='lti_ltireso_platfor_4a3f27_idx'), ), migrations.AddIndex( model_name='ltiresourcelink', index=models.Index(fields=['context_id'], name='lti_ltireso_context_c6f9e2_idx'), ), migrations.AlterUniqueTogether( name='ltiresourcelink', unique_together={('platform', 'context_id', 'resource_link_id')}, ), migrations.AddIndex( model_name='ltilaunchlog', index=models.Index(fields=['-created_at'], name='lti_ltilaun_created_94c574_idx'), ), migrations.AddIndex( model_name='ltilaunchlog', index=models.Index(fields=['platform', 'user'], name='lti_ltilaun_platfor_5240bf_idx'), ), migrations.AlterUniqueTogether( name='ltirolemapping', unique_together={('platform', 'lti_role')}, ), migrations.AddIndex( model_name='ltiusermapping', index=models.Index(fields=['platform', 'lti_user_id'], name='lti_ltiuser_platfor_9c70bb_idx'), ), migrations.AddIndex( model_name='ltiusermapping', index=models.Index(fields=['user'], name='lti_ltiuser_user_id_b06d01_idx'), ), migrations.AlterUniqueTogether( name='ltiusermapping', unique_together={('platform', 'lti_user_id')}, ), ]