diff --git a/core/admin.py b/core/admin.py index c64df21..8982378 100644 --- a/core/admin.py +++ b/core/admin.py @@ -8,6 +8,8 @@ from core.models.video import Video from core.models.glb import Glb from core.models.pdf import Pdf from core.models.TeamMember import TeamMember +from core.models.Team import Team +from core.models.Space import Space from core.models.AssetBundleRoom import AssetBundleRoom from core.models.AssignRoomUser import AssignRoomUser # from .model import user @@ -22,6 +24,8 @@ admin.site.register(Role) admin.site.register(AssignedRule) admin.site.register(Customer) admin.site.register(Subscription) +admin.site.register(Team) +admin.site.register(Space) admin.site.register(TeamMember) admin.site.register(AssetBundleRoom) admin.site.register(AssignRoomUser) diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py index 84aee35..f22275e 100644 --- a/core/migrations/0001_initial.py +++ b/core/migrations/0001_initial.py @@ -1,8 +1,8 @@ -# Generated by Django 5.0 on 2025-05-21 07:58 +# Generated by Django 4.2 on 2025-06-01 07:13 -import django.db.models.deletion from django.conf import settings from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): @@ -14,6 +14,18 @@ class Migration(migrations.Migration): ] operations = [ + migrations.CreateModel( + name='AssetBundleRoom', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('url', models.FileField(blank=True, null=True, upload_to='user_files/')), + ('name', models.CharField(max_length=255)), + ('description', models.TextField(blank=True)), + ('maxPerson', models.PositiveIntegerField()), + ('img', models.FileField(blank=True, null=True, upload_to='user_imgs/')), + ('Private', models.BooleanField(default=True)), + ], + ), migrations.CreateModel( name='Role', fields=[ @@ -21,6 +33,73 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=100, unique=True)), ], ), + migrations.CreateModel( + name='Video', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('url', models.URLField(blank=True, max_length=250)), + ('video', models.FileField(blank=True, null=True, upload_to='user_videos/')), + ('name', models.CharField(max_length=100)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Subscription', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('license_number', models.CharField(max_length=100)), + ('user_count', models.PositiveIntegerField(blank=True, default=0)), + ('startTime', models.DateTimeField()), + ('endTime', models.DateTimeField()), + ('price', models.DecimalField(decimal_places=2, max_digits=30)), + ('user', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='subscriptions', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Space', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('description', models.TextField()), + ('capacity', models.PositiveIntegerField()), + ('assetBundleRoomId', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.assetbundleroom')), + ('userId', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Pdf', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('url', models.URLField(blank=True, max_length=250)), + ('pdf', models.FileField(blank=True, null=True, upload_to='user_pdfs/')), + ('name', models.CharField(max_length=100)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Image', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('url', models.URLField(blank=True, max_length=250)), + ('image', models.ImageField(blank=True, null=True, upload_to='user_images/')), + ('name', models.CharField(max_length=100)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Glb', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('url', models.URLField(blank=True, max_length=250)), + ('glb', models.FileField(blank=True, null=True, upload_to='user_glbs/')), + ('name', models.CharField(max_length=100)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), migrations.CreateModel( name='Customer', fields=[ @@ -37,68 +116,35 @@ class Migration(migrations.Migration): ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), - migrations.CreateModel( - name='Glb', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.URLField(blank=True, max_length=250)), - ('glb', models.FileField(blank=True, null=True, upload_to='user_glbs/')), - ('name', models.CharField(max_length=100)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.CreateModel( - name='Image', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.URLField(blank=True, max_length=250)), - ('image', models.ImageField(blank=True, null=True, upload_to='user_images/')), - ('name', models.CharField(max_length=100)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.CreateModel( - name='Pdf', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.URLField(blank=True, max_length=250)), - ('pdf', models.FileField(blank=True, null=True, upload_to='user_pdfs/')), - ('name', models.CharField(max_length=100)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), migrations.CreateModel( name='AssignedRule', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.role')), - ], - ), - migrations.CreateModel( - name='Subscription', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('license_number', models.CharField(max_length=100)), - ('user_count', models.PositiveIntegerField(blank=True, default=0)), - ('startTime', models.DateTimeField()), - ('endTime', models.DateTimeField()), - ('price', models.DecimalField(decimal_places=2, max_digits=30)), - ('user', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.CreateModel( - name='Video', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.URLField(blank=True, max_length=250)), - ('video', models.FileField(blank=True, null=True, upload_to='user_videos/')), - ('name', models.CharField(max_length=100)), - ('created_at', models.DateTimeField(auto_now_add=True)), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), + migrations.CreateModel( + name='TeamMember', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('isAdmin', models.BooleanField(default=False)), + ('adminUser', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='admin_user', to=settings.AUTH_USER_MODEL)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('user', 'adminUser')}, + }, + ), + migrations.CreateModel( + name='AssignRoomUser', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('assetbundle_room', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.assetbundleroom')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('user', 'assetbundle_room')}, + }, + ), ] diff --git a/core/migrations/0009_space.py b/core/migrations/0002_team.py similarity index 52% rename from core/migrations/0009_space.py rename to core/migrations/0002_team.py index 00dba85..ad49c5d 100644 --- a/core/migrations/0009_space.py +++ b/core/migrations/0002_team.py @@ -1,27 +1,26 @@ -# Generated by Django 5.0 on 2025-05-25 10:50 +# Generated by Django 4.2 on 2025-06-01 09:18 -import django.db.models.deletion from django.conf import settings from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('core', '0008_assignroomuser'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('core', '0001_initial'), ] operations = [ migrations.CreateModel( - name='Space', + name='Team', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=100)), - ('description', models.TextField()), - ('capacity', models.PositiveIntegerField()), - ('assetBundleRoomId', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.assetbundleroom')), - ('userId', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('description', models.TextField(blank=True)), + ('max_persons', models.PositiveIntegerField()), + ('admin', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='admin_teams', to=settings.AUTH_USER_MODEL)), ], ), ] diff --git a/core/migrations/0002_teammember.py b/core/migrations/0002_teammember.py deleted file mode 100644 index a6b6841..0000000 --- a/core/migrations/0002_teammember.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.0 on 2025-05-21 12:48 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='TeamMember', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('isAdmin', models.BooleanField(default=False)), - ('adminUser', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='admin_user', to=settings.AUTH_USER_MODEL)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'unique_together': {('user', 'adminUser')}, - }, - ), - ] diff --git a/core/migrations/0003_alter_teammember_unique_together_teammember_team_and_more.py b/core/migrations/0003_alter_teammember_unique_together_teammember_team_and_more.py new file mode 100644 index 0000000..a2462c8 --- /dev/null +++ b/core/migrations/0003_alter_teammember_unique_together_teammember_team_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 4.2 on 2025-06-01 09:46 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('core', '0002_team'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='teammember', + unique_together=set(), + ), + migrations.AddField( + model_name='teammember', + name='team', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='members', to='core.team'), + ), + migrations.AlterField( + model_name='teammember', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='team_memberships', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterUniqueTogether( + name='teammember', + unique_together={('user', 'team')}, + ), + migrations.RemoveField( + model_name='teammember', + name='adminUser', + ), + ] diff --git a/core/migrations/0003_subscription_adminuser_alter_subscription_user.py b/core/migrations/0003_subscription_adminuser_alter_subscription_user.py deleted file mode 100644 index 2f217a2..0000000 --- a/core/migrations/0003_subscription_adminuser_alter_subscription_user.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 5.0 on 2025-05-24 10:36 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0002_teammember'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AddField( - model_name='subscription', - name='adminUser', - field=models.ForeignKey(blank=True, default=None, on_delete=django.db.models.deletion.CASCADE, related_name='admin_subscriptions', to=settings.AUTH_USER_MODEL), - ), - migrations.AlterField( - model_name='subscription', - name='user', - field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='subscriptions', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/core/migrations/0004_remove_subscription_adminuser.py b/core/migrations/0004_remove_subscription_adminuser.py deleted file mode 100644 index f305a2a..0000000 --- a/core/migrations/0004_remove_subscription_adminuser.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 5.0 on 2025-05-24 10:51 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0003_subscription_adminuser_alter_subscription_user'), - ] - - operations = [ - migrations.RemoveField( - model_name='subscription', - name='adminUser', - ), - ] diff --git a/core/migrations/0004_team_subscription.py b/core/migrations/0004_team_subscription.py new file mode 100644 index 0000000..b3a2306 --- /dev/null +++ b/core/migrations/0004_team_subscription.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2 on 2025-06-01 12:51 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0003_alter_teammember_unique_together_teammember_team_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='team', + name='subscription', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teams', to='core.subscription'), + ), + ] diff --git a/core/migrations/0005_assetbundleroom.py b/core/migrations/0005_assetbundleroom.py deleted file mode 100644 index b8c9dfd..0000000 --- a/core/migrations/0005_assetbundleroom.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 5.0 on 2025-05-24 12:12 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0004_remove_subscription_adminuser'), - ] - - operations = [ - migrations.CreateModel( - name='AssetBundleRoom', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.TextField()), - ('name', models.CharField(max_length=255)), - ('description', models.TextField(blank=True)), - ('maxPerson', models.PositiveIntegerField()), - ('img', models.TextField()), - ], - ), - ] diff --git a/core/migrations/0005_meeting.py b/core/migrations/0005_meeting.py new file mode 100644 index 0000000..23692a3 --- /dev/null +++ b/core/migrations/0005_meeting.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2 on 2025-06-01 13:45 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('core', '0004_team_subscription'), + ] + + operations = [ + migrations.CreateModel( + name='Meeting', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('description', models.TextField(blank=True)), + ('date_time', models.DateTimeField()), + ('use_space', models.BooleanField(default=False)), + ('asset_bundle', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.assetbundleroom')), + ('creator_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_meetings', to=settings.AUTH_USER_MODEL)), + ('space', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.space')), + ], + ), + ] diff --git a/core/migrations/0006_alter_assetbundleroom_img_alter_assetbundleroom_url.py b/core/migrations/0006_alter_assetbundleroom_img_alter_assetbundleroom_url.py deleted file mode 100644 index 6c5c9a0..0000000 --- a/core/migrations/0006_alter_assetbundleroom_img_alter_assetbundleroom_url.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.0 on 2025-05-24 12:14 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0005_assetbundleroom'), - ] - - operations = [ - migrations.AlterField( - model_name='assetbundleroom', - name='img', - field=models.FileField(blank=True, null=True, upload_to='user_imgs/'), - ), - migrations.AlterField( - model_name='assetbundleroom', - name='url', - field=models.FileField(blank=True, null=True, upload_to='user_files/'), - ), - ] diff --git a/core/migrations/0007_assetbundleroom_private.py b/core/migrations/0007_assetbundleroom_private.py deleted file mode 100644 index cd397a3..0000000 --- a/core/migrations/0007_assetbundleroom_private.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.0 on 2025-05-24 13:08 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0006_alter_assetbundleroom_img_alter_assetbundleroom_url'), - ] - - operations = [ - migrations.AddField( - model_name='assetbundleroom', - name='Private', - field=models.BooleanField(default=True), - ), - ] diff --git a/core/migrations/0008_assignroomuser.py b/core/migrations/0008_assignroomuser.py deleted file mode 100644 index d147242..0000000 --- a/core/migrations/0008_assignroomuser.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 5.0 on 2025-05-24 13:26 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0007_assetbundleroom_private'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='AssignRoomUser', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('assetbundle_room', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.assetbundleroom')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'unique_together': {('user', 'assetbundle_room')}, - }, - ), - ] diff --git a/core/models/Meeting.py b/core/models/Meeting.py new file mode 100644 index 0000000..d82fb1a --- /dev/null +++ b/core/models/Meeting.py @@ -0,0 +1,21 @@ +from django.db import models +from django.contrib.auth.models import User +from core.models.Subscription import Subscription +from core.models.Space import Space +from core.models.AssetBundleRoom import AssetBundleRoom + + + + +class Meeting(models.Model): + name = models.CharField(max_length=255) + description = models.TextField(blank=True) + date_time = models.DateTimeField() + creator_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='created_meetings') + space = models.ForeignKey(Space, on_delete=models.SET_NULL, null=True, blank=True) + asset_bundle = models.ForeignKey(AssetBundleRoom, on_delete=models.SET_NULL, null=True, blank=True) + use_space = models.BooleanField(default=False) + + def __str__(self): + return self.name + diff --git a/core/models/Team.py b/core/models/Team.py new file mode 100644 index 0000000..a4c33fb --- /dev/null +++ b/core/models/Team.py @@ -0,0 +1,12 @@ +from django.db import models +from django.contrib.auth.models import User +from core.models.Subscription import Subscription +class Team(models.Model): + name = models.CharField(max_length=100) + description = models.TextField(blank=True) + max_persons = models.PositiveIntegerField() + admin = models.ForeignKey(User, on_delete=models.CASCADE, related_name='admin_teams') + subscription = models.ForeignKey(Subscription, on_delete=models.CASCADE, related_name='teams', null=True, blank=True) + + def __str__(self): + return self.name diff --git a/core/models/TeamMember.py b/core/models/TeamMember.py index 0862fcf..2ee6f56 100644 --- a/core/models/TeamMember.py +++ b/core/models/TeamMember.py @@ -1,13 +1,14 @@ from django.db import models from django.conf import settings +from core.models.Team import Team # Adjust the import if Team is in a different app class TeamMember(models.Model): - user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user', on_delete=models.CASCADE) - adminUser = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='admin_user', on_delete=models.CASCADE) + user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='team_memberships', on_delete=models.CASCADE) + team = models.ForeignKey(Team, related_name='members', on_delete=models.CASCADE , default=1) isAdmin = models.BooleanField(default=False) def __str__(self): - return f"TeamMember {self.user} - Admin: {self.isAdmin}" + return f"{self.user.username} in {self.team.name} - Admin: {self.isAdmin}" class Meta: - unique_together = ('user', 'adminUser') # Ensure that one user can be added only once per admin + unique_together = ('user', 'team') # Ensure that a user is only added once per team diff --git a/core/serializers/MeetingSerializer.py b/core/serializers/MeetingSerializer.py new file mode 100644 index 0000000..b8c575f --- /dev/null +++ b/core/serializers/MeetingSerializer.py @@ -0,0 +1,7 @@ +from rest_framework import serializers +from core.models.Meeting import Meeting + +class MeetingSerializer(serializers.ModelSerializer): + class Meta: + model = Meeting + fields = ['id', 'name', 'description', 'date_time', 'creator_user', 'space', 'asset_bundle', 'use_space'] diff --git a/core/serializers/TeamMemberSerializer.py b/core/serializers/TeamMemberSerializer.py index f48cda8..00c144c 100644 --- a/core/serializers/TeamMemberSerializer.py +++ b/core/serializers/TeamMemberSerializer.py @@ -4,13 +4,11 @@ from core.models.TeamMember import TeamMember class TeamMemberSerializer(serializers.ModelSerializer): class Meta: model = TeamMember - fields = ['user', 'adminUser', 'isAdmin'] - read_only_fields = ['adminUser'] + fields = ['user', 'team', 'isAdmin'] + read_only_fields = [] def create(self, validated_data): """ - Override the create method to automatically assign the admin user (request.user). + Creates a new team member using validated data. """ - admin_user = validated_data.pop('adminUser', None) - team_member = TeamMember.objects.create(adminUser=self.context['request'].user, **validated_data) - return team_member + return TeamMember.objects.create(**validated_data) diff --git a/core/serializers/TeamSerializer.py b/core/serializers/TeamSerializer.py new file mode 100644 index 0000000..913f308 --- /dev/null +++ b/core/serializers/TeamSerializer.py @@ -0,0 +1,7 @@ +from rest_framework import serializers +from core.models.Team import Team + +class TeamSerializer(serializers.ModelSerializer): + class Meta: + model = Team + fields = ['id', 'name', 'description', 'max_persons', 'admin', 'subscription'] diff --git a/core/settings.py b/core/settings.py index 20f8175..d38649b 100644 --- a/core/settings.py +++ b/core/settings.py @@ -46,7 +46,9 @@ INSTALLED_APPS = [ 'rest_framework', 'rest_framework.authtoken', 'core', - 'corsheaders' + 'corsheaders', + + 'drf_yasg', ] @@ -78,6 +80,7 @@ TEMPLATES = [ 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], @@ -89,6 +92,21 @@ WSGI_APPLICATION = 'core.wsgi.application' +from drf_yasg import openapi + +SWAGGER_SETTINGS = { + 'SECURITY_DEFINITIONS': { + 'Token': { + 'type': 'apiKey', + 'in': 'header', + 'name': 'Authorization', + 'description': "Token-based authentication with required prefix: **Token **" + } + }, +} + + + # Database # https://docs.djangoproject.com/en/4.2/ref/settings/#databases @@ -107,7 +125,7 @@ else: 'USER': 'root', 'PASSWORD': '', 'HOST': 'localhost', - 'PORT': '3306', + 'PORT': '3309', } } @@ -148,6 +166,11 @@ USE_TZ = True STATIC_URL = 'static/' +# Optional but recommended +STATICFILES_DIRS = [ + BASE_DIR / "static", +] + # Default primary key field type # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field diff --git a/core/urls.py b/core/urls.py index 2c2dec5..8d78be1 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,3 +1,9 @@ +from rest_framework import permissions +from drf_yasg.views import get_schema_view +from drf_yasg import openapi +from rest_framework.permissions import AllowAny + + from django.urls import re_path from django.contrib import admin from django.urls import path @@ -5,16 +11,43 @@ from django.urls import path from .views import userView from .views import spaceView from .views import AssetBundle +from .views import teamView +from .views import meetingView from django.urls import include, path from django.conf import settings from django.conf.urls.static import static + + +schema_view = get_schema_view( + openapi.Info( + title="Your API Title", + default_version='v1', + description="API documentation", + ), + public=True, + permission_classes=(AllowAny,), + authentication_classes=[], # Leave this empty here +) + + + + urlpatterns = [ + + + path('admin/', admin.site.urls), + # Swagger + + path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), + path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), + + # re_path(r'^signup/$', userView.signup, name="signup"), re_path('signup', userView.signup , name="signup"), @@ -56,6 +89,12 @@ urlpatterns = [ re_path('get_assigned_assetbundle_rooms', AssetBundle.getAssignedAssetBundleRooms , name='get_assigned_assetbundle_rooms'), + + re_path('add_team', teamView.addTeam), + re_path('get_team', teamView.getTeams), + + path('add_meeting', meetingView. addMeeting, name='add_meeting'), + ]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/core/views/meetingView.py b/core/views/meetingView.py new file mode 100644 index 0000000..6dfca82 --- /dev/null +++ b/core/views/meetingView.py @@ -0,0 +1,41 @@ +from rest_framework.decorators import api_view, authentication_classes, permission_classes +from rest_framework.permissions import IsAuthenticated +from rest_framework.authentication import TokenAuthentication, SessionAuthentication +from rest_framework.response import Response +from rest_framework import status +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi +from core.serializers.MeetingSerializer import MeetingSerializer + + +@swagger_auto_schema( + method='post', + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + required=['name', 'description', 'date_time'], + properties={ + 'name': openapi.Schema(type=openapi.TYPE_STRING, default='Sprint Planning'), + 'description': openapi.Schema(type=openapi.TYPE_STRING, default='Discuss the next sprint'), + 'date_time': openapi.Schema(type=openapi.TYPE_STRING, format='date-time', default='2025-06-01T14:00:00Z'), + 'space': openapi.Schema(type=openapi.TYPE_INTEGER, default=1), + 'asset_bundle': openapi.Schema(type=openapi.TYPE_INTEGER, default=1), + 'use_space': openapi.Schema(type=openapi.TYPE_BOOLEAN, default=False), + } + ) +) +@api_view(['POST']) +@authentication_classes([SessionAuthentication, TokenAuthentication]) +@permission_classes([IsAuthenticated]) +def addMeeting(request): + data = request.data.copy() + data['creator_user'] = request.user.id + + serializer = MeetingSerializer(data=data) + if serializer.is_valid(): + meeting = serializer.save() + return Response({ + "message": "Meeting created successfully.", + "meeting": serializer.data + }, status=status.HTTP_201_CREATED) + + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/core/views/spaceView.py b/core/views/spaceView.py index 3daac53..972cfe9 100644 --- a/core/views/spaceView.py +++ b/core/views/spaceView.py @@ -8,7 +8,8 @@ from core.models.AssetBundleRoom import AssetBundleRoom from core.serializers.SpaceSerializer import SpaceSerializer , SpaceSerializerforadd - +from drf_yasg import openapi +from drf_yasg.utils import swagger_auto_schema @@ -34,7 +35,19 @@ def getSpaces(request): - +@swagger_auto_schema( + method='post', + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + required=['assetBundleRoomId', 'name', 'description', 'capacity'], + properties={ + 'assetBundleRoomId': openapi.Schema(type=openapi.TYPE_STRING, default='1'), + 'name': openapi.Schema(type=openapi.TYPE_STRING, default='ROOM1'), + 'description': openapi.Schema(type=openapi.TYPE_STRING, default='TEST DES'), + 'capacity': openapi.Schema(type=openapi.TYPE_STRING, default='12'), + } + ) +) @api_view(['POST']) @authentication_classes([SessionAuthentication, TokenAuthentication]) @permission_classes([IsAuthenticated]) diff --git a/core/views/teamView.py b/core/views/teamView.py new file mode 100644 index 0000000..9cf3006 --- /dev/null +++ b/core/views/teamView.py @@ -0,0 +1,65 @@ +from rest_framework.decorators import api_view, authentication_classes, permission_classes +from rest_framework.authentication import SessionAuthentication, TokenAuthentication +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework import status + +from core.models.Team import Team +from core.serializers.TeamSerializer import TeamSerializer + + +from drf_yasg import openapi +from drf_yasg.utils import swagger_auto_schema + + + + + +@swagger_auto_schema( + method='post', + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + required=['name', 'description', 'max_persons', 'subscriptionId'], + properties={ + 'name': openapi.Schema(type=openapi.TYPE_STRING, default='team1'), + 'description': openapi.Schema(type=openapi.TYPE_STRING, default='TEST DES'), + 'max_persons': openapi.Schema(type=openapi.TYPE_STRING, default='9'), + 'subscriptionId': openapi.Schema(type=openapi.TYPE_STRING, default='1'), + } + ) +) +@api_view(['POST']) +@authentication_classes([SessionAuthentication, TokenAuthentication]) +@permission_classes([IsAuthenticated]) +def addTeam(request): + data = request.data.copy() + data['admin'] = request.user.id + data['subscription'] = data.get('subscriptionId') # Map subscriptionId to FK field + + serializer = TeamSerializer(data=data) + + if serializer.is_valid(): + team = serializer.save() + return Response({ + "message": "Team created successfully.", + "team": serializer.data + }, status=status.HTTP_201_CREATED) + + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + + + + + +@api_view(['GET']) +@authentication_classes([SessionAuthentication, TokenAuthentication]) +@permission_classes([IsAuthenticated]) +def getTeams(request): + user = request.user + teams = Team.objects.filter(admin=user) + serializer = TeamSerializer(teams, many=True) + + return Response({ + "teams": serializer.data + }, status=status.HTTP_200_OK) \ No newline at end of file diff --git a/core/views/userView.py b/core/views/userView.py index ecda0d5..7d2cb75 100644 --- a/core/views/userView.py +++ b/core/views/userView.py @@ -22,6 +22,7 @@ from core.serializers.VideoSerializer import VideoSerializer from core.serializers.PdfSerializer import PdfSerializer from core.serializers.SubscriptionSerializer import SubscriptionSerializer from core.serializers.TeamMemberSerializer import TeamMemberSerializer +from core.serializers.TeamSerializer import TeamSerializer from core.serializers.SpaceSerializer import SpaceSerializer # utils.py @@ -32,6 +33,7 @@ from core.models.pdf import Pdf from core.models.glb import Glb from core.models.Subscription import Subscription from core.models.TeamMember import TeamMember +from core.models.Team import Team from django.core.mail import EmailMultiAlternatives from django.template.loader import render_to_string @@ -229,6 +231,24 @@ def sendCodeAndNewPassword(request): +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi +# Define request body schema for Swagger +login_schema = openapi.Schema( + type=openapi.TYPE_OBJECT, + required=['mobile_number', 'password'], + properties={ + 'mobile_number': openapi.Schema(type=openapi.TYPE_STRING, example='09140086509'), + 'password': openapi.Schema(type=openapi.TYPE_STRING, example='12345678'), + }, +) + +@swagger_auto_schema( + method='post', + request_body=login_schema, + responses={200: 'Success', 401: 'Unauthorized'} +) + @api_view(['POST']) def login(request): mobile_number = request.data.get('mobile_number') @@ -994,33 +1014,7 @@ def addSubscription(request): -# @api_view(['POST']) -# @authentication_classes([SessionAuthentication, TokenAuthentication]) -# @permission_classes([IsAuthenticated]) -# def addTeamMember(request): -# # Make a mutable copy of the request data -# data = request.data.copy() -# data['adminUser'] = request.user.id # Automatically assign the authenticated user as admin - -# # Pass the request object to the serializer context -# serializer = TeamMemberSerializer(data=data, context={'request': request}) - -# if serializer.is_valid(): -# # Save the team member using the validated data -# team_member = serializer.save() # This automatically saves the team member - -# # Return the response with the team member data -# return Response({ -# "message": "Team member added successfully.", -# "team_member": serializer.data # This gives you the serialized data of the saved team member -# }, status=status.HTTP_201_CREATED) - -# else: -# # If validation fails, return the errors -# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - - + @@ -1041,7 +1035,7 @@ def addSubscription(request): @api_view(['POST']) @authentication_classes([SessionAuthentication, TokenAuthentication]) @permission_classes([IsAuthenticated]) -def addTeamMember(request): +def addTeamMember2(request): # Print the incoming data for debugging purposes print("Received data:", request.data) @@ -1107,8 +1101,98 @@ def addTeamMember(request): return Response(user_serializer.errors, status=status.HTTP_400_BAD_REQUEST) +@swagger_auto_schema( + method='post', + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + required=['mobile_number', 'first_name', 'last_name', 'semat', 'password', 'isAdmin', 'teamId'], + properties={ + 'mobile_number': openapi.Schema(type=openapi.TYPE_STRING, default='09140086605'), + 'first_name': openapi.Schema(type=openapi.TYPE_STRING, default='mahdi'), + 'last_name': openapi.Schema(type=openapi.TYPE_STRING, default='arabi'), + 'semat': openapi.Schema(type=openapi.TYPE_STRING, default='modir'), + 'password': openapi.Schema(type=openapi.TYPE_STRING, default='12345678'), + 'isAdmin': openapi.Schema(type=openapi.TYPE_BOOLEAN, default=True), + 'teamId': openapi.Schema(type=openapi.TYPE_STRING, default='1'), + } + ) +) +@api_view(['POST']) +@authentication_classes([SessionAuthentication, TokenAuthentication]) +@permission_classes([IsAuthenticated]) +def addTeamMember(request): + print("Received data:", request.data) + # Check for required fields + if 'mobile_number' not in request.data or not request.data['mobile_number'].strip(): + return Response({'mobile_number': ['This field is required and cannot be empty.']}, status=status.HTTP_400_BAD_REQUEST) + if 'teamId' not in request.data: + return Response({'teamId': ['This field is required.']}, status=status.HTTP_400_BAD_REQUEST) + + # Get the team + try: + team = Team.objects.get(id=request.data['teamId']) + except Team.DoesNotExist: + return Response({'teamId': ['Team not found.']}, status=status.HTTP_404_NOT_FOUND) + + # Check for duplicate username + if User.objects.filter(username=request.data['mobile_number']).exists(): + return Response({'username': ['A user with that username already exists.']}, status=status.HTTP_400_BAD_REQUEST) + + # Create user + user_serializer = UserSerializer(data=request.data) + if user_serializer.is_valid(): + user = user_serializer.save() + + username = request.data['mobile_number'] + if not username.strip(): + return Response({'username': ['Username cannot be empty.']}, status=status.HTTP_400_BAD_REQUEST) + + user.set_password(request.data['password']) + user.username = username + user.first_name = request.data['first_name'] + user.last_name = request.data['last_name'] + user.save() + + # Create Customer entry + customer_data = { + 'user': user.id, + 'mobile_number': request.data['mobile_number'], + 'semat': request.data['semat'], + } + + customer_serializer = CustomerSerializer(data=customer_data) + if customer_serializer.is_valid(): + customer_serializer.save() + token = Token.objects.create(user=user) + + # Create TeamMember + team_member_data = { + 'user': user.id, + 'team': team.id, + 'isAdmin': False + } + + serializer = TeamMemberSerializer(data=team_member_data) + if serializer.is_valid(): + team_member = serializer.save() + + return Response({ + "message": "Team member added successfully.", + "team_member": serializer.data + }, status=status.HTTP_201_CREATED) + + else: + user.delete() + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + else: + user.delete() + return Response(customer_serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + else: + return Response(user_serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/media/user_files/Screenshot_2.png b/media/user_files/Screenshot_2.png new file mode 100644 index 0000000..b1cc450 Binary files /dev/null and b/media/user_files/Screenshot_2.png differ diff --git a/media/user_imgs/Screenshot_2.png b/media/user_imgs/Screenshot_2.png new file mode 100644 index 0000000..b1cc450 Binary files /dev/null and b/media/user_imgs/Screenshot_2.png differ diff --git a/requirements.txt b/requirements.txt index fca4b01..4271f2a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,4 @@ djangorestframework==3.15.1 django-cors-headers==4.3.1 pymysql==1.1.0 python-dotenv==1.0.1 -mysqlclient==2.1.1 - -py -m pip install Django==5.2 +mysqlclient==2.1.1 \ No newline at end of file diff --git a/test.rest b/test.rest index a2ff269..e75886c 100644 --- a/test.rest +++ b/test.rest @@ -3,14 +3,17 @@ POST http://127.0.0.1:8000/signup Content-Type: application/json -{ "username": "09140086509", "password": "12345678", "email": "adam2@mail.com" , "mobile_number":"09140086509" } +{ "username": "09140086509", "password": "12345678", +"email": "adam2@mail.com" , "mobile_number":"09140086509", +"first_name":"mahdi" , "last_name":"arabi" , "semat":"مدیر" + } ### POST http://127.0.0.1:8000/login Content-Type: application/json -{ "mobile_number":"09140086509", "password": "13245678" } +{ "mobile_number":"09140086509", "password": "12345678" } ### @@ -91,10 +94,17 @@ Authorization: token d3f1b03996140c8f7561d67221953ff704b482cb POST http://127.0.0.1:8000/add_teamMember Content-Type: application/json -Authorization: token 8dcae0063521ca707a9d0ab6ce3d4794a90064ca - -{ "mobile_number": "09140086608", "first_name":"mahdi", "last_name":"arabi", "semat":"modir", "password": "12345678", "isAdmin":"true" } +Authorization: token e2792ac06153e10f00949a1924483a07629cc753 +{ + "mobile_number": "09140086605", + "first_name": "mahdi", + "last_name": "arabi", + "semat": "modir", + "password": "12345678", + "isAdmin": true, + "teamId": "1" +} @@ -118,4 +128,24 @@ Authorization: token 8dcae0063521ca707a9d0ab6ce3d4794a90064ca GET http://127.0.0.1:8000/get_assigned_assetbundle_rooms Content-Type: application/json Authorization: token 8dcae0063521ca707a9d0ab6ce3d4794a90064ca - \ No newline at end of file + + + + +### + +POST http://127.0.0.1:8000/add_team +Content-Type: application/json +Authorization: token e2792ac06153e10f00949a1924483a07629cc753 + +{ "name":"team1", "description":"TEST DES", "max_persons":"9" , "capacity": "12" } + + + + + +### +GET http://127.0.0.1:8000/get_team +Content-Type: application/json +Authorization: token e2792ac06153e10f00949a1924483a07629cc753 + diff --git a/testServer.rest b/testServer.rest index 3acc0d4..b9f0a52 100644 --- a/testServer.rest +++ b/testServer.rest @@ -118,4 +118,14 @@ Authorization: token a6e1d5db61c4e8b2a0a4c0b623915e3b3a68df40 GET http://my.xroomapp.com:8000/get_assigned_assetbundle_rooms Content-Type: application/json Authorization: token a6e1d5db61c4e8b2a0a4c0b623915e3b3a68df40 - \ No newline at end of file + + + + +### + +POST http://my.xroomapp.com:8000/add_team +Content-Type: application/json +Authorization: token a6e1d5db61c4e8b2a0a4c0b623915e3b3a68df40 + +{ "assetBundleRoomId": "1", "name":"ROOM1", "description":"TEST DES", "capacity": "12" }