Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ jobs:
env:
VITE_CLERK_PUBLISHABLE_KEY: ${{ secrets.VITE_CLERK_PUBLISHABLE_KEY }}
VITE_SERVER_HOST: ${{ secrets.VITE_SERVER_HOST }}
VITE_GEMINI_API_KEY: ${{ secrets.VITE_GEMINI_API_KEY }}
working-directory: frontend/bitmatch

- name: Sync build to S3
Expand Down
2 changes: 1 addition & 1 deletion backend/projects/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Register the Project model
@admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
list_display = ('title', 'group', 'institution', 'match_percentage', 'followers_count', 'likes_count')
list_display = ('title', 'group', 'institution', 'followers_count', 'likes_count')
search_fields = ('title', 'group', 'institution')
list_filter = ('group', 'institution')

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.1.7 on 2025-04-26 03:39

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('projects', '0002_initial'),
]

operations = [
migrations.RemoveField(
model_name='project',
name='match_percentage',
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 5.1.7 on 2025-04-26 05:09

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('projects', '0003_remove_project_match_percentage'),
]

operations = [
migrations.AlterField(
model_name='project',
name='description',
field=models.TextField(max_length=255),
),
migrations.AlterField(
model_name='project',
name='full_description',
field=models.TextField(blank=True, max_length=2500, null=True),
),
migrations.AlterField(
model_name='project',
name='wanted_description',
field=models.TextField(blank=True, max_length=2500, null=True),
),
]
18 changes: 18 additions & 0 deletions backend/projects/migrations/0005_alter_project_location.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-04-26 05:57

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('projects', '0004_alter_project_description_and_more'),
]

operations = [
migrations.AlterField(
model_name='project',
name='location',
field=models.CharField(max_length=255),
),
]
10 changes: 4 additions & 6 deletions backend/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,30 @@ class Project(models.Model):
# REQUIRED
title = models.CharField(max_length=255, blank=False, null=False)
institution = models.CharField(max_length=255, blank=False, null=False)
description = models.TextField(blank=False, null=False)
description = models.TextField(max_length=255,blank=False, null=False)
positions = models.JSONField(default=list)
image_url = models.ImageField(upload_to="projects/", blank=False, null=False)

# Not Required
group = models.CharField(max_length=255, blank=True, null=True)
followers_count = models.IntegerField(default=0)
likes_count = models.IntegerField(default=0)
match_percentage = models.FloatField(blank=True, null=True)
location = models.JSONField(default=list, blank=True)
location = models.CharField(max_length=255, blank=False, null=False)
images = models.JSONField(default=list, blank=True)
interest_tags = ArrayField(models.CharField(max_length=225), blank=True, null=True)
skill_tags = ArrayField(models.CharField(max_length=225), blank=True, null=True)
full_description = models.CharField(max_length=1000, blank=True, null=True)
full_description = models.TextField(max_length=2500, blank=True, null=True)
email = models.EmailField(unique=True, blank=True, null=True)
other_contact = models.CharField(max_length=225, blank=True, null=True)
updates = ArrayField(models.CharField(max_length=540), blank=True, null=True)
wanted_description = models.CharField(max_length=540, blank=True, null=True)
wanted_description = models.TextField(max_length=2500, blank=True, null=True)

members = models.ManyToManyField(
'userauth.User',
related_name='member_of_projects',
blank=True
)

# todo: see if we can remove this without hurting anything
owner = models.ForeignKey(
'userauth.User',
on_delete=models.CASCADE,
Expand Down
12 changes: 2 additions & 10 deletions backend/projects/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ def setUp(self):
# Create a sample project record
self.project = Project.objects.create(
group="CPP SEA",
match_percentage=85.5,
title="Icebreak",
institution="Cal Poly Pomona",
description="We help connect organizations with their members.",
Expand All @@ -23,7 +22,6 @@ def setUp(self):
# Rebecca's test project
self.project2 = Project.objects.create(
group="Codebreaker's Club",
match_percentage=20,
title="Enigma Machine Emulator",
institution="University of California, Irvine",
description="An Enigma machine emulator that replicates the encryption process of the Enigma M3 series used by the Germans.",
Expand All @@ -35,7 +33,6 @@ def setUp(self):
# Luis' test project
self.project3 = Project.objects.create(
group="CodeWarriors",
match_percentage=60,
title="PersonalityPlaylist",
institution="University of California, Riverside",
description="An app to create custom Spotify playlists based on a personality profile.",
Expand All @@ -47,7 +44,6 @@ def setUp(self):
#William's test project
self.project4 = Project.objects.create(
group="Byte Me",
match_percentage=10,
title="F1 Race Machine Learning Model",
institution="University of California, Berkley",
description="A machine learning model that takes data from F1 races and predicts winners based on previous results.",
Expand All @@ -59,7 +55,6 @@ def setUp(self):
def test_project_creation_1(self):
# Check if the record populated correctly
self.assertEqual(self.project.group, "CPP SEA")
self.assertEqual(self.project.match_percentage, 85.5)
self.assertEqual(self.project.title, "Icebreak")
self.assertEqual(self.project.institution, "Cal Poly Pomona")
self.assertEqual(self.project.description, "We help connect organizations with their members.")
Expand All @@ -71,7 +66,6 @@ def test_project_creation_1(self):
# Rebecca's test project
def test_project_creation_2(self):
self.assertEqual(self.project2.group, "Codebreaker's Club")
self.assertEqual(self.project2.match_percentage, 20)
self.assertEqual(self.project2.title, "Enigma Machine Emulator")
self.assertEqual(self.project2.institution, "University of California, Irvine")
self.assertEqual(self.project2.description, "An Enigma machine emulator that replicates the encryption process of the Enigma M3 series used by the Germans.")
Expand All @@ -84,7 +78,6 @@ def test_project_creation_2(self):
def test_project_creation_3(self):
# Check if the record populated correctly
self.assertEqual(self.project3.group, "CodeWarriors")
self.assertEqual(self.project3.match_percentage, 60)
self.assertEqual(self.project3.title, "PersonalityPlaylist")
self.assertEqual(self.project3.institution, "University of California, Riverside")
self.assertEqual(self.project3.description, "An app to create custom Spotify playlists based on a personality profile.")
Expand All @@ -97,7 +90,6 @@ def test_project_creation_3(self):
def test_project_creation_4(self):
# Check if the record populated correctly
self.assertEqual(self.project4.group, "Byte Me")
self.assertEqual(self.project4.match_percentage, 10)
self.assertEqual(self.project4.title, "F1 Race Machine Learning Model")
self.assertEqual(self.project4.institution, "University of California, Berkley")
self.assertEqual(self.project4.description, "A machine learning model that takes data from F1 races and predicts winners based on previous results.")
Expand All @@ -110,10 +102,10 @@ class GetProjectsAPITest(APITestCase):
def setUp(self):
# Add mock project data
self.project1 = Project.objects.create(
title="AI for Healthcare", institution="MIT", match_percentage=85.5
title="AI for Healthcare", institution="MIT"
)
self.project2 = Project.objects.create(
title="Web Dev", institution="Stanford", match_percentage=70.0
title="Web Dev", institution="Stanford"
)
self.url = reverse("get_projects")

Expand Down
5 changes: 3 additions & 2 deletions backend/projects/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from django.urls import path
from .views import ProjectCRUDView, get_projects, toggle_follow, toggle_like
from .views import ProjectCRUDView, get_projects, toggle_follow, toggle_like, project_member_hander

urlpatterns = [
path('', get_projects, name='get_projects'), # Fetch all projects
path('create/', ProjectCRUDView.as_view(), name='project-create'), # Create project
path('<str:pk>/', ProjectCRUDView.as_view(), name='project-detail'), # Read, Update, Delete project by ID
path('like/<str:project_id>', toggle_like, name='toggle_like'),
path('follow/<str:project_id>', toggle_follow, name='toggle_follow')
path('follow/<str:project_id>', toggle_follow, name='toggle_follow'),
path('member/manage/<str:project_id>', project_member_hander, name='project-member-handler'),
]
33 changes: 33 additions & 0 deletions backend/projects/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,36 @@ def toggle_follow(request, project_id):
return Response({'message': 'Project unfollowed successfully'}, status=status.HTTP_200_OK)
else:
return Response({'message': 'You already unfollowed this project.'}, status=status.HTTP_400_BAD_REQUEST)

@api_view(['PATCH'])
@permission_classes([AllowAny])
def project_member_hander(request, project_id):
try:
project = Project.objects.get(id=project_id)
user = User.objects.get(id=request.data['user_id'])
action = request.data.get('action')

is_member = project.members.filter(id=user.id).exists()

if action == "join":
if is_member:
return Response({'error': 'User is already a member of this project'}, status=400)
project.members.add(user)
user.projects.add(project)
return Response({'message': 'Member added successfully'})

elif action == "leave":
if not is_member:
return Response({'error': 'User is not a member of this project'}, status=400)
project.members.remove(user)
user.projects.remove(project)
return Response({'message': 'Member removed successfully'})

else:
return Response({'error': 'Invalid action'}, status=400)

except Project.DoesNotExist:
return Response({'error': 'Project not found'}, status=404)
except User.DoesNotExist:
return Response({'error': 'User not found'}, status=404)

2 changes: 1 addition & 1 deletion backend/userauth/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ class UserAdmin(admin.ModelAdmin):
)
search_fields = ('username', 'email', 'first_name', 'last_name')
list_filter = ('location',)
filter_horizontal = ('projects', 'owned')
filter_horizontal = ('projects',)
17 changes: 17 additions & 0 deletions backend/userauth/migrations/0010_remove_user_owned.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.1.7 on 2025-04-26 03:39

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('userauth', '0009_remove_user_colleges_user_about_me_user_college_and_more'),
]

operations = [
migrations.RemoveField(
model_name='user',
name='owned',
),
]
5 changes: 0 additions & 5 deletions backend/userauth/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,6 @@ class User(models.Model):
related_name='contributors',
blank=True
)
owned = models.ManyToManyField(
'projects.Project',
related_name='owners',
blank=True
)

def __str__(self):
return self.username or f"User-{self.id}"
3 changes: 2 additions & 1 deletion backend/userauth/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from django.urls import path
from .views import UsersCRUDView, check_if_onboarded
from .views import UsersCRUDView, check_if_onboarded, fetch_by_uuid

urlpatterns = [
path('onboard/', UsersCRUDView.as_view(), name='onboard-user'), # Create user
path('onboard/check/<str:clerk_id>', check_if_onboarded, name="onboard-check"), # Check if user onboarded
path('<str:clerk_id>/', UsersCRUDView.as_view(), name='user'), # Read, Update, Delete user by ID
path('fetch/<str:uuid>/', fetch_by_uuid, name='uuid-fetch'),
]
7 changes: 7 additions & 0 deletions backend/userauth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ def check_if_onboarded(request, clerk_id):
return Response({"message": "User already onboarded"})
else:
return Response({"message": "User not onboarded yet!"})

@api_view(['GET'])
@permission_classes([AllowAny])
def fetch_by_uuid(request, uuid):
user = get_object_or_404(User, pk=uuid)
serializer = UserSerializer(user)
return Response(serializer.data)

# DRF CRUD views for Users model
class UsersCRUDView(APIView):
Expand Down
Loading
Loading