diff --git a/backend/config/urls.py b/backend/config/urls.py index 4626daa..7e38ccd 100644 --- a/backend/config/urls.py +++ b/backend/config/urls.py @@ -27,9 +27,9 @@ from hoagiehelp.api.study_group_views import StudyGroupDetailView, StudyGroupListView from hoagiehelp.api.user_views import ( UserView, - user_answers, - user_comments, - user_questions, + get_answers_for_user, + get_comments_for_user, + get_questions_for_user, ) urlpatterns = [ @@ -64,9 +64,9 @@ ), # Users path("users//", UserView.as_view(), name="user-detail"), - path("users//questions/", user_questions, name="user-questions"), - path("users//answers/", user_answers, name="user-answers"), - path("users//comments/", user_comments, name="user-comments"), + path("users//questions/", get_questions_for_user, name="user-questions"), + path("users//answers/", get_answers_for_user, name="user-answers"), + path("users//comments/", get_comments_for_user, name="user-comments"), # Notifications path("notifications/", get_notifications, name="get-notifications"), path( diff --git a/backend/hoagiehelp/api/user_views.py b/backend/hoagiehelp/api/user_views.py index f0f1abe..ade9b11 100644 --- a/backend/hoagiehelp/api/user_views.py +++ b/backend/hoagiehelp/api/user_views.py @@ -52,7 +52,7 @@ def get_questions_for_user(request, user_id: str): return Response(serializer.data, status=status.HTTP_200_OK) -def user_answers(request, user_id: str): +def get_answers_for_user(request, user_id: str): pass diff --git a/backend/hoagiehelp/migrations/0001_initial.py b/backend/hoagiehelp/migrations/0001_initial.py new file mode 100644 index 0000000..a8d309b --- /dev/null +++ b/backend/hoagiehelp/migrations/0001_initial.py @@ -0,0 +1,176 @@ +# Generated by Django 5.2.8 on 2026-09-10 01:35 + +import django.contrib.auth.models +import django.contrib.auth.validators +import django.core.validators +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='Tag', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('name', models.CharField(choices=[('exam prep', 'exam prep'), ('problem set', 'problem set'), ('study tips', 'study tips'), ('concepts', 'concepts'), ('course selection', 'course selection'), ('degree planning', 'degree planning'), ('citation', 'citation'), ('course advice', 'course advice'), ('career', 'career')], max_length=30, unique=True)), + ], + options={ + 'db_table': 'Tag', + }, + ), + migrations.CreateModel( + name='CustomUser', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('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')), + ('net_id', models.CharField(db_index=True, max_length=20, unique=True)), + ('class_year', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1900), django.core.validators.MaxValueValidator(2100)])), + ('hearts', models.PositiveIntegerField(default=0)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('groups', 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')), + ('user_permissions', 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')), + ], + options={ + 'db_table': 'User', + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name='Answer', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('text', models.TextField()), + ('hearts', models.PositiveIntegerField(default=0)), + ('is_anonymous', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'Answer', + }, + ), + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('text', models.TextField()), + ('hearts', models.PositiveIntegerField(default=0)), + ('is_anonymous', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('answer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hoagiehelp.answer')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'Comment', + }, + ), + migrations.CreateModel( + name='Question', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('title', models.CharField(max_length=255)), + ('course', models.CharField(blank=True, db_index=True, max_length=100, null=True)), + ('details', models.TextField()), + ('create_time', models.DateTimeField(auto_now_add=True)), + ('last_updated_time', models.DateTimeField(auto_now=True)), + ('hearts', models.PositiveIntegerField(default=0)), + ('view', models.PositiveIntegerField(default=0)), + ('user_is_anonymous', models.BooleanField(default=False)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('tags', models.ManyToManyField(to='hoagiehelp.tag')), + ], + options={ + 'db_table': 'Question', + }, + ), + migrations.CreateModel( + name='Notification', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('is_read', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('answer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hoagiehelp.answer')), + ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='hoagiehelp.comment')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hoagiehelp.question')), + ], + options={ + 'db_table': 'Notification', + }, + ), + migrations.AddField( + model_name='answer', + name='question', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hoagiehelp.question'), + ), + migrations.CreateModel( + name='Heart', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('answer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='hoagiehelp.answer')), + ('comment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='hoagiehelp.comment')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('question', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='hoagiehelp.question')), + ], + options={ + 'db_table': 'Heart', + 'constraints': [models.UniqueConstraint(condition=models.Q(('question__isnull', False)), fields=('user', 'question'), name='unique_heart_question'), models.UniqueConstraint(condition=models.Q(('answer__isnull', False)), fields=('user', 'answer'), name='unique_heart_answer'), models.UniqueConstraint(condition=models.Q(('comment__isnull', False)), fields=('user', 'comment'), name='unique_heart_comment'), models.CheckConstraint(condition=models.Q(models.Q(('answer__isnull', True), ('comment__isnull', True), ('question__isnull', False)), models.Q(('answer__isnull', False), ('comment__isnull', True), ('question__isnull', True)), models.Q(('answer__isnull', True), ('comment__isnull', False), ('question__isnull', True)), _connector='OR'), name='heart_exactly_one_target')], + }, + ), + migrations.CreateModel( + name='AnonymousName', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('anonymous_name', models.CharField(max_length=255)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hoagiehelp.question')), + ], + options={ + 'db_table': 'AnonymousName', + 'constraints': [models.UniqueConstraint(fields=('question', 'user'), name='unique_question_user'), models.UniqueConstraint(fields=('question', 'anonymous_name'), name='unique_question_anonymous_name')], + }, + ), + migrations.CreateModel( + name='StudyGroup', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('title', models.CharField(max_length=255)), + ('description', models.TextField(blank=True)), + ('meeting_datetime', models.DateTimeField(db_index=True)), + ('max_spots', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1)])), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('leader', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='study_groups_led', to=settings.AUTH_USER_MODEL)), + ('members', models.ManyToManyField(blank=True, related_name='study_groups_joined', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'StudyGroup', + 'ordering': ['-created_at'], + 'constraints': [models.UniqueConstraint(fields=('leader', 'meeting_datetime', 'title'), name='unique_study_group_per_leader')], + }, + ), + ] diff --git a/backend/hoagiehelp/migrations/__init__.py b/backend/hoagiehelp/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/hoagiehelp/models/__init__.py b/backend/hoagiehelp/models/__init__.py index 8820411..10dd088 100644 --- a/backend/hoagiehelp/models/__init__.py +++ b/backend/hoagiehelp/models/__init__.py @@ -1,17 +1,21 @@ from .anonymous_name import AnonymousName from .answer import Answer from .comment import Comment +from .heart import Heart +from .notification import Notification from .question import Question from .studygroup import StudyGroup from .tag import Tag from .user import CustomUser __all__ = [ - "CustomUser", - "Question", + "AnonymousName", "Answer", "Comment", - "Tag", + "CustomUser", + "Heart", + "Notification", + "Question", "StudyGroup", - "AnonymousName", + "Tag", ] diff --git a/backend/hoagiehelp/models/anonymous_name.py b/backend/hoagiehelp/models/anonymous_name.py index 7725d55..5282410 100644 --- a/backend/hoagiehelp/models/anonymous_name.py +++ b/backend/hoagiehelp/models/anonymous_name.py @@ -13,6 +13,7 @@ class AnonymousName(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) class Meta: + db_table = "AnonymousName" constraints = [ models.UniqueConstraint( fields=["question", "user"], diff --git a/backend/hoagiehelp/models/question.py b/backend/hoagiehelp/models/question.py index 98a1b37..562861f 100644 --- a/backend/hoagiehelp/models/question.py +++ b/backend/hoagiehelp/models/question.py @@ -1,4 +1,3 @@ -from django.core.validators import MinValueValidator from django.db import models from hoagiehelp.models.tag import Tag @@ -16,9 +15,12 @@ class Question(models.Model): details = models.TextField() create_time = models.DateTimeField(auto_now_add=True) last_updated_time = models.DateTimeField(auto_now=True) - hearts = models.IntegerField(default=0, validators=[MinValueValidator(0)]) - view = models.IntegerField(default=0, validators=[MinValueValidator(0)]) + hearts = models.PositiveIntegerField(default=0) + view = models.PositiveIntegerField(default=0) user_is_anonymous = models.BooleanField(default=False) + class Meta: + db_table = "Question" + def __str__(self): return f"Question(id={self.id}, title={self.title})" diff --git a/backend/hoagiehelp/models/studygroup.py b/backend/hoagiehelp/models/studygroup.py index e1e277f..687e966 100644 --- a/backend/hoagiehelp/models/studygroup.py +++ b/backend/hoagiehelp/models/studygroup.py @@ -32,6 +32,7 @@ class StudyGroup(models.Model): updated_at = models.DateTimeField(auto_now=True) class Meta: + db_table = "StudyGroup" ordering = ["-created_at"] constraints = [ models.UniqueConstraint(