Skip to content

Commit

Permalink
sync-2
Browse files Browse the repository at this point in the history
  • Loading branch information
krrish-sehgal committed Jan 26, 2025
2 parents 654ef66 + 399de4a commit 57403a9
Show file tree
Hide file tree
Showing 23 changed files with 1,218 additions and 43 deletions.
7 changes: 7 additions & 0 deletions blt/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@
)
from website.views.slack_handlers import slack_commands, slack_events
from website.views.teams import (
TeamChallenges,
TeamLeaderboard,
TeamOverview,
add_member,
create_team,
Expand All @@ -201,6 +203,7 @@
GlobalLeaderboardView,
InviteCreate,
SpecificMonthLeaderboardView,
UserChallengeListView,
UserDeleteView,
UserProfileDetailsView,
UserProfileDetailView,
Expand Down Expand Up @@ -856,8 +859,12 @@
name="similarity_scan",
),
path("projects/create/", create_project, name="create_project"),
path("teams/challenges/", TeamChallenges.as_view(), name="team_challenges"),
path("teams/leaderboard/", TeamLeaderboard.as_view(), name="team_leaderboard"),
path("challenges/", UserChallengeListView.as_view(), name="user_challenges"),
path("project/<slug:slug>/", ProjectsDetailView.as_view(), name="projects_detail"),
path("slack/events", slack_events, name="slack_events"),
path("owasp/", TemplateView.as_view(template_name="owasp.html"), name="owasp"),

path("discussion-rooms/", RoomsListView.as_view(), name="rooms_list"),
path("discussion-rooms/create/", RoomCreateView.as_view(), name="room_create"),
Expand Down
3 changes: 2 additions & 1 deletion website/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ class WebsiteConfig(AppConfig):
name = "website"

def ready(self):
import website.signals # noqa
import website.challenge_signals # noqa
import website.feed_signals # noqa
199 changes: 199 additions & 0 deletions website/challenge_signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils import timezone

from .models import Challenge, IpReport, Issue, Points, TimeLog, UserProfile


def update_challenge_progress(user, challenge_title, model_class, reason, threshold=None, team_threshold=None):
if not user.is_authenticated:
return
try:
challenge = Challenge.objects.get(title=challenge_title)

if challenge.challenge_type == "team":
# Get the user's team
user_profile = user.userprofile
if user_profile.team is None:
return

team = user_profile.team
if team not in challenge.team_participants.all():
challenge.team_participants.add(team)

total_actions = 0
for member in team.user_profiles.all():
total_actions += model_class.objects.filter(user=member.user).count()

# Calculate progress based on actions performed by the team
team_progress = min((total_actions / team_threshold) * 100, 100)

challenge.progress = int(team_progress)
challenge.save()

if team_progress == 100 and not challenge.completed:
challenge.completed = True # Explicitly mark the challenge as completed
challenge.completed_at = timezone.now() # Track completion time (optional)
challenge.save() # Save changes to the challenge

team.team_points += challenge.points
team.save()
else:
if user not in challenge.participants.all():
challenge.participants.add(user)

user_count = model_class.objects.filter(user=user).count()
progress = min((user_count / threshold) * 100, 100) # Ensure it doesn't exceed 100%

challenge.progress = int(progress)
challenge.save()
print(challenge.completed)
if challenge.progress == 100 and not challenge.completed:
challenge.completed = True # Explicitly mark the challenge as completed
challenge.completed_at = timezone.now()
challenge.save()

# Award points to the user
Points.objects.create(user=user, score=challenge.points, reason=reason)

except Challenge.DoesNotExist:
pass


@receiver(post_save)
def handle_post_save(sender, instance, created, **kwargs):
"""Generic handler for post_save signal."""
if sender == IpReport and created: # Track first IP report
if instance.user and instance.user.is_authenticated:
update_challenge_progress(
user=instance.user,
challenge_title="Report 5 IPs",
model_class=IpReport,
reason="Completed 'Report 5 IPs' challenge",
threshold=5,
)
if instance.user.is_authenticated and instance.user.userprofile.team:
update_challenge_progress(
user=instance.user,
challenge_title="Report 10 IPs",
model_class=IpReport,
reason="Completed 'Report 10 IPs challenge",
team_threshold=10, # For team challenge
)

elif sender == Issue and created: # Track first bug report
if instance.user and instance.user.is_authenticated:
update_challenge_progress(
user=instance.user,
challenge_title="Report 5 Issues",
model_class=Issue,
reason="Completed 'Report 5 Issues challenge",
threshold=5,
)
if instance.user.is_authenticated and instance.user.userprofile.team:
update_challenge_progress(
user=instance.user,
challenge_title="Report 10 Issues",
model_class=Issue,
reason="Completed 'Report 10 Issues challenge",
team_threshold=10, # For team challenge
)


@receiver(post_save, sender=TimeLog)
def update_user_streak(sender, instance, created, **kwargs):
if created and instance.user and instance.user.is_authenticated:
check_in_date = instance.start_time.date() # Extract the date from TimeLog
user = instance.user

try:
user_profile = user.userprofile
user_profile.update_streak_and_award_points(check_in_date)

handle_sign_in_challenges(user, user_profile)

if user_profile.team:
handle_team_sign_in_challenges(user_profile.team)

except UserProfile.DoesNotExist:
pass


def handle_sign_in_challenges(user, user_profile):
"""
Update progress for single challenges based on the user's streak.
"""
try:
print("Handling user sign-in challenge...")
challenge_title = "Sign in for 5 Days"
challenge = Challenge.objects.get(title=challenge_title, challenge_type="single")

if user not in challenge.participants.all():
challenge.participants.add(user)

streak_count = user_profile.current_streak
print(streak_count)

if streak_count >= 5:
progress = 100
else:
progress = streak_count * 100 / 5 # Calculate progress if streak is less than 5
print(progress)
# Update the challenge progress
challenge.progress = int(progress)
challenge.save()

# Award points if the challenge is completed (when streak is 5)
if progress == 100 and not challenge.completed:
challenge.completed = True
challenge.completed_at = timezone.now()
challenge.save()

Points.objects.create(
user=user,
score=challenge.points,
reason=f"Completed '{challenge_title}' challenge",
)

except Challenge.DoesNotExist:
# Handle case when the challenge does not exist
pass


def handle_team_sign_in_challenges(team):
"""
Update progress for team challenges where all members must sign in for 5 days consecutively.
"""
try:
challenge_title = "All Members Sign in for 5 Days" # Title of the team challenge
challenge = Challenge.objects.get(title=challenge_title, challenge_type="team")
print("Handling team sign-in challenge...")

# Ensure the team is registered as a participant
if team not in challenge.team_participants.all():
challenge.team_participants.add(team)

# Get streaks for all team members
streaks = [member.current_streak for member in team.user_profiles.all()]

if streaks: # If the team has members
min_streak = min(streaks)
progress = min((min_streak / 5) * 100, 100)
else:
min_streak = 0
progress = 0

challenge.progress = int(progress)
challenge.save()

if progress == 100 and not challenge.completed:
challenge.completed = True
challenge.completed_at = timezone.now()
challenge.save()

# Add points to the team
team.team_points += challenge.points
team.save()
except Challenge.DoesNotExist:
print(f"Challenge '{challenge_title}' does not exist.")
pass
34 changes: 26 additions & 8 deletions website/consumers.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ async def receive(self, text_data):
branch2 = data.get("branch2") # Branch name for the second repository

if not repo1 or not repo2 or not type1 or not type2:
await self.send(json.dumps({"error": "Both repositories and their types are required."}))
await self.send(
json.dumps({"error": "Both repositories and their types are required."})
)
return

if type1 not in ["github", "zip"] or type2 not in ["github", "zip"]:
Expand Down Expand Up @@ -150,7 +152,9 @@ async def download_and_extract_zip(self, zip_url, temp_dir, repo_name):
async with aiohttp.ClientSession() as session:
async with session.get(zip_url) as response:
if response.status != 200:
raise Exception(f"Failed to download ZIP file. Status code: {response.status}")
raise Exception(
f"Failed to download ZIP file. Status code: {response.status}"
)

# Extract the ZIP file
zip_file_path = Path(temp_dir) / f"{repo_name}.zip"
Expand Down Expand Up @@ -225,7 +229,9 @@ def process_similarity_analysis(self, repo1_path, repo2_path):
if i % 5 == 0:
# Ping the frontend every 5 iterations
try:
asyncio.run(self.send(json.dumps({"ping": "ping"}))) # Send ping from the worker thread
asyncio.run(
self.send(json.dumps({"ping": "ping"}))
) # Send ping from the worker thread
except Exception as e:
return None # Stop the analysis if the connection is lost
i += 1
Expand All @@ -241,14 +247,23 @@ def process_similarity_analysis(self, repo1_path, repo2_path):
for func1 in functions1:
for func2 in functions2:
name_similarity = (
difflib.SequenceMatcher(None, func1["signature"]["name"], func2["signature"]["name"]).ratio() * 100
difflib.SequenceMatcher(
None, func1["signature"]["name"], func2["signature"]["name"]
).ratio()
* 100
)

# Signature similarity using difflib
signature1 = f"{func1['signature']['name']}({', '.join(func1['signature']['args'])})"
signature2 = f"{func2['signature']['name']}({', '.join(func2['signature']['args'])})"
signature1 = (
f"{func1['signature']['name']}({', '.join(func1['signature']['args'])})"
)
signature2 = (
f"{func2['signature']['name']}({', '.join(func2['signature']['args'])})"
)

signature_similarity = difflib.SequenceMatcher(None, signature1, signature2).ratio() * 100
signature_similarity = (
difflib.SequenceMatcher(None, signature1, signature2).ratio() * 100
)

# Content similarity using OpenAI embeddings
fulltext1 = func1["full_text"]
Expand Down Expand Up @@ -287,7 +302,9 @@ def process_similarity_analysis(self, repo1_path, repo2_path):
models2 = extract_django_models(repo2_path)
for model1 in models1:
for model2 in models2:
model_similarity = difflib.SequenceMatcher(None, model1["name"], model2["name"]).ratio() * 100
model_similarity = (
difflib.SequenceMatcher(None, model1["name"], model2["name"]).ratio() * 100
)

model_fields_similarity = compare_model_fields(model1, model2)
matching_details["models"].append(
Expand All @@ -305,3 +322,4 @@ def process_similarity_analysis(self, repo1_path, repo2_path):
return None

return matching_details

7 changes: 2 additions & 5 deletions website/signals.py → website/feed_signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def handle_post_save(sender, instance, created, **kwargs):
@receiver(pre_delete)
def handle_pre_delete(sender, instance, **kwargs):
"""Generic handler for pre_delete signal."""
if sender in [Issue, Hunt, IpReport, Post]: # Add any model you want to track
if sender in [Issue, Hunt, IpReport, Post]:
create_activity(instance, "deleted")


Expand All @@ -106,15 +106,12 @@ def update_user_streak(sender, instance, created, **kwargs):
"""
Automatically update user's streak when a TimeLog is created
"""
if created:
# Use the date of the start_time for streak tracking
if created and instance.user and instance.user.is_authenticated:
check_in_date = instance.start_time.date()
# Get the user's profile and update streak
try:
user_profile = instance.user.userprofile
user_profile.update_streak_and_award_points(check_in_date)
except UserProfile.DoesNotExist:
# Fallback: create profile if it doesn't exist
UserProfile.objects.create(
user=instance.user, current_streak=1, longest_streak=1, last_check_in=check_in_date
)
56 changes: 56 additions & 0 deletions website/migrations/0173_challenge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Generated by Django 5.1.3 on 2024-12-18 18:54

from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("website", "0172_merge_20241218_0505"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name="Challenge",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("title", models.CharField(max_length=255)),
("description", models.TextField()),
(
"challenge_type",
models.CharField(
choices=[("single", "Single User"), ("team", "Team")],
default="single",
max_length=10,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("points", models.IntegerField(default=0)),
("progress", models.IntegerField(default=0)),
("completed", models.BooleanField(default=False)),
("completed_at", models.DateTimeField(blank=True, null=True)),
(
"participants",
models.ManyToManyField(
blank=True,
related_name="user_challenges",
to=settings.AUTH_USER_MODEL,
),
),
(
"team_participants",
models.ManyToManyField(blank=True, related_name="team_challenges", to="website.organization"),
),
],
),
]
Loading

0 comments on commit 57403a9

Please sign in to comment.