Skip to content

Commit

Permalink
Merge branch 'master' into version-upgrades
Browse files Browse the repository at this point in the history
  • Loading branch information
rm03 committed Feb 12, 2024
2 parents eec2172 + 3b49bcd commit 74d67e1
Show file tree
Hide file tree
Showing 17 changed files with 168 additions and 151 deletions.
4 changes: 2 additions & 2 deletions backend/clubs/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,8 @@ class ZoomMeetingVisitAdmin(admin.ModelAdmin):


class ApplicationSubmissionAdmin(admin.ModelAdmin):
list_display = ("user", "id", "created_at", "status", "archived")
list_filter = ("archived",)
search_fields = ("user__username",)
list_display = ("user", "id", "created_at", "status")


admin.site.register(Asset)
Expand Down
36 changes: 36 additions & 0 deletions backend/clubs/management/commands/update_club_counts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from django.core.management.base import BaseCommand
from django.db.models import Count, Q

from clubs.models import Club


class Command(BaseCommand):
help = "Update stored favorite and membership counts."

def handle(self, *args, **kwargs):
try:
queryset = Club.objects.all().annotate(
temp_favorite_count=Count("favorite", distinct=True),
temp_membership_count=Count(
"membership", distinct=True, filter=Q(active=True)
),
)

for club in queryset:
club.favorite_count = club.temp_favorite_count
club.membership_count = club.temp_membership_count
Club.objects.bulk_update(queryset, ["favorite_count", "membership_count"])

self.stdout.write(
self.style.SUCCESS(
"Successfully updated all club favorite and membership counts!"
)
)
except Exception as e:
self.stdout.write(
self.style.ERROR(
"An error was encountered while updating"
+ "club favorite and membership counts!"
)
)
self.stdout.write(e)
34 changes: 34 additions & 0 deletions backend/clubs/migrations/0095_rm_field_add_count.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 3.2.18 on 2024-02-03 22:21

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("clubs", "0094_applicationcycle_release_date"),
]

operations = [
migrations.RemoveField(model_name="applicationsubmission", name="archived",),
migrations.AddField(
model_name="club",
name="favorite_count",
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name="club",
name="membership_count",
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name="historicalclub",
name="favorite_count",
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name="historicalclub",
name="membership_count",
field=models.IntegerField(default=0),
),
]
8 changes: 7 additions & 1 deletion backend/clubs/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,13 @@ class Club(models.Model):
appointment_needed = models.BooleanField(default=False)
signature_events = models.TextField(blank=True) # html

# cache club aggregation counts
favorite_count = models.IntegerField(default=0)
membership_count = models.IntegerField(default=0)

# cache club rankings
rank = models.IntegerField(default=0)

# cache club rankings
rank = models.IntegerField(default=0)

Expand Down Expand Up @@ -1727,7 +1734,6 @@ class ApplicationSubmission(models.Model):
on_delete=models.SET_NULL,
null=True,
)
archived = models.BooleanField(default=False)
notified = models.BooleanField(default=False)

created_at = models.DateTimeField(auto_now_add=True)
Expand Down
12 changes: 8 additions & 4 deletions backend/clubs/serializers.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import datetime
import json
import re
from collections import OrderedDict
from urllib.parse import parse_qs, urlparse

import bleach
import pytz
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.cache import cache
Expand Down Expand Up @@ -2751,6 +2749,12 @@ def get_image_url(self, obj):
def validate(self, data):
acceptance_template = data.get("acceptance_email", "")
rejection_template = data.get("rejection_email", "")
request = self.context["request"].data

if "committees" in request and data["application_start_time"] < timezone.now():
raise serializers.ValidationError(
"You cannot edit committees once the application is open"
)

if not ClubApplication.validate_template(
acceptance_template
Expand Down Expand Up @@ -2791,7 +2795,7 @@ def save(self):
request = self.context["request"].data

# only allow modifications to committees if the application is not yet open
now = pytz.timezone("America/New_York").localize(datetime.datetime.now())
now = timezone.now()
if "committees" in request and application_obj.application_start_time > now:
committees = map(
lambda x: x["value"] if "value" in x else x["name"],
Expand All @@ -2801,11 +2805,11 @@ def save(self):
application=application_obj
)
# nasty hack for idempotency
prev_committee_names = prev_committees.values("name")
for prev_committee in prev_committees:
if prev_committee.name not in committees:
prev_committee.delete()

prev_committee_names = prev_committees.values("name")
for name in committees:
if name not in prev_committee_names:
ApplicationCommittee.objects.create(
Expand Down
46 changes: 6 additions & 40 deletions backend/clubs/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1014,13 +1014,7 @@ class ClubViewSet(XLSXFormatterMixin, viewsets.ModelViewSet):
"""

queryset = (
Club.objects.all()
.annotate(
favorite_count=Count("favorite", distinct=True),
membership_count=Count("membership", distinct=True, filter=Q(active=True)),
)
.prefetch_related("tags")
.order_by("-favorite_count", "name")
Club.objects.all().prefetch_related("tags").order_by("-favorite_count", "name")
)
permission_classes = [ClubPermission | IsSuperuser]
filter_backends = [filters.SearchFilter, ClubsSearchFilter, ClubsOrderingFilter]
Expand Down Expand Up @@ -4460,7 +4454,6 @@ def question_response(self, *args, **kwargs):
user=self.request.user,
committee__isnull=False,
application=application,
archived=False,
)
.values_list("committee__name", flat=True)
.distinct()
Expand Down Expand Up @@ -4498,7 +4491,6 @@ def question_response(self, *args, **kwargs):
user=self.request.user,
application=application,
committee=committee,
archived=False,
)

key = f"applicationsubmissions:{application.id}"
Expand Down Expand Up @@ -4609,7 +4601,6 @@ def questions(self, *args, **kwargs):
ApplicationQuestionResponse.objects.filter(
question=question,
submission__user=self.request.user,
submission__archived=False,
)
.select_related("submission", "multiple_choice", "question")
.prefetch_related("question__committees", "question__multiple_choice")
Expand Down Expand Up @@ -4722,8 +4713,7 @@ def send_emails(self, *args, **kwargs):

# Query for recent submissions with user and committee joined
submissions = ApplicationSubmission.objects.filter(
application=app,
archived=False,
application=app
).select_related("user", "committee")

dry_run = self.request.data.get("dry_run")
Expand Down Expand Up @@ -5190,10 +5180,7 @@ def get_operation_id(self, **kwargs):

def get_queryset(self):
return (
ApplicationSubmission.objects.filter(
application__is_wharton_council=True,
archived=False,
)
ApplicationSubmission.objects.filter(application__is_wharton_council=True)
.annotate(
annotated_name=F("application__name"),
annotated_committee=F("committee__name"),
Expand Down Expand Up @@ -5235,10 +5222,7 @@ class ApplicationSubmissionViewSet(viewsets.ModelViewSet):
def get_queryset(self):
app_id = self.kwargs["application_pk"]
submissions = (
ApplicationSubmission.objects.filter(
application=app_id,
archived=False,
)
ApplicationSubmission.objects.filter(application=app_id)
.select_related("user__profile", "committee", "application__club")
.prefetch_related(
Prefetch(
Expand Down Expand Up @@ -5303,10 +5287,7 @@ def export(self, *args, **kwargs):
"""
app_id = int(self.kwargs["application_pk"])
data = (
ApplicationSubmission.objects.filter(
application=app_id,
archived=False,
)
ApplicationSubmission.objects.filter(application=app_id)
.select_related("user__profile", "committee", "application__club")
.prefetch_related(
Prefetch(
Expand Down Expand Up @@ -5351,7 +5332,6 @@ def exportall(self, *args, **kwargs):
ApplicationSubmission.objects.filter(
application__is_wharton_council=True,
application__application_cycle=cycle,
archived=False,
)
.select_related("application", "application__application_cycle")
.annotate(
Expand Down Expand Up @@ -5504,10 +5484,7 @@ class ApplicationSubmissionUserViewSet(viewsets.ModelViewSet):

def get_queryset(self):
submissions = (
ApplicationSubmission.objects.filter(
user=self.request.user,
archived=False,
)
ApplicationSubmission.objects.filter(user=self.request.user)
.select_related("user__profile", "committee", "application__club")
.prefetch_related(
Prefetch(
Expand All @@ -5522,17 +5499,6 @@ def get_queryset(self):
)
return submissions

def perform_destroy(self, instance):
"""
Set archived boolean to be True so that the submissions
appears to have been deleted
"""

instance.archived = True
instance.archived_by = self.request.user
instance.archived_on = timezone.now()
instance.save()


class ApplicationQuestionViewSet(viewsets.ModelViewSet):
"""
Expand Down
7 changes: 6 additions & 1 deletion backend/pennclubs/settings/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": f"redis://{REDIS_HOST}:6379/1",
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"IGNORE_EXCEPTIONS": True, # ignore Redis connection errors
"SOCKET_CONNECT_TIMEOUT": 1,
"SOCKET_TIMEOUT": 1,
},
"KEY_PREFIX": "django",
}
}
8 changes: 4 additions & 4 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ exclude = [
".venv",
"migrations",
]
ignore = ["E203"]
lint.ignore = ["E203"]
line-length = 88
select = [
lint.select = [
"E",
"F",
"Q",
"W",
"I",
]

[tool.ruff.flake8-quotes]
[tool.ruff.lint.flake8-quotes]
inline-quotes = "double"

[tool.ruff.isort]
[tool.ruff.lint.isort]
known-first-party = ["pennclubs", "clubs"]
lines-after-imports = 2
3 changes: 1 addition & 2 deletions frontend/components/ClubPage/Actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,6 @@ const Actions = ({
<div className={className} style={style}>
<Wrapper>
{SHOW_MEMBERSHIP_REQUEST &&
!inClub &&
club.members.length > 0 &&
isMembershipOpen &&
club.accepting_members && (
Expand All @@ -293,7 +292,7 @@ const Actions = ({
updateRequests={updateRequests}
/>
)}
{SHOW_APPLICATIONS && (
{SHOW_APPLICATIONS && !isMembershipOpen && club.accepting_members && (
<Link
href={CLUB_APPLY_ROUTE()}
as={CLUB_APPLY_ROUTE(code)}
Expand Down
15 changes: 15 additions & 0 deletions frontend/components/Settings/WhartonApplicationStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,18 @@ const WhartonApplicationStatus = ({
ApplicationStatus[] | { detail: string } | null
>(initialStatuses ?? null)

function downloadData(statuses) {
const dataStr =
'data:text/json;charset=utf-8,' +
encodeURIComponent(JSON.stringify(statuses, null, 2))
const downloadAnchorNode = document.createElement('a')
downloadAnchorNode.setAttribute('href', dataStr)
downloadAnchorNode.setAttribute('download', 'applicationStatuses.json')
document.body.appendChild(downloadAnchorNode)
downloadAnchorNode.click()
downloadAnchorNode.remove()
}

if (statuses == null) {
return <Loading />
}
Expand All @@ -187,6 +199,9 @@ const WhartonApplicationStatus = ({
registered {OBJECT_NAME_PLURAL} for an {FAIR_NAME} fair. Only users with
the required permissions can view this page.
</Text>
<button className="button" onClick={() => downloadData(statuses)}>
Download Data
</button>
<div className="column">
<DiscreteColorLegend
items={Object.keys(colors).map((label) => ({
Expand Down
18 changes: 12 additions & 6 deletions frontend/components/Submissions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,18 @@ function SubmissionsPage({
<button
onClick={(e) => {
e.stopPropagation()
doApiRequest(`/submissions/${submission.pk}/?format=json`, {
method: 'DELETE',
})
setSubmissions(
submissions.filter((submission) => submission.pk !== id),
)
if (
confirm(
'Are you sure you want to permanently delete this submission?',
)
) {
doApiRequest(`/submissions/${submission.pk}/?format=json`, {
method: 'DELETE',
})
setSubmissions(
submissions.filter((submission) => submission.pk !== id),
)
}
}}
className="button is-danger is-small ml-3"
>
Expand Down
2 changes: 1 addition & 1 deletion frontend/utils/branding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const sites = {
// enable showing members for each club
SHOW_MEMBERS: false,
// enable the membership request feature
SHOW_MEMBERSHIP_REQUEST: false,
SHOW_MEMBERSHIP_REQUEST: true,
// show the links to the ranking algorithm from various parts of the site
SHOW_RANK_ALGORITHM: true,
// show the link to the Penn accessibility help page at the bottom of each page
Expand Down
Loading

0 comments on commit 74d67e1

Please sign in to comment.