Skip to content

Commit

Permalink
Add mypy (#322)
Browse files Browse the repository at this point in the history
* 🌷 add mypy

* 🌌 fix mypy errors in portal

* πŸ‘₯user and sublet typing

* 🍽️ fix dining typing

* 🏘️ fix subletting tests typing

* πŸ“Š penndata typing errors

* πŸ“š gsr booking type fixes

* πŸ§Žβ€β™€οΈOMFG NO WAY NO MORE TYPE ERRORS

* 🧹 clean up types files

* πŸ’― fix tests

* πŸŒ‰ extend pre-commit rules
  • Loading branch information
ashleyzhang01 authored Nov 14, 2024
1 parent 848c72b commit 43daeaa
Show file tree
Hide file tree
Showing 138 changed files with 2,006 additions and 2,077 deletions.
27 changes: 26 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,34 @@ repos:
rev: v1.1-mobile-backend
hooks:
- id: black
args: [-l100]
args: [-l100, --config, backend/pyproject.toml]
- id: isort
args: []
- id: flake8
args: [--config, backend/setup.cfg]
- id: detect-private-key

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
additional_dependencies:
- types-requests
- types-python-dateutil
- django-stubs
- djangorestframework-stubs
- types-PyYAML
- types-redis
- types-pytz
files: ^backend/
exclude: ^backend/.*/migrations/.*$
args: [
--ignore-missing-imports,
--disallow-untyped-defs,
--check-untyped-defs,
--warn-redundant-casts,
--no-implicit-optional,
--strict-optional,
--warn-unused-ignores,
--disallow-incomplete-defs,
]
2 changes: 2 additions & 0 deletions backend/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ webdriver-manager = "*"
pre-commit = "*"
alt-profanity-check = "*"
inflection = "*"
types-redis = "*"
types-pytz = "*"

[requires]
python_version = "3.11"
397 changes: 214 additions & 183 deletions backend/Pipfile.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions backend/dining/api_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ def get_venues(self) -> list[dict[str, Any]]:
for key, value in venues.items():
# Cleaning up json response
venue = Venue.objects.filter(venue_id=key).first()
if venue is None:
continue

Check warning on line 69 in backend/dining/api_wrapper.py

View check run for this annotation

Codecov / codecov/patch

backend/dining/api_wrapper.py#L69

Added line #L69 was not covered by tests
value["name"] = venue.name
value["image"] = venue.image_url if venue else None

Expand Down
4 changes: 1 addition & 3 deletions backend/dining/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ class Migration(migrations.Migration):

initial = True

dependencies = [
("user", "0002_profile_laundry_preferences"),
]
dependencies = [("user", "0002_profile_laundry_preferences")]

operations = [
migrations.CreateModel(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@

class Migration(migrations.Migration):

dependencies = [
("dining", "0001_initial"),
]
dependencies = [("dining", "0001_initial")]

operations = [
migrations.CreateModel(
Expand Down
10 changes: 3 additions & 7 deletions backend/dining/migrations/0003_venue_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@

class Migration(migrations.Migration):

dependencies = [
("dining", "0002_diningitem_diningstation_diningmenu"),
]
dependencies = [("dining", "0002_diningitem_diningstation_diningmenu")]

operations = [
migrations.AddField(
model_name="venue",
name="name",
field=models.CharField(max_length=255, null=True),
),
model_name="venue", name="name", field=models.CharField(max_length=255, null=True)
)
]
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,16 @@

class Migration(migrations.Migration):

dependencies = [
("dining", "0003_venue_name"),
]
dependencies = [("dining", "0003_venue_name")]

operations = [
migrations.RemoveField(
model_name="diningtransaction",
name="profile",
),
migrations.RemoveField(model_name="diningtransaction", name="profile"),
migrations.AlterField(
model_name="diningitem",
name="description",
field=models.CharField(max_length=1000),
model_name="diningitem", name="description", field=models.CharField(max_length=1000)
),
migrations.AlterField(
model_name="diningitem",
name="ingredients",
field=models.CharField(max_length=1000),
),
migrations.DeleteModel(
name="DiningBalance",
),
migrations.DeleteModel(
name="DiningTransaction",
model_name="diningitem", name="ingredients", field=models.CharField(max_length=1000)
),
migrations.DeleteModel(name="DiningBalance"),
migrations.DeleteModel(name="DiningTransaction"),
]
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@

class Migration(migrations.Migration):

dependencies = [
("dining", "0004_remove_diningtransaction_profile_and_more"),
]
dependencies = [("dining", "0004_remove_diningtransaction_profile_and_more")]

operations = [
migrations.AddField(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,10 @@

class Migration(migrations.Migration):

dependencies = [
("dining", "0005_diningitem_allergens_diningitem_nutrition_info_and_more"),
]
dependencies = [("dining", "0005_diningitem_allergens_diningitem_nutrition_info_and_more")]

operations = [
migrations.RemoveField(
model_name="diningmenu",
name="stations",
),
migrations.RemoveField(model_name="diningmenu", name="stations"),
migrations.AlterField(
model_name="diningitem",
name="allergens",
Expand Down
44 changes: 25 additions & 19 deletions backend/dining/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,43 @@


class Venue(models.Model):
venue_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=255, null=True)
image_url = models.URLField()
venue_id: models.IntegerField = models.IntegerField(primary_key=True)
name: models.CharField = models.CharField(max_length=255, null=True)
image_url: models.URLField = models.URLField()

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


class DiningItem(models.Model):
item_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=255)
description = models.CharField(max_length=1000, blank=True)
ingredients = models.CharField(max_length=1000, blank=True) # comma separated list
allergens = models.CharField(max_length=1000, blank=True) # comma separated list
nutrition_info = models.CharField(max_length=1000, blank=True) # json string.
item_id: models.IntegerField = models.IntegerField(primary_key=True)
name: models.CharField = models.CharField(max_length=255)
description: models.CharField = models.CharField(max_length=1000, blank=True)
ingredients: models.CharField = models.CharField(
max_length=1000, blank=True
) # comma separated list
allergens: models.CharField = models.CharField(
max_length=1000, blank=True
) # comma separated list
nutrition_info: models.CharField = models.CharField(max_length=1000, blank=True) # json string.
# Technically, postgres supports json fields but that involves local postgres
# instead of sqlite AND we don't need to query on this field

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


class DiningStation(models.Model):
name = models.CharField(max_length=255)
items = models.ManyToManyField(DiningItem)
menu = models.ForeignKey("DiningMenu", on_delete=models.CASCADE, related_name="stations")
name: models.CharField = models.CharField(max_length=255)
items: models.ManyToManyField = models.ManyToManyField(DiningItem)
menu: models.ForeignKey = models.ForeignKey(
"DiningMenu", on_delete=models.CASCADE, related_name="stations"
)


class DiningMenu(models.Model):
venue = models.ForeignKey(Venue, on_delete=models.CASCADE)
date = models.DateField(default=timezone.now)
start_time = models.DateTimeField()
end_time = models.DateTimeField()
service = models.CharField(max_length=255)
venue: models.ForeignKey = models.ForeignKey(Venue, on_delete=models.CASCADE)
date: models.DateField = models.DateField(default=timezone.now)
start_time: models.DateTimeField = models.DateTimeField()
end_time: models.DateTimeField = models.DateTimeField()
service: models.CharField = models.CharField(max_length=255)
14 changes: 8 additions & 6 deletions backend/dining/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import datetime

from django.core.cache import cache
from django.db.models import Count, QuerySet
from django.db.models import Count, Manager, QuerySet
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.utils.timezone import make_aware
Expand All @@ -15,6 +15,7 @@
from dining.models import DiningMenu, Venue
from dining.serializers import DiningMenuSerializer
from utils.cache import Cache
from utils.types import get_user


d = DiningAPIWrapper()
Expand All @@ -40,7 +41,7 @@ class Menus(generics.ListAPIView):

serializer_class = DiningMenuSerializer

def get_queryset(self) -> QuerySet[DiningMenu]:
def get_queryset(self) -> QuerySet[DiningMenu, Manager[DiningMenu]]:
# TODO: We only have data for the next week, so we should 404
# if date_param is out of bounds
if date_param := self.kwargs.get("date"):
Expand All @@ -63,18 +64,19 @@ class Preferences(APIView):
key = "dining_preferences:{user_id}"

def get(self, request: Request) -> Response:
key = self.key.format(user_id=request.user.id)
key = self.key.format(user_id=get_user(request).id)
cached_preferences = cache.get(key)
if cached_preferences is None:
preferences = request.user.profile.dining_preferences
preferences = get_user(request).profile.dining_preferences
# aggregates venues and puts it in form {"venue_id": x, "count": x}
cached_preferences = preferences.values("venue_id").annotate(count=Count("venue_id"))
cache.set(key, cached_preferences, Cache.MONTH)
return Response({"preferences": cached_preferences})

def post(self, request: Request) -> Response:
key = self.key.format(user_id=request.user.id)
profile = request.user.profile
user = get_user(request)
key = self.key.format(user_id=user.id)
profile = user.profile
preferences = profile.dining_preferences

venue_ids = set(request.data["venues"])
Expand Down
16 changes: 10 additions & 6 deletions backend/gsr_booking/admin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from typing import Any, cast

from django.contrib import admin
from django.db.models import QuerySet
from rest_framework.request import Request
from django.db.models import Manager, QuerySet
from django.http import HttpRequest

from gsr_booking.models import GSR, Group, GroupMembership, GSRBooking, Reservation
from utils.types import UserType


class GroupMembershipInline(admin.TabularInline):
Expand All @@ -12,12 +15,13 @@ class GroupMembershipInline(admin.TabularInline):
readonly_fields = ["name"]

def name(self, obj: GroupMembership) -> str:
return obj.user.get_full_name()
user = cast(UserType, obj.user)
return str(user.get_full_name())

Check warning on line 19 in backend/gsr_booking/admin.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/admin.py#L18-L19

Added lines #L18 - L19 were not covered by tests

def get_fields(self, request, obj=None) -> list[str]:
def get_fields(self, request: HttpRequest, obj: Any = None) -> list[str]:
fields = super().get_fields(request, obj)
to_remove = ["user", "name"]
return ["name"] + [f for f in fields if f not in to_remove]
return ["name"] + [str(f) for f in fields if f not in to_remove]

Check warning on line 24 in backend/gsr_booking/admin.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/admin.py#L24

Added line #L24 was not covered by tests


class GroupAdmin(admin.ModelAdmin):
Expand All @@ -33,7 +37,7 @@ class GroupMembershipAdmin(admin.ModelAdmin):


class GSRAdmin(admin.ModelAdmin):
def get_queryset(self, request: Request) -> QuerySet[GSR]:
def get_queryset(self, request: HttpRequest) -> QuerySet[GSR, Manager[GSR]]:
return GSR.all_objects.all()

list_display = ["name", "kind", "lid", "gid", "in_use"]
Expand Down
Loading

0 comments on commit 43daeaa

Please sign in to comment.