Skip to content

Commit

Permalink
Merge pull request #1061 from jhuopensource/develop
Browse files Browse the repository at this point in the history
Release/v5.6.1
  • Loading branch information
JiaqiWang18 authored Dec 9, 2023
2 parents e06a0f6 + 05ebbe6 commit 18fe9fe
Show file tree
Hide file tree
Showing 31 changed files with 962 additions and 2,058 deletions.
8 changes: 8 additions & 0 deletions analytics/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,13 @@
# GNU General Public License for more details.

from django.contrib import admin
from .models import UIErrorLog

# Register your models here.


@admin.register(UIErrorLog)
class UIErrorLogAdmin(admin.ModelAdmin):
"""Enables admins to create, view, and edit news updates at /admin"""

list_display = ("name", "message", "user", "time_occurred")
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Generated by Django 3.2.20 on 2023-10-07 01:34

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


class Migration(migrations.Migration):

replaces = [
("analytics", "0022_uierrorlog"),
("analytics", "0023_alter_uierrorlog_user"),
("analytics", "0024_alter_uierrorlog_options"),
]

dependencies = [
("analytics", "0021_auto_20230203_1230"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("student", "0046_delete_registrationtoken"),
]

operations = [
migrations.CreateModel(
name="UIErrorLog",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("time_occurred", models.DateTimeField(auto_now_add=True)),
("message", models.CharField(max_length=1000)),
("name", models.CharField(max_length=100)),
("stack", models.TextField()),
("componentStack", models.TextField()),
(
"user",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"ordering": ["-time_occurred"],
"abstract": False,
},
),
]
30 changes: 30 additions & 0 deletions analytics/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from timetable.models import *
from student.models import Student
from django.contrib.auth.models import User


class SharedTimetable(Timetable):
Expand Down Expand Up @@ -112,3 +113,32 @@ class CalendarExport(models.Model):
time_created = models.DateTimeField(auto_now_add=True)
school = models.CharField(max_length=50)
is_google_calendar = models.BooleanField(blank=True, default=False)


class ErrorLog(models.Model):
"""
Logs errors that occur on the site.
"""

time_occurred = models.DateTimeField(auto_now_add=True)
message = models.CharField(max_length=1000)
name = models.CharField(max_length=100)
stack = models.TextField()
user = models.ForeignKey(
User, blank=True, null=True, on_delete=models.deletion.CASCADE
)

class Meta:
abstract = True
ordering = ["-time_occurred"]

def __str__(self) -> str:
return f"{self.name}: {self.message}"


class UIErrorLog(ErrorLog):
"""
UI errors that occur in the React components
"""

componentStack = models.TextField()
21 changes: 21 additions & 0 deletions analytics/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from rest_framework import serializers
from .models import UIErrorLog


class CurrentUserDefault(object):
def set_context(self, serializer_field):
self.user = serializer_field.context["request"].user

def __call__(self):
return self.user if self.user.is_authenticated else None

def __repr__(self):
return "%s()" % self.__class__.__name__


class UIErrorLogSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=CurrentUserDefault())

class Meta:
model = UIErrorLog
fields = "__all__"
43 changes: 43 additions & 0 deletions analytics/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

from django.contrib.auth.models import User
from rest_framework.test import APITestCase
from helpers.test.test_cases import UrlTestCase
from .models import UIErrorLog


class UrlsTest(UrlTestCase):
Expand All @@ -24,3 +27,43 @@ def test_urls_call_correct_views(self):
"/robots.txt", "analytics.views.view_analytics_dashboard"
)
self.assertUrlResolvesToView("/user/log_ical/", "student.views.log_ical_export")
self.assertUrlResolvesToView(
"/ui-error-logs/", "analytics.views.UIErrorLogCreateView"
)


class UIErrorLogCreateViewTest(APITestCase):
def setUp(self):
self.request_data = {
"name": "test name",
"message": "test message",
"stack": "test stack",
"componentStack": "test componentStack",
}
self.user = User.objects.create_user(
username="user", email="[email protected]", password="password"
)

def test_add_new_log(self):
response = self.client.post("/ui-error-logs/", self.request_data, format="json")
self.assertEqual(response.status_code, 201)

self.assertEqual(1, len(UIErrorLog.objects.all()))
created = UIErrorLog.objects.all()[0]
self.assert_attributes(created, None)

def test_add_new_log_logged_in(self):
self.client.force_login(self.user)
response = self.client.post("/ui-error-logs/", self.request_data, format="json")
self.assertEqual(response.status_code, 201)

self.assertEqual(1, len(UIErrorLog.objects.all()))
created = UIErrorLog.objects.all()[0]
self.assert_attributes(created, self.user)

def assert_attributes(self, created, user):
self.assertEqual(created.name, self.request_data["name"])
self.assertEqual(created.message, self.request_data["message"])
self.assertEqual(created.stack, self.request_data["stack"])
self.assertEqual(created.componentStack, self.request_data["componentStack"])
self.assertEqual(created.user, user)
1 change: 1 addition & 0 deletions analytics/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@
re_path(r"^analytics/*$", analytics.views.view_analytics_dashboard),
re_path(r"^robots.txt*$", analytics.views.view_analytics_dashboard),
re_path(r"^user/log_ical/*$", student.views.log_ical_export),
re_path(r"^ui-error-logs/*$", analytics.views.UIErrorLogCreateView.as_view()),
]
9 changes: 8 additions & 1 deletion analytics/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@
from django.views.decorators.csrf import csrf_exempt
from django.http import Http404
from django.db.models import Count

from rest_framework import generics
from rest_framework.response import Response
from student.models import Student

from student.utils import get_student
from student.models import *
from analytics.models import *
from analytics.serializers import UIErrorLogSerializer
from timetable.models import Semester
from parsing.schools.active import ACTIVE_SCHOOLS

Expand Down Expand Up @@ -240,3 +242,8 @@ def number_students_by_school():
)
result[school] = students.count()
return result


class UIErrorLogCreateView(generics.CreateAPIView):
serializer_class = UIErrorLogSerializer
queryset = UIErrorLog.objects.all()
2 changes: 1 addition & 1 deletion build/semesterly-base/requirements_base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ celery==5.2.2
coverage==4.4.1
cryptography==41.0.4
dateparser==1.1.0
Django==3.2.20
Django==3.2.23
django-cachalot==2.4.3
django-extensions==3.1.5
django-picklefield==3.0.1
Expand Down
1 change: 1 addition & 0 deletions courses/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

urlpatterns = [
re_path(r"^courses/?$", courses.views.all_courses),
re_path(r"^courses/json/?$", courses.views.all_courses_json),
re_path(r"c/(?P<code>.+?)/?$", courses.views.course_page),
re_path(
r"course/(?P<code>.+?)/(?P<sem_name>.+?)/(?P<year>.+?)/?$",
Expand Down
22 changes: 21 additions & 1 deletion courses/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import json
from datetime import datetime

from django.http import HttpResponse
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, get_object_or_404
from django.template import RequestContext
from pytz import timezone
Expand Down Expand Up @@ -61,6 +61,26 @@ def all_courses(request):
return render(request, "all_courses.html", context)


def all_courses_json(request):
"""
Returns all course names and codes in JSON.
"""
school = request.subdomain
courses = Course.objects.filter(school=school).all()

# Create a list of dictionaries with course name and code
courses_data = [
{
"name": course.name,
"code": course.code,
}
for course in courses
]

# Return as JSON
return JsonResponse(courses_data, safe=False)


def get_classmates_in_course(request, school, sem_name, year, course_id):
"""
Finds all classmates for the authenticated user who also have a
Expand Down
22 changes: 11 additions & 11 deletions docs/frontend.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,27 +122,27 @@ Modals
+-------------------------------+--------------------------------------------------+--------------------------+
| Component File | Screenshot | Description |
+===============================+==================================================+==========================+
|``course_modal_body.jsx`` | .. image:: components/course_modal_body.png | |
|``CourseModalBody.tsx`` | .. image:: components/course_modal_body.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``course_modal.jsx`` | .. image:: components/course_modal.png | |
|``CousreModal.tsx`` | .. image:: components/course_modal.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``AdvancedSearchModal.jsx`` | .. image:: components/exploration_modal.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``final_exams_modal.jsx`` | .. image:: components/final_exams_modal.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``peer_modal.jsx`` | .. image:: components/peer_modal.png | |
|``PeerModal.tsx`` | .. image:: components/peer_modal.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``preference_modal.jsx`` | .. image:: components/preference_modal.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``save_calendar_modal.jsx`` | .. image:: components/save_calendar_modal.png | |
|``SaveCalendarModal.tsx`` | .. image:: components/save_calendar_modal.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``signup_modal.jsx`` | .. image:: components/signup_modal.png | |
|``SignupModal.tsx`` | .. image:: components/signup_modal.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``tut_modal.jsx`` | .. image:: components/tut_modal.png | |
|``TutorialModal.tsx`` | .. image:: components/tut_modal.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``user_acquisition_modal.jsx`` | .. image:: components/user_acquisition_modal.png | |
|``UserAquisitionModal.tsx`` | .. image:: components/user_acquisition_modal.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``user_settings_modal.jsx`` | .. image:: components/user_settings_modal.png | |
|``UserSettingsModal.tsx`` | .. image:: components/user_settings_modal.png | |
+-------------------------------+--------------------------------------------------+--------------------------+

General Components
Expand All @@ -154,7 +154,7 @@ General Components
+-------------------------------+--------------------------------------------------+--------------------------+
|``Calendar.tsx`` | .. image:: components/calendar.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``course_modal_section.jsx`` | .. image:: components/course_modal_section.png | |
|``CourseModalSelection.tsx`` | .. image:: components/course_modal_section.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``CreditTicker.tsx`` | .. image:: components/credit_ticker.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
Expand Down Expand Up @@ -188,7 +188,7 @@ General Components
+-------------------------------+--------------------------------------------------+--------------------------+
|``SlotManager.tsx`` | .. image:: components/slot_manager.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``slot.jsx`` | .. image:: components/slot.png | |
|``Slot.tsx`` | .. image:: components/slot.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
|``social_profile.jsx`` | .. image:: components/social_profile.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
Expand All @@ -198,7 +198,7 @@ General Components
+-------------------------------+--------------------------------------------------+--------------------------+
| ``timetable_loader.jsx`` | .. image:: components/timetable_loader.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
| ``timetable_name_input.jsx`` | .. image:: components/timetable_name_input.png | |
| ``TimetableNameInput.tsx`` | .. image:: components/timetable_name_input.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
| ``TopBar.tsx`` | .. image:: components/top_bar.png | |
+-------------------------------+--------------------------------------------------+--------------------------+
Loading

0 comments on commit 18fe9fe

Please sign in to comment.