diff --git a/backend/authentication/migrations/0001_initial.py b/backend/authentication/migrations/0001_initial.py new file mode 100644 index 000000000..d8ff55f70 --- /dev/null +++ b/backend/authentication/migrations/0001_initial.py @@ -0,0 +1,85 @@ +# Generated by Django 4.2.10 on 2024-03-06 01:55 + +import django.contrib.auth.models +import django.contrib.auth.validators +import django.contrib.postgres.fields +from django.db import migrations, models +import django.utils.timezone +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='User', + fields=[ + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='Created at')), + ('deletion_date', models.DateTimeField(blank=True, null=True, verbose_name='Deletion date')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('user_name', models.CharField(max_length=255)), + ('name', models.CharField(blank=True, max_length=255)), + ('password', models.CharField(max_length=255)), + ('description', models.TextField(max_length=500)), + ('verified', models.BooleanField(default=False)), + ('verification_method', models.CharField(blank=True, max_length=30)), + ('social_accounts', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=255), blank=True, null=True, size=None)), + ('total_flags', models.IntegerField(default=0)), + ('private', models.BooleanField(default=False)), + ('high_risk', models.BooleanField(default=False)), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name='Support', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + migrations.CreateModel( + name='SupportEntityType', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name='UserResource', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + migrations.CreateModel( + name='UserTask', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + migrations.CreateModel( + name='UserTopic', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + ] diff --git a/backend/authentication/migrations/0002_initial.py b/backend/authentication/migrations/0002_initial.py new file mode 100644 index 000000000..6bfa75f06 --- /dev/null +++ b/backend/authentication/migrations/0002_initial.py @@ -0,0 +1,90 @@ +# Generated by Django 4.2.10 on 2024-03-06 01:55 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('content', '0002_initial'), + ('entities', '0001_initial'), + ('auth', '0012_alter_user_first_name_max_length'), + ('authentication', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='usertopic', + name='topic_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.topic'), + ), + migrations.AddField( + model_name='usertopic', + name='user_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='usertask', + name='task_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.task'), + ), + migrations.AddField( + model_name='usertask', + name='user_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='userresource', + name='resource_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.resource'), + ), + migrations.AddField( + model_name='userresource', + name='user_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='support', + name='supported_entity', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supported', to='entities.organization'), + ), + migrations.AddField( + model_name='support', + name='supported_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supported', to='authentication.supportentitytype'), + ), + migrations.AddField( + model_name='support', + name='supporter_entity', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supporter', to='entities.organization'), + ), + migrations.AddField( + model_name='support', + name='supporter_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supporter', to='authentication.supportentitytype'), + ), + migrations.AddField( + model_name='user', + name='groups', + field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups'), + ), + migrations.AddField( + model_name='user', + name='user_icon', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='content.image'), + ), + migrations.AddField( + model_name='user', + name='user_permissions', + field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions'), + ), + migrations.AddField( + model_name='user', + name='verification_partner', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/backend/backend/urls.py b/backend/backend/urls.py index 65c6b85a0..bd12ec7f4 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -14,6 +14,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.contrib import admin from django.urls import include, path from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView diff --git a/backend/content/migrations/0001_initial.py b/backend/content/migrations/0001_initial.py new file mode 100644 index 000000000..45bb25b88 --- /dev/null +++ b/backend/content/migrations/0001_initial.py @@ -0,0 +1,87 @@ +# Generated by Django 4.2.10 on 2024-03-06 01:55 + +import django.contrib.postgres.fields +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Faq', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ('question', models.TextField(max_length=500)), + ('answer', models.TextField(max_length=500)), + ('last_updated', models.DateTimeField(auto_now=True)), + ], + ), + migrations.CreateModel( + name='Image', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('image_location', models.ImageField(upload_to='images/')), + ('creation_date', models.DateTimeField(auto_now_add=True)), + ], + ), + migrations.CreateModel( + name='Resource', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ('description', models.TextField(max_length=500)), + ('topics', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=255), blank=True, default=list, size=None)), + ('category', models.CharField(blank=True, max_length=255)), + ('url', models.URLField(max_length=255)), + ('total_flags', models.IntegerField(default=0)), + ('private', models.BooleanField(default=True)), + ('creation_date', models.DateTimeField(auto_now_add=True)), + ('last_updated', models.DateTimeField(auto_now=True)), + ], + ), + migrations.CreateModel( + name='ResourceTopic', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + migrations.CreateModel( + name='Task', + fields=[ + ('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='Created at')), + ('deletion_date', models.DateTimeField(blank=True, null=True, verbose_name='Deletion date')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ('description', models.TextField(max_length=500)), + ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=255), default=list, null=True, size=None)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Topic', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ('active', models.BooleanField(default=True)), + ('description', models.TextField(max_length=500)), + ('creation_date', models.DateTimeField(auto_now_add=True)), + ('last_updated', models.DateTimeField(auto_now=True)), + ('deprecation_date', models.DateTimeField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='TopicFormat', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + ] diff --git a/backend/content/migrations/0002_initial.py b/backend/content/migrations/0002_initial.py new file mode 100644 index 000000000..8f12ad1c0 --- /dev/null +++ b/backend/content/migrations/0002_initial.py @@ -0,0 +1,50 @@ +# Generated by Django 4.2.10 on 2024-03-06 01:55 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('entities', '0001_initial'), + ('events', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('content', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='topicformat', + name='format_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.format'), + ), + migrations.AddField( + model_name='topicformat', + name='topic_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.topic'), + ), + migrations.AddField( + model_name='resourcetopic', + name='resource_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.resource'), + ), + migrations.AddField( + model_name='resourcetopic', + name='topic_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.topic'), + ), + migrations.AddField( + model_name='resource', + name='created_by', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='faq', + name='org_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.organization'), + ), + ] diff --git a/backend/content/migrations/0003_tag_resourcetag.py b/backend/content/migrations/0003_tag_resourcetag.py new file mode 100644 index 000000000..4ae99d2b4 --- /dev/null +++ b/backend/content/migrations/0003_tag_resourcetag.py @@ -0,0 +1,48 @@ +# Generated by Django 4.2.10 on 2024-03-13 18:32 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("content", "0002_initial"), + ] + + operations = [ + migrations.CreateModel( + name="Tag", + fields=[ + ("id", models.IntegerField(primary_key=True, serialize=False)), + ("text", models.CharField(max_length=255)), + ("creation_date", models.DateTimeField(auto_now_add=True)), + ], + ), + migrations.CreateModel( + name="ResourceTag", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "resource_id", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="content.resource", + ), + ), + ( + "tag_id", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="content.tag" + ), + ), + ], + ), + ] diff --git a/backend/entities/migrations/0001_initial.py b/backend/entities/migrations/0001_initial.py new file mode 100644 index 000000000..5e2571561 --- /dev/null +++ b/backend/entities/migrations/0001_initial.py @@ -0,0 +1,164 @@ +# Generated by Django 4.2.10 on 2024-03-06 01:55 + +from django.conf import settings +import django.contrib.postgres.fields +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('events', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('content', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Group', + fields=[ + ('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='Created at')), + ('deletion_date', models.DateTimeField(blank=True, null=True, verbose_name='Deletion date')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ('tagline', models.CharField(blank=True, max_length=255)), + ('description', models.TextField(max_length=500)), + ('social_accounts', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=255), blank=True, default=list, size=None)), + ('category', models.CharField(max_length=255)), + ('total_flags', models.IntegerField(default=0)), + ('about_images', models.ManyToManyField(blank=True, related_name='about_img', to='content.image')), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('group_icon', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='content.image')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Organization', + fields=[ + ('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='Created at')), + ('deletion_date', models.DateTimeField(blank=True, null=True, verbose_name='Deletion date')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ('tagline', models.CharField(blank=True, max_length=255)), + ('description', models.TextField(max_length=500)), + ('social_accounts', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=255), blank=True, default=list, size=None)), + ('high_risk', models.BooleanField(default=False)), + ('total_flags', models.IntegerField(default=0)), + ('about_images', models.ManyToManyField(blank=True, related_name='about_images', to='content.image')), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_orgs', to=settings.AUTH_USER_MODEL)), + ('org_icon', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='content.image')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='OrganizationApplicationStatus', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status_name', models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name='OrganizationTopic', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('org_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.organization')), + ('topic_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.topic')), + ], + ), + migrations.CreateModel( + name='OrganizationTask', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('group_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='entities.group')), + ('org_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.organization')), + ('task_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.task')), + ], + ), + migrations.CreateModel( + name='OrganizationResource', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('org_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.organization')), + ('resource_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.resource')), + ], + ), + migrations.CreateModel( + name='OrganizationMember', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('is_owner', models.BooleanField(default=False)), + ('is_admin', models.BooleanField(default=False)), + ('is_comms', models.BooleanField(default=False)), + ('org_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.organization')), + ('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='OrganizationEvent', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event')), + ('org_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.organization')), + ], + ), + migrations.CreateModel( + name='OrganizationApplication', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('orgs_in_favor', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(blank=True, null=True), blank=True, default=list, size=None)), + ('orgs_against', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(blank=True, null=True), blank=True, default=list, size=None)), + ('creation_date', models.DateTimeField(auto_now_add=True)), + ('status_updated', models.DateTimeField(auto_now=True)), + ('org_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.organization')), + ('status', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='entities.organizationapplicationstatus')), + ], + ), + migrations.CreateModel( + name='GroupTopic', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('group_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.group')), + ('topic_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.topic')), + ], + ), + migrations.CreateModel( + name='GroupResource', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('group_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.group')), + ('resource_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.resource')), + ], + ), + migrations.CreateModel( + name='GroupMember', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('is_owner', models.BooleanField(default=False)), + ('is_admin', models.BooleanField(default=False)), + ('is_comms', models.BooleanField(default=False)), + ('group_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.group')), + ('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='GroupEvent', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event')), + ('group_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.group')), + ], + ), + migrations.AddField( + model_name='group', + name='org_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entities.organization'), + ), + ] diff --git a/backend/entities/views.py b/backend/entities/views.py index 1b2456059..002cc905f 100644 --- a/backend/entities/views.py +++ b/backend/entities/views.py @@ -151,6 +151,36 @@ class OrganizationMemberViewSet(viewsets.ModelViewSet[OrganizationMember]): serializer_class = OrganizationMemberSerializer pagination_class = CustomPagination + def create(self, request: Request, *args: str, **kwargs: int) -> Response: + serializer = self.get_serializer(data=request.data) + if serializer.is_valid(): + instance = serializer.save() + data = { + "message": f"New Organization Member created with id: {instance.id}" + } + return Response(data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def retrieve(self, request: Request, *args: str, **kwargs: int) -> Response: + instance = get_object_or_404(OrganizationMember, pk=kwargs["pk"]) + serializer = self.get_serializer(instance) + return Response(serializer.data) + + def partial_update(self, request: Request, *args: str, **kwargs: int) -> Response: + instance = get_object_or_404(OrganizationMember, pk=kwargs["pk"]) + serializer = self.get_serializer(instance, data=request.data, partial=True) + if serializer.is_valid(): + serializer.save() + data = {"message": f'Organization {kwargs["pk"]} has been updated'} + return Response(data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def destroy(self, request: Request, *args: str, **kwargs: int) -> Response: + instance = get_object_or_404(OrganizationMember, pk=kwargs["pk"]) + instance.delete() + data = {"message": f'Organization {kwargs["pk"]} has been deleted successfully'} + return Response(data, status=status.HTTP_204_NO_CONTENT) + class OrganizationResourceViewSet(viewsets.ModelViewSet[OrganizationResource]): queryset = OrganizationResource.objects.all() diff --git a/backend/events/migrations/0001_initial.py b/backend/events/migrations/0001_initial.py new file mode 100644 index 000000000..a2a6fddef --- /dev/null +++ b/backend/events/migrations/0001_initial.py @@ -0,0 +1,122 @@ +# Generated by Django 4.2.10 on 2024-03-06 01:55 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('content', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Event', + fields=[ + ('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='Created at')), + ('deletion_date', models.DateTimeField(blank=True, null=True, verbose_name='Deletion date')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ('tagline', models.CharField(blank=True, max_length=255)), + ('type', models.CharField(max_length=255)), + ('description', models.TextField(max_length=500)), + ('get_involved_text', models.TextField(max_length=500)), + ('online_location_link', models.CharField(blank=True, max_length=255)), + ('offline_location_lat', models.FloatField(blank=True, null=True)), + ('offline_location_long', models.FloatField(blank=True, null=True)), + ('start_time', models.DateTimeField()), + ('end_time', models.DateTimeField()), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_events', to=settings.AUTH_USER_MODEL)), + ('event_icon', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='content.image')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='EventAttendeeStatus', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status_name', models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name='Format', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('description', models.TextField(max_length=500)), + ('creation_date', models.DateTimeField(auto_now_add=True)), + ('last_updated', models.DateTimeField(auto_now=True)), + ('deprecation_date', models.DateTimeField(null=True)), + ], + ), + migrations.CreateModel( + name='Role', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('is_custom', models.BooleanField(default=False)), + ('description', models.TextField(max_length=500)), + ('creation_date', models.DateTimeField(auto_now_add=True)), + ('last_updated', models.DateTimeField(auto_now=True)), + ('deprecation_date', models.DateTimeField(null=True)), + ], + ), + migrations.CreateModel( + name='EventTopic', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event')), + ('topic_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.topic')), + ], + ), + migrations.CreateModel( + name='EventTask', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event')), + ('task_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.task')), + ], + ), + migrations.CreateModel( + name='EventRole', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event')), + ('role_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.role')), + ], + ), + migrations.CreateModel( + name='EventResource', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event')), + ('resource_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.resource')), + ], + ), + migrations.CreateModel( + name='EventFormat', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event')), + ('format_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.format')), + ], + ), + migrations.CreateModel( + name='EventAttendee', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('attendee_status', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='events.eventattendeestatus')), + ('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event')), + ('role_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='events.role')), + ('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/backend/events/migrations/0002_alter_event_creation_date_alter_event_deletion_date_and_more.py b/backend/events/migrations/0002_alter_event_creation_date_alter_event_deletion_date_and_more.py new file mode 100644 index 000000000..83f565830 --- /dev/null +++ b/backend/events/migrations/0002_alter_event_creation_date_alter_event_deletion_date_and_more.py @@ -0,0 +1,60 @@ +# Generated by Django 4.2.10 on 2024-03-13 18:32 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("content", "0003_tag_resourcetag"), + ("events", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="event", + name="creation_date", + field=models.DateTimeField(auto_now_add=True), + ), + migrations.AlterField( + model_name="event", + name="deletion_date", + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name="event", + name="online_location_link", + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AlterField( + model_name="event", + name="tagline", + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.CreateModel( + name="EventTag", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "event_id", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="events.event" + ), + ), + ( + "tag_id", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="content.tag" + ), + ), + ], + ), + ] diff --git a/backend/events/models.py b/backend/events/models.py index 109be673f..c2abdd273 100644 --- a/backend/events/models.py +++ b/backend/events/models.py @@ -16,6 +16,7 @@ - EventTopic - EventTag """ + from uuid import uuid4 from django.db import models @@ -31,6 +32,9 @@ class Event(CreationDeletionMixin): description = models.TextField(max_length=500) get_involved_text = models.TextField(max_length=500) online_location_link = models.CharField(max_length=255, blank=True) + # offline_location_id = models.ForeignKey( + # "Location", on_delete=models.CASCADE, null=True + # ) offline_location_lat = models.FloatField(null=True, blank=True) offline_location_long = models.FloatField(null=True, blank=True) start_time = models.DateTimeField() diff --git a/backend/events/serializers.py b/backend/events/serializers.py index 522214521..4d7f20216 100644 --- a/backend/events/serializers.py +++ b/backend/events/serializers.py @@ -31,35 +31,41 @@ class EventSerializer(serializers.ModelSerializer[Event]): class Meta: model = Event - fields = "__all__" - - def validate(self, data: Dict[str, Union[str, int]]) -> Dict[str, Union[str, int]]: - required_fields = [ + fields = [ + "id", "name", "tagline", "type", "description", "get_involved_text", + "online_location_link", + # "offline_location_id", + "offline_location_lat", + "offline_location_long", "start_time", "end_time", "created_by", + "event_icon", "creation_date", "deletion_date", - "event_icon", ] - def isEmpty() -> bool: - for field in required_fields: - if data[field] == "" or data[field] is None: - return True - return False - - if isEmpty(): + def validate(self, data: Dict[str, Union[str, int]]) -> Dict[str, Union[str, int]]: + def exists(attr): + if attr in data: + return data[attr] + if self.instance and hasattr(self.instance, attr): + return getattr(self.instance, attr) + return None + + created_by = exists("created_by") + start_time = exists("start_time") + end_time = exists("end_time") + + if created_by != getattr(self.instance, "created_by"): raise serializers.ValidationError( - _( - "Only the fields offline_location_lat and offline_location_long fields can be empty for Events." - ), - code="invalid_value", + _("You cannot update an event's coordinator."), + code="invalid", ) if parse_datetime(data["start_time"]) > parse_datetime(data["end_time"]): # type: ignore diff --git a/backend/events/views.py b/backend/events/views.py index a280e3752..e680fb75c 100644 --- a/backend/events/views.py +++ b/backend/events/views.py @@ -1,10 +1,14 @@ -from rest_framework import viewsets +from django.shortcuts import get_object_or_404 +from rest_framework import status, viewsets +from rest_framework.request import Request +from rest_framework.response import Response from rest_framework.throttling import ( AnonRateThrottle, UserRateThrottle, ) from backend.paginator import CustomPagination +from entities.models import OrganizationMember from .models import ( Event, @@ -38,6 +42,52 @@ class EventViewSet(viewsets.ModelViewSet[Event]): pagination_class = CustomPagination throttle_classes = [AnonRateThrottle, UserRateThrottle] + def list(self, request: Request, *args: str, **kwagrs: int) -> Response: + serializer = self.get_serializer(self.queryset, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) + + def create(self, request: Request, *args: str, **kwargs: int) -> Response: + serializer = self.get_serializer(data=request.data) + member = get_object_or_404( + OrganizationMember, user_id=request.data["created_by"] + ) + if member.is_admin and serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def retrieve(self, request: Request, *args: str, **kwargs: int) -> Response: + instance = get_object_or_404(Event, pk=kwargs["pk"]) + serializer = self.get_serializer(instance) + return Response(serializer.data, status=status.HTTP_200_OK) + + def update(self, request: Request, *args: str, **kwargs: int) -> Response: + instance = get_object_or_404(Event, pk=kwargs["pk"]) + serializer = self.get_serializer(instance, data=request.data) + member = get_object_or_404( + OrganizationMember, user_id=request.data["created_by"] + ) + if member.is_admin and serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def partial_update(self, request: Request, *args: str, **kwargs: int) -> Response: + instance = get_object_or_404(Event, pk=kwargs["pk"]) + serializer = self.get_serializer(instance, data=request.data, partial=True) + member = get_object_or_404( + OrganizationMember, user_id=request.data["created_by"] + ) + if member.is_admin and serializer.is_valid(raise_exception=True): + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def destroy(self, request: Request, *args: str, **kwargs: int) -> Response: + instance = get_object_or_404(Event, pk=kwargs["pk"]) + instance.delete() + return Response(status=status.HTTP_201_CREATED) + class FormatViewSet(viewsets.ModelViewSet[Format]): queryset = Format.objects.all() diff --git a/backend/manage.py b/backend/manage.py index eeaf11a9c..1a54aed75 100755 --- a/backend/manage.py +++ b/backend/manage.py @@ -1,5 +1,6 @@ #!/usr/bin/env python """Django's command-line utility for administrative tasks.""" + import os import sys diff --git a/frontend/i18n/check_repeat_i18n_values.py b/frontend/i18n/check_repeat_i18n_values.py index b07bc298a..dc69c5276 100644 --- a/frontend/i18n/check_repeat_i18n_values.py +++ b/frontend/i18n/check_repeat_i18n_values.py @@ -5,7 +5,6 @@ If yes, combine them in a `_global` sub name at the lowest matching name level of `en-US.json`. """ - import json import string from collections import Counter diff --git a/frontend/i18n/check_unused_i18n_keys.py b/frontend/i18n/check_unused_i18n_keys.py index f771a4efd..cd4dd833c 100644 --- a/frontend/i18n/check_unused_i18n_keys.py +++ b/frontend/i18n/check_unused_i18n_keys.py @@ -5,7 +5,6 @@ If yes, then remove those keys from the `en-US.json`. """ - import json import os from pathlib import Path