Skip to content

Commit

Permalink
🌌 fix mypy errors in portal
Browse files Browse the repository at this point in the history
  • Loading branch information
ashleyzhang01 committed Nov 11, 2024
1 parent f699b8b commit 0c4bedc
Show file tree
Hide file tree
Showing 13 changed files with 426 additions and 324 deletions.
12 changes: 8 additions & 4 deletions backend/pennmobile/urls.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from typing import List, Union

from django.conf import settings
from django.contrib import admin
from django.urls import include, path
from django.urls import URLPattern, URLResolver, include, path
from django.views.generic import TemplateView
from rest_framework.schemas import get_schema_view


urlpatterns = [
URLPatternList = List[Union[URLPattern, URLResolver]]

urlpatterns: URLPatternList = [
path("gsr/", include("gsr_booking.urls")),
path("portal/", include("portal.urls")),
path("admin/", admin.site.urls),
Expand All @@ -29,12 +33,12 @@
path("sublet/", include("sublet.urls")),
]

urlpatterns = [
urlpatterns: URLPatternList = [
path("api/", include(urlpatterns)),
path("", include((urlpatterns, "apex"))),
]

if settings.DEBUG:
import debug_toolbar

urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns
urlpatterns: URLPatternList = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns
32 changes: 19 additions & 13 deletions backend/portal/admin.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,57 @@
from typing import Any

from django.contrib import admin
from django.utils.html import escape, mark_safe
from django.db.models import QuerySet
from django.utils.html import escape
from django.utils.safestring import SafeString, mark_safe

from portal.models import Content, Poll, PollOption, PollVote, Post, TargetPopulation


class ContentAdmin(admin.ModelAdmin):
@admin.action(description="Set status to Approved")
def action_approved(modeladmin, request, queryset):
def action_approved(modeladmin: Any, request: Any, queryset: QuerySet) -> None:
queryset.update(status=Content.STATUS_APPROVED)

@admin.action(description="Set status to Draft")
def action_draft(modeladmin, request, queryset):
def action_draft(modeladmin: Any, request: Any, queryset: QuerySet) -> None:
queryset.update(status=Content.STATUS_DRAFT)

@admin.action(description="Set status to Revision")
def action_revision(modeladmin, request, queryset):
def action_revision(modeladmin: Any, request: Any, queryset: QuerySet) -> None:
queryset.update(status=Content.STATUS_REVISION)

actions = [action_approved, action_draft, action_revision]

def get_queryset(self, request):
def get_queryset(self, request: Any) -> QuerySet:
queryset = super().get_queryset(request)
return queryset.annotate(ar=Content.ACTION_REQUIRED_CONDITION).order_by(
"-ar", "-created_date"
)

def ar(self, obj):
return obj.ar
# Using any for the ar property since it comes from a queryset annotation
def ar(self, obj: Any) -> bool:
return bool(obj.ar)

ar.boolean = True
ar.boolean = True # type: ignore[attr-defined]


class PostAdmin(ContentAdmin):
def image_tag(instance, height):
@staticmethod
def image_tag(instance: Post, height: int) -> SafeString:
return mark_safe(
f'<img src="%s" height="{height}" />' % escape(instance.image and instance.image.url)
)

def small_image(self, instance):
def small_image(self, instance: Post) -> SafeString:
return PostAdmin.image_tag(instance, 100)

small_image.short_description = "Post Image"
small_image.short_description = "Post Image" # type: ignore[attr-defined]

def large_image(self, instance):
def large_image(self, instance: Post) -> SafeString:
return PostAdmin.image_tag(instance, 300)

large_image.short_description = "Post Image"
large_image.short_description = "Post Image" # type: ignore[attr-defined]

readonly_fields = ("large_image",)
list_display = (
Expand Down
58 changes: 25 additions & 33 deletions backend/portal/logic.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,24 @@
import json
from collections import defaultdict
from typing import TYPE_CHECKING, Any
from typing import Any, Optional

from accounts.ipc import authenticated_request
from django.contrib.auth import get_user_model
from rest_framework.exceptions import PermissionDenied

from portal.models import Poll, PollOption, PollVote, TargetPopulation
from portal.types import PopulationGroups, PopulationList
from utils.types import DjangoUserType


if TYPE_CHECKING:
from django.contrib.auth.models import AbstractUser

UserType = AbstractUser
else:
UserType = Any

User = get_user_model()


def get_user_info(user: "UserType") -> dict[str, Any]:
def get_user_info(user: DjangoUserType) -> dict[str, Any]:
"""Returns Platform user information"""
response = authenticated_request(user, "GET", "https://platform.pennlabs.org/accounts/me/")
if response.status_code == 403:
raise PermissionDenied("IPC request failed")
return json.loads(response.content)


def get_user_clubs(user: "UserType") -> list[dict[str, Any]]:
def get_user_clubs(user: DjangoUserType) -> list[dict[str, Any]]:
"""Returns list of clubs that user is a member of"""
response = authenticated_request(user, "GET", "https://pennclubs.com/api/memberships/")
if response.status_code == 403:
Expand All @@ -36,7 +27,7 @@ def get_user_clubs(user: "UserType") -> list[dict[str, Any]]:
return res_json


def get_club_info(user: "UserType", club_code: str) -> dict[str, Any]:
def get_club_info(user: DjangoUserType, club_code: str) -> dict[str, Any]:
"""Returns club information based on club code"""
response = authenticated_request(user, "GET", f"https://pennclubs.com/api/clubs/{club_code}/")
if response.status_code == 403:
Expand All @@ -45,12 +36,12 @@ def get_club_info(user: "UserType", club_code: str) -> dict[str, Any]:
return {"name": res_json["name"], "image": res_json["image_url"], "club_code": club_code}


def get_user_populations(user: "UserType") -> list[TargetPopulation]:
def get_user_populations(user: DjangoUserType) -> PopulationGroups:
"""Returns the target populations that the user belongs to"""

user_info = get_user_info(user)

year = (
year: PopulationList = (
[
TargetPopulation.objects.get(
kind=TargetPopulation.KIND_YEAR, population=user_info["student"]["graduation_year"]
Expand All @@ -60,7 +51,7 @@ def get_user_populations(user: "UserType") -> list[TargetPopulation]:
else []
)

school = (
school: PopulationList = (
[
TargetPopulation.objects.get(kind=TargetPopulation.KIND_SCHOOL, population=x["name"])
for x in user_info["student"]["school"]
Expand All @@ -69,7 +60,7 @@ def get_user_populations(user: "UserType") -> list[TargetPopulation]:
else []
)

major = (
major: PopulationList = (
[
TargetPopulation.objects.get(kind=TargetPopulation.KIND_MAJOR, population=x["name"])
for x in user_info["student"]["major"]
Expand All @@ -78,7 +69,7 @@ def get_user_populations(user: "UserType") -> list[TargetPopulation]:
else []
)

degree = (
degree: PopulationList = (
[
TargetPopulation.objects.get(
kind=TargetPopulation.KIND_DEGREE, population=x["degree_type"]
Expand All @@ -92,29 +83,30 @@ def get_user_populations(user: "UserType") -> list[TargetPopulation]:
return [year, school, major, degree]


def check_targets(obj: Poll, user: "UserType") -> bool:
def check_targets(obj: Poll, user: DjangoUserType) -> bool:
"""
Check if user aligns with target populations of poll or post
"""

populations = get_user_populations(user)
population_groups = get_user_populations(user)

year = set(obj.target_populations.filter(kind=TargetPopulation.KIND_YEAR))
school = set(obj.target_populations.filter(kind=TargetPopulation.KIND_SCHOOL))
major = set(obj.target_populations.filter(kind=TargetPopulation.KIND_MAJOR))
degree = set(obj.target_populations.filter(kind=TargetPopulation.KIND_DEGREE))
year_targets = set(obj.target_populations.filter(kind=TargetPopulation.KIND_YEAR))
school_targets = set(obj.target_populations.filter(kind=TargetPopulation.KIND_SCHOOL))
major_targets = set(obj.target_populations.filter(kind=TargetPopulation.KIND_MAJOR))
degree_targets = set(obj.target_populations.filter(kind=TargetPopulation.KIND_DEGREE))

return (
set(populations[0]).issubset(year)
and set(populations[1]).issubset(school)
and set(populations[2]).issubset(major)
and set(populations[3]).issubset(degree)
return all(
set(group).issubset(targets)
for group, targets in zip(
population_groups, [year_targets, school_targets, major_targets, degree_targets]
)
)


def get_demographic_breakdown(poll_id: int) -> list[dict[str, Any]]:
def get_demographic_breakdown(poll_id: Optional[int] = None) -> list[dict[str, Any]]:
"""Collects Poll statistics on school and graduation year demographics"""

if poll_id is None:
raise ValueError("poll_id is required")
# passing in id is necessary because
# poll info is already serialized
poll = Poll.objects.get(id=poll_id)
Expand Down
75 changes: 21 additions & 54 deletions backend/portal/management/commands/polls_populate.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
import datetime
from typing import Any

from django.contrib.auth import get_user_model
from django.core.management import call_command
from django.core.management.base import BaseCommand
from django.utils import timezone

from portal.models import Poll, PollOption, PollVote, TargetPopulation
from user.models import Profile


User = get_user_model()
from utils.types import DjangoUserModel, DjangoUserType


class Command(BaseCommand):
def handle(self, *args, **kwargs):
def _create_user(
self, username: str, email: str, password: str, graduation_date: datetime.date
) -> DjangoUserType:
"""Helper to create a user with profile"""
if not DjangoUserModel.objects.filter(username=username).exists():
user = DjangoUserModel.objects.create_user(username, email, password)
profile = Profile.objects.get(user=user)
setattr(profile, "expected_graduation", graduation_date)
profile.save()
return user
return DjangoUserModel.objects.get(username=username)

def handle(self, *args: Any, **kwargs: Any) -> None:

# Define graduation years
df_2022 = datetime.date(2022, 5, 15)
Expand All @@ -22,55 +32,12 @@ def handle(self, *args, **kwargs):
df_2025 = datetime.date(2025, 5, 17)

# Create users and set graduation years
if not User.objects.filter(username="user1").first():
user1 = User.objects.create_user("user1", "[email protected]", "user")
user1_profile = Profile.objects.get(user=user1)
user1_profile.expected_graduation = df_2022
user1_profile.save()
else:
user1 = User.objects.get(username="user1")

if not User.objects.filter(username="user2").first():
user2 = User.objects.create_user("user2", "[email protected]", "user2")
user2_profile = Profile.objects.get(user=user2)
user2_profile.expected_graduation = df_2023
user2_profile.save()
else:
user2 = User.objects.get(username="user2")

if not User.objects.filter(username="user3").first():
user3 = User.objects.create_user("user3", "[email protected]", "user3")
user3_profile = Profile.objects.get(user=user3)
user3_profile.expected_graduation = df_2024
user3_profile.save()
else:
user3 = User.objects.get(username="user3")

if not User.objects.filter(username="user_cas").first():
user_cas = User.objects.create_user("user_cas", "[email protected]", "user_cas")
user_cas_profile = Profile.objects.get(user=user_cas)
user_cas_profile.expected_graduation = df_2025
user_cas_profile.save()
else:
user_cas = User.objects.get(username="user_cas")

if not User.objects.filter(username="user_wh").first():
user_wh = User.objects.create_user("user_wh", "[email protected]", "user_wh")
user_wh_profile = Profile.objects.get(user=user_wh)
user_wh_profile.expected_graduation = df_2024
user_wh_profile.save()
else:
user_wh = User.objects.get(username="user_wh")

if not User.objects.filter(username="user_nursing").first():
user_nursing = User.objects.create_user(
"user_nursing", "[email protected]", "user_nursing"
)
user_nursing_profile = Profile.objects.get(user=user_nursing)
user_nursing_profile.expected_graduation = df_2023
user_nursing_profile.save()
else:
user_nursing = User.objects.get(username="user_nursing")
self._create_user("user1", "[email protected]", "user", df_2022)
self._create_user("user2", "[email protected]", "user2", df_2023)
self._create_user("user3", "[email protected]", "user3", df_2024)
self._create_user("user_cas", "[email protected]", "user_cas", df_2025)
self._create_user("user_wh", "[email protected]", "user_wh", df_2024)
self._create_user("user_nursing", "[email protected]", "user_nursing", df_2023)

# Create target populations
call_command("load_target_populations", "--years", "2022, 2023, 2024, 2025")
Expand Down
Loading

0 comments on commit 0c4bedc

Please sign in to comment.