Skip to content

Commit 434b944

Browse files
committed
chore: cleanup project names
1 parent 63e7638 commit 434b944

File tree

15 files changed

+162
-39
lines changed

15 files changed

+162
-39
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FROM python:3.13-slim-bookworm AS base
22
COPY --from=ghcr.io/astral-sh/uv:0.6.2 /uv /uvx /bin/
33

44
LABEL maintainer="TC Dev"
5-
LABEL org.opencontainers.image.source="https://github.com/capn-nepal/website-backend/"
5+
LABEL org.opencontainers.image.source="https://github.com/toggle-corp/rc-select-backend/"
66

77
ENV PYTHONUNBUFFERED=1
88

apps/common/graphql/dataloaders.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from django.db import models
2+
3+
4+
# -- Helper
5+
def load_model_objects[DjangoModel: models.Model](
6+
Model: type[DjangoModel],
7+
keys: list[int],
8+
) -> list[DjangoModel]:
9+
qs = Model.objects.filter(id__in=keys)
10+
_map = {obj.pk: obj for obj in qs}
11+
return [_map[key] for key in keys]

apps/tool_picker/admin.py

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
OrdinalTypeEnum,
1111
Question,
1212
QuestionTypeEnum,
13+
RecommendationResult,
1314
Tool,
1415
ToolAnswer,
16+
UserAnswer,
17+
UserSubmission,
1518
)
1619

1720

@@ -244,9 +247,12 @@ class ToolAdmin(UserResourceAdmin, admin.ModelAdmin): # type: ignore[reportMiss
244247
"Basic Information",
245248
{
246249
"fields": (
247-
"catalog", "name",
248-
"tagline", "description",
249-
"video_link", "tool_link",
250+
"catalog",
251+
"name",
252+
"tagline",
253+
"description",
254+
"video_link",
255+
"tool_link",
250256
"logo",
251257
),
252258
},
@@ -278,3 +284,113 @@ def save_model(self, request, obj, form, change): # type: ignore[reportMissingT
278284
else None,
279285
},
280286
)
287+
288+
289+
# ============================================================================
290+
# User Submission Models Admin
291+
# ============================================================================
292+
293+
294+
class UserAnswerInline(admin.TabularInline): # type: ignore[reportMissingTypeArgument]
295+
model = UserAnswer
296+
extra = 0
297+
fields = ["question", "ordinal_value", "get_selected_options"]
298+
readonly_fields = ["get_selected_options"]
299+
ordering = ["question__order"]
300+
301+
@admin.display(description="Checkbox Options")
302+
def get_selected_options(self, obj: UserAnswer):
303+
if obj.pk and obj.question.question_type == "checkbox":
304+
return ", ".join([opt.text for opt in obj.selected_options.all()])
305+
return "-"
306+
307+
308+
class RecommendationResultInline(admin.TabularInline): # type: ignore[reportMissingTypeArgument]
309+
model = RecommendationResult
310+
extra = 0
311+
fields = ["rank", "tool", "score"]
312+
ordering = ["rank"]
313+
314+
315+
@admin.register(UserSubmission)
316+
class UserSubmissionAdmin(admin.ModelAdmin): # type: ignore[reportMissingTypeArgument]
317+
list_display = ["id", "catalog", "created_at", "answer_count", "recommendation_count"]
318+
list_filter = ["catalog", "created_at"]
319+
readonly_fields = ["id", "created_at"]
320+
fields = ["id", "catalog"]
321+
inlines = [UserAnswerInline, RecommendationResultInline]
322+
323+
@admin.display(description="Answers")
324+
def answer_count(self, obj: UserSubmission):
325+
return obj.answers.count()
326+
327+
@admin.display(description="Results")
328+
def recommendation_count(self, obj: UserSubmission):
329+
return obj.results.count()
330+
331+
332+
@admin.register(UserAnswer)
333+
class UserAnswerAdmin(admin.ModelAdmin): # type: ignore[reportMissingTypeArgument]
334+
list_display = ["submission_id", "question", "question_type", "get_answer"]
335+
list_filter = ["submission__catalog", "question__question_type"]
336+
search_fields = ["submission__id", "question__title"]
337+
338+
def get_fields(self, request, obj=None): # type: ignore[reportMissingTypeArgument]
339+
"""Show only relevant fields based on question type"""
340+
base_fields = ["submission", "question"]
341+
if obj and obj.question.question_type == "ordinal":
342+
return base_fields + ["ordinal_value"]
343+
if obj and obj.question.question_type == "checkbox":
344+
return base_fields + ["selected_options"]
345+
return base_fields + ["ordinal_value", "selected_options"]
346+
347+
def get_form(self, request, obj=None, **kwargs): # type: ignore[reportMissingTypeArgument]
348+
form = super().get_form(request, obj, **kwargs)
349+
if obj and obj.question.question_type == "checkbox":
350+
self.filter_horizontal = ["selected_options"]
351+
else:
352+
self.filter_horizontal = []
353+
return form
354+
355+
@admin.display(description="Submission")
356+
def submission_id(self, obj: UserAnswer):
357+
return str(obj.submission.id)[:8] + "..."
358+
359+
@admin.display(description="Type")
360+
def question_type(self, obj: UserAnswer):
361+
return obj.question.get_question_type_display()
362+
363+
@admin.display(description="Answer")
364+
def get_answer(self, obj: UserAnswer):
365+
if obj.question.question_type == "ordinal":
366+
return obj.ordinal_value or "-"
367+
options = obj.selected_options.all()
368+
return ", ".join([opt.text for opt in options]) if options else "(none)"
369+
370+
def formfield_for_manytomany(self, db_field, request, **kwargs): # type: ignore[reportMissingTypeArgument]
371+
if db_field.name == "selected_options":
372+
user_answer_id = request.resolver_match.kwargs.get("object_id") # type: ignore[reportOptionsArgumentAccess]
373+
if user_answer_id:
374+
try:
375+
user_answer = UserAnswer.objects.get(pk=user_answer_id)
376+
kwargs["queryset"] = CheckboxOption.objects.filter(question=user_answer.question)
377+
except UserAnswer.DoesNotExist:
378+
pass
379+
return super().formfield_for_manytomany(db_field, request, **kwargs)
380+
381+
382+
@admin.register(RecommendationResult)
383+
class RecommendationResultAdmin(admin.ModelAdmin): # type: ignore[reportMissingTypeArgument]
384+
list_display = ["submission_short", "rank", "tool", "score", "catalog"]
385+
list_filter = ["submission__catalog", "rank"]
386+
search_fields = ["submission__id", "tool__name"]
387+
ordering = ["submission", "rank"]
388+
fields = ["submission", "tool", "rank", "score"]
389+
390+
@admin.display(description="Submission")
391+
def submission_short(self, obj: RecommendationResult):
392+
return str(obj.submission.id)[:8] + "..."
393+
394+
@admin.display(description="Catalog")
395+
def catalog(self, obj: RecommendationResult):
396+
return obj.submission.catalog.name

apps/tool_picker/models.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import typing
22
import uuid
33

4-
from django.core.validators import MinValueValidator, MaxValueValidator
4+
from django.core.validators import MaxValueValidator, MinValueValidator
55
from django.db import models
66
from django_choices_field import IntegerChoicesField
77
from django_stubs_ext.db.models.manager import RelatedManager
@@ -58,6 +58,7 @@ class Question(UserResource):
5858

5959
# type hints
6060
options: typing.ClassVar[RelatedManager["CheckboxOption"]]
61+
get_question_type_display: typing.ClassVar[typing.Callable[[typing.Self], str]]
6162

6263
class Meta(UserResource.Meta):
6364
ordering = ["catalog", "order"]
@@ -255,10 +256,10 @@ class RecommendationResult(models.Model):
255256
related_name="recommendations",
256257
)
257258
rank = models.IntegerField[int, int](
258-
validators=[MinValueValidator(1), MaxValueValidator(5)]
259+
validators=[MinValueValidator(1), MaxValueValidator(5)],
259260
)
260261
score = models.FloatField[float, float](
261-
help_text="Proximity score - lower is better (closer match)"
262+
help_text="Proximity score - lower is better (closer match)",
262263
)
263264

264265
class Meta(UserResource.Meta):

apps/user/factories.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# pyright: reportRedeclaration=false
22
# pyright: reportIncompatibleVariableOverride=false
33
# pyright: reportMissingTypeArgument=false
4+
# pyright: reportPrivateImportUsage=false
45
import typing
56

67
import factory

apps/user/models.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88

99

1010
class User(AbstractUser):
11-
"""Custom user model with email as unique identifier.
12-
"""
11+
"""Custom user model with email as unique identifier."""
1312

1413
EMAIL_FIELD = USERNAME_FIELD = "email"
1514
REQUIRED_FIELDS = []
@@ -29,7 +28,6 @@ def anonymized_email(self):
2928
email_name_first_char, email_name_last_char = email_name[:1], email_name[-1:]
3029
return f"{email_name_first_char}***{email_name_last_char}@{email_domain}"
3130

32-
3331
@typing.override
3432
def save(self, *args, **kwargs): # type: ignore[reportMissingParameterType]
3533
# Make sure email are same and lowercase

apps/user/tests.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
from django.test import TestCase
2-
31
# Create your tests here.

apps/user/views.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
from django.shortcuts import render
2-
31
# Create your views here.

docker-compose-prod.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: nrcs-backend
1+
name: rc-select-backend
22

33
services:
44
web:

docker-compose.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ x-server: &base_server_setup
1616
SESSION_COOKIE_DOMAIN: ${SESSION_COOKIE_DOMAIN:-localhost}
1717
CSRF_COOKIE_DOMAIN: ${CSRF_COOKIE_DOMAIN:-localhost}
1818
# Postgres
19-
POSTGRES_DB: ${POSTGRES_DB:-nrcs}
19+
POSTGRES_DB: ${POSTGRES_DB:-rc-select}
2020
POSTGRES_USER: ${POSTGRES_USER:-postgres}
2121
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
2222
POSTGRES_HOST: ${POSTGRES_HOST:-db}
@@ -36,7 +36,7 @@ services:
3636
db:
3737
image: postgres:17
3838
environment:
39-
POSTGRES_DB: nrcs
39+
POSTGRES_DB: rc-select
4040
POSTGRES_USER: postgres
4141
POSTGRES_PASSWORD: postgres
4242
volumes:

0 commit comments

Comments
 (0)