Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development #1529

Open
wants to merge 35 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9a1bbaa
add micro cohorts to cohort model
gustavomm19 Nov 7, 2024
1faf214
Merge branch 'development' of https://github.com/gustavomm19/apiv2 in…
gustavomm19 Nov 14, 2024
c44da0d
add color to cohorts
gustavomm19 Nov 14, 2024
6e57c0c
add color to serializer
gustavomm19 Nov 14, 2024
7bf3fce
create cohort users in micro cohorts when cohort user is created in t…
gustavomm19 Nov 14, 2024
039a8c1
add signal to graduate student from main cohorts after it is graduate…
gustavomm19 Nov 15, 2024
1e85dbd
add cohort to user me task serializer
gustavomm19 Nov 15, 2024
490a0c9
add cohort user only if it does not exist yet
gustavomm19 Nov 18, 2024
f629cf5
add tests to graduate saas main cohort on all micro cohort student gr…
gustavomm19 Nov 20, 2024
b8ff2ca
add tests to new_cohort_user signal
gustavomm19 Nov 27, 2024
79c19fd
fix tests, add works api
jefer94 Dec 2, 2024
5b1b2c3
Merge pull request #1510 from jefer94/feat/works-api
jefer94 Dec 2, 2024
b538f05
Merge pull request #1494 from gustavomm19/marketing-cohorts
jefer94 Dec 3, 2024
2b4bd5a
adding featured course to tech
lumi-tip Dec 4, 2024
00fd11a
Merge pull request #1511 from lumi-tip/development-lumi-7920
jefer94 Dec 5, 2024
4e4371d
Merge pull request #1518 from breatheco-de/main
tommygonzaleza Dec 16, 2024
be90f0c
Migrations conflict fixed
tommygonzaleza Dec 16, 2024
4099ac8
add micro cohort users in the same as the main cohort user
gustavomm19 Dec 18, 2024
fd3393b
Merge pull request #1519 from gustavomm19/marketing-cohorts
jefer94 Dec 18, 2024
898b2fb
Merge branch 'main' of https://github.com/breatheco-de/apiv2 into dev…
jefer94 Dec 19, 2024
839b75f
fix tests, fix google oauth
jefer94 Dec 23, 2024
49e2db2
remove prints
jefer94 Dec 23, 2024
015b6e2
Add id in studentPOSTSerializer
avanegasp Dec 26, 2024
25e9120
Merge branch 'development' into available_as_saas
avanegasp Dec 26, 2024
3c91dfb
add micro cohorts to public cohort serializer
gustavomm19 Dec 26, 2024
6236f13
Merge pull request #1523 from gustavomm19/micro-cohorts-serializer
jefer94 Dec 26, 2024
d2eff12
Add id in different tests
avanegasp Dec 30, 2024
a6e99e5
Merge pull request #1524 from avanegasp/available_as_saas
jefer94 Dec 30, 2024
91de21e
add endpoints to be used by works api
jefer94 Dec 31, 2024
f014b2d
Merge pull request #1525 from jefer94/development
jefer94 Dec 31, 2024
6c1ad70
add App model to prevent error on migrations
gustavomm19 Jan 7, 2025
64e5287
Merge pull request #1527 from gustavomm19/temporary-fix
tommygonzaleza Jan 7, 2025
8caa0d8
add commands to fix out dated assets slugs in syllabus and tasks
gustavomm19 Jan 8, 2025
7d5ad93
Merge pull request #1528 from gustavomm19/syllabus-assets
tommygonzaleza Jan 9, 2025
1f36bc8
Merge pull request #1532 from breatheco-de/main
tommygonzaleza Jan 10, 2025
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
2 changes: 1 addition & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ django-storages = {extras = ["google"] }
aiohttp = {extras = ["speedups"] }
aiodns = "*"
eventlet = "*"
linked-services = {extras = ["django", "aiohttp", "requests"] }
celery-task-manager = {extras = ["django"] }
django-sql-explorer = {extras = ["xls"] }
contextlib2 = "*"
Expand All @@ -157,3 +156,4 @@ pyright = "*"
mypy = "*"
python-magic = "*"
uvicorn = {extras = ["standard"], version = "*"}
linked-services = {extras = ["requests", "django", "aiohttp"], version = "*"}
4,386 changes: 2,184 additions & 2,202 deletions Pipfile.lock

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions breathecode/activity/management/commands/upload_activities.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import os

from django.core.cache import cache
from django.core.management.base import BaseCommand
from django.utils import timezone
from django.core.cache import cache

from breathecode.activity import tasks

IS_DJANGO_REDIS = hasattr(cache, "delete_pattern")
IS_DJANGO_REDIS = hasattr(cache, "fake") is False


def db_backup_bucket():
Expand Down
4 changes: 2 additions & 2 deletions breathecode/activity/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import Optional

import zstandard
from capyc.core.managers import feature
from celery import shared_task
from django.core.cache import cache
from django.utils import timezone
Expand All @@ -24,7 +25,6 @@
from breathecode.utils import NDB
from breathecode.utils.decorators import TaskPriority
from breathecode.utils.redis import Lock
from capyc.core.managers import feature

from .models import StudentActivity

Expand All @@ -38,7 +38,7 @@ def get_activity_sampling_rate():
return 60


IS_DJANGO_REDIS = hasattr(cache, "delete_pattern")
IS_DJANGO_REDIS = hasattr(cache, "fake") is False

API_URL = os.getenv("API_URL", "")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import json
from django.core.management.base import BaseCommand
from .models import SyllabusVersion
from breathecode.registry.models import Asset


class Command(BaseCommand):
help = "Replace asset aliases in the syllabus with the current slugs"

def handle(self, *args, **options):
try:
from breathecode.certificate.actions import syllabus_weeks_to_days

syllabus_list = SyllabusVersion.objects.all()
key_map = {
"QUIZ": "quizzes",
"LESSON": "lessons",
"EXERCISE": "replits",
"PROJECT": "assignments",
}

for s in syllabus_list:
if isinstance(s.json, str):
s.json = json.loads(s.json)

# in case the json contains "weeks" instead of "days"
s.json = syllabus_weeks_to_days(s.json)

for module_index, day in enumerate(s.json["days"]):

for asset_type in key_map:
if key_map[asset_type] not in day:
continue

for asset_index, assignment in enumerate(day[key_map[asset_type]]):
assignment_slug = assignment["slug"] if isinstance(assignment, dict) else assignment
asset = Asset.get_by_slug(assignment_slug)

if asset is not None and (asset.lang not in ["us", "en"] or asset.slug != assignment_slug):
updated_slug = assignment_slug

if asset.lang not in ["us", "en"]:
english_translation = asset.all_translations.filter(lang__in=["en", "us"]).first()
updated_slug = english_translation.slug
elif asset.slug != assignment_slug:
updated_slug = asset.slug

if isinstance(assignment, dict):
s.json["days"][module_index][key_map[asset_type]][asset_index][
"slug"
] = updated_slug
else:
s.json["days"][module_index][key_map[asset_type]][asset_index] = updated_slug

s.save()

except Exception:
self.stderr.write("Failed to fix assets slugs in all syllabus")
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 5.1.2 on 2024-11-07 22:18

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("admissions", "0064_academy_legal_name"),
]

operations = [
migrations.AddField(
model_name="cohort",
name="cohorts_order",
field=models.CharField(
blank=True,
default=None,
help_text="An IDs comma separated list to indicate the order in which the micro cohorts will be displayed",
max_length=50,
null=True,
),
),
migrations.AddField(
model_name="cohort",
name="micro_cohorts",
field=models.ManyToManyField(
blank=True,
help_text="This cohorts will represent small courses inside a main course",
related_name="cohorts",
to="admissions.cohort",
),
),
]
24 changes: 24 additions & 0 deletions breathecode/admissions/migrations/0066_cohort_color.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 5.1.2 on 2024-11-14 12:32

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("admissions", "0065_cohort_cohorts_order_cohort_micro_cohorts"),
]

operations = [
migrations.AddField(
model_name="cohort",
name="color",
field=models.CharField(
blank=True,
default=None,
help_text="Add the color with hexadecimal format, i.e.: #FFFFFF",
max_length=50,
null=True,
),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.1.2 on 2024-11-15 09:48

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("admissions", "0066_cohort_color"),
]

operations = [
migrations.AlterField(
model_name="cohort",
name="micro_cohorts",
field=models.ManyToManyField(
blank=True,
help_text="This cohorts will represent small courses inside a main course",
related_name="main_cohorts",
to="admissions.cohort",
),
),
]
13 changes: 13 additions & 0 deletions breathecode/admissions/migrations/0068_merge_20241216_1552.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 5.1.3 on 2024-12-16 15:52

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("admissions", "0065_alter_cohortuser_educational_status"),
("admissions", "0067_alter_cohort_micro_cohorts"),
]

operations = []
22 changes: 22 additions & 0 deletions breathecode/admissions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,28 @@ class Cohort(models.Model):

language = models.CharField(max_length=2, default="en", db_index=True)

micro_cohorts = models.ManyToManyField(
"Cohort",
blank=True,
help_text="This cohorts will represent small courses inside a main course",
related_name="main_cohorts",
)

cohorts_order = models.CharField(
max_length=50,
null=True,
blank=True,
default=None,
help_text="An IDs comma separated list to indicate the order in which the micro cohorts will be displayed",
)
color = models.CharField(
max_length=50,
null=True,
blank=True,
default=None,
help_text="Add the color with hexadecimal format, i.e.: #FFFFFF",
)

created_at = models.DateTimeField(auto_now_add=True, editable=False)
updated_at = models.DateTimeField(auto_now=True, editable=False)

Expand Down
50 changes: 46 additions & 4 deletions breathecode/admissions/receivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging
import re
from typing import Any, Type
from asgiref.sync import sync_to_async

from django.dispatch import receiver

Expand All @@ -12,7 +13,7 @@

from ..activity import tasks as activity_tasks
from .models import Cohort, CohortUser
from .signals import cohort_log_saved, cohort_user_created
from .signals import cohort_log_saved, cohort_user_created, student_edu_status_updated

# add your receives here
logger = logging.getLogger(__name__)
Expand All @@ -27,9 +28,25 @@ def process_cohort_history_log(sender: Type[Cohort], instance: Cohort, **kwargs:
activity_tasks.get_attendancy_log.delay(instance.id)


@receiver(cohort_user_created, sender=Cohort)
async def new_cohort_user(sender: Type[Cohort], instance: Cohort, **kwargs: Any):
logger.info("Processing Cohort history log for cohort: " + str(instance.id))
@sync_to_async
def join_to_micro_cohorts(cohort_user):

micro_cohorts = cohort_user.cohort.micro_cohorts.all()

user = cohort_user.user
for cohort in micro_cohorts:
micro_cohort_user = CohortUser.objects.filter(user=user, cohort=cohort, role=cohort_user.role).first()
if micro_cohort_user is None:
micro_cohort_user = CohortUser(
user=user, cohort=cohort, role=cohort_user.role, finantial_status="FULLY_PAID"
)
micro_cohort_user.save()


@receiver(cohort_user_created, sender=CohortUser)
async def new_cohort_user(sender: Type[CohortUser], instance: CohortUser, **kwargs: Any):
logger.info("Signal for created cohort user: " + str(instance.id))
await join_to_micro_cohorts(instance)

await authenticate_actions.send_webhook(
"rigobot",
Expand Down Expand Up @@ -76,6 +93,31 @@ def schedule_repository_deletion(sender: Type[Task], instance: Task, **kwargs: A
order.save()


@receiver(student_edu_status_updated, sender=CohortUser)
def post_save_cohort_user(sender: Type[CohortUser], instance: CohortUser, **kwargs: Any):
logger.info("Validating if the student is graduating from a saas cohort")
cohort = instance.cohort

if instance.cohort is None:
return

if cohort.available_as_saas and instance.educational_status == "GRADUATED":
# main_cohorts is the backwards relationship for the many to many
# it contains every cohort that another cohort is linked to as a micro cohort
main_cohorts = cohort.main_cohorts.all()
for main in main_cohorts:
main_cohort_user = CohortUser.objects.filter(cohort=main, user=instance.user).first()
if main_cohort_user.educational_status != "GRADUATED":
main_cohort = main_cohort_user.cohort
micro_cohorts = main_cohort.micro_cohorts.all()
cohort_users = CohortUser.objects.filter(user=instance.user, cohort__in=micro_cohorts).exclude(
educational_status__in=["GRADUATED"]
)
if len(cohort_users) == 0:
main_cohort_user.educational_status = "GRADUATED"
main_cohort_user.save()


@receiver(revision_status_updated, sender=Task, weak=False)
def mark_saas_student_as_graduated(sender: Type[Task], instance: Task, **kwargs: Any):
logger.info("Processing available as saas student's tasks and marking as GRADUATED if it is")
Expand Down
21 changes: 21 additions & 0 deletions breathecode/admissions/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@
logger = logging.getLogger(__name__)


class GetTinyCohortSerializer(serpy.Serializer):
"""The serializer schema definition."""

# Use a Field subclass like IntField if you need more validation.
id = serpy.Field()
name = serpy.Field()
slug = serpy.Field()


class CountrySerializer(serpy.Serializer):
"""The serializer schema definition."""

Expand Down Expand Up @@ -361,6 +370,7 @@ class PublicCohortSerializer(serpy.Serializer):
name = serpy.Field()
never_ends = serpy.Field()
private = serpy.Field()
micro_cohorts = serpy.MethodField()
language = serpy.Field()
kickoff_date = serpy.Field()
ending_date = serpy.Field()
Expand All @@ -378,6 +388,10 @@ def get_distance(self, obj):

return haversine(obj.longitude, obj.latitude, obj.academy.longitude, obj.academy.latitude)

def get_micro_cohorts(self, obj):
cohorts = obj.micro_cohorts.all()
return GetTinyCohortSerializer(cohorts, many=True).data


class GetSmallCohortSerializer(serpy.Serializer):
"""The serializer schema definition."""
Expand Down Expand Up @@ -433,15 +447,22 @@ class GetMeCohortSerializer(serpy.Serializer):
name = serpy.Field()
kickoff_date = serpy.Field()
ending_date = serpy.Field()
micro_cohorts = serpy.MethodField()
cohorts_order = serpy.Field()
intro_video = serpy.Field()
current_day = serpy.Field()
color = serpy.Field()
current_module = serpy.Field()
syllabus_version = SyllabusVersionSmallSerializer(required=False)
academy = GetAcademySerializer()
stage = serpy.Field()
is_hidden_on_prework = serpy.Field()
available_as_saas = serpy.Field()

def get_micro_cohorts(self, obj):
cohorts = obj.micro_cohorts.all()
return GetTinyCohortSerializer(cohorts, many=True).data


class GetPublicCohortUserSerializer(serpy.Serializer):
user = UserPublicSerializer()
Expand Down
Loading
Loading