pronouns: per-user pronouns ideology + Pronouns applet on Game Kit; provenance prose uses actor.pronouns at render time — TDD
- User.pronouns CharField w. choices=[pluralism (default), bawlmorese, misogyny, misandry, misanthropy] + pronoun_subj/obj/poss properties; PRONOUN_TABLE single source of truth in apps.lyric.models; mig 0002_user_pronouns
- drama.GameEvent.to_prose() drops module-level PRONOUN_* constants; SIG_READY/SIG_UNREADY/ROLE_SELECTED now resolve poss/subj from self.actor.pronouns at render time, so flipping a user's preference rewrites all their existing scroll prose; default actor → "their"
- SIG_READY prose strips a leading "The " from card_name so "the The Wanderer" reads "the Wanderer" and "the Engraven The Nomad" reads "the Engraven Nomad"; minor arcana ("Maid of Brands") untouched
- new applets/0005 seeds 'pronouns' applet (3x3, game-kit, default visible); _game_kit_sections.html grows a #id_gk_pronouns block w. 5 .gk-pronoun-card items labeled by ideology slug (italic) and tagged data-pronoun + data-trio
- card click → window.showGuard(card, "Set pronoun preference?<span class='guard-pronoun-trio'>{trio}</span>", commitCb); on OK fetches POST /dashboard/set-pronouns w. CSRF cookie + reloads so .active class moves and provenance prose re-renders; NVM dismisses
- dashboard.set_pronouns view (POST-only, login_required, 204/400/405) at /dashboard/set-pronouns; rejects choices not in PRONOUN_TABLE
- _game-kit.scss extends shared card rule to .gk-pronoun-card w. .active fill state + italic ideology label; #id_guard_portal .guard-pronoun-trio styled small/dim/centered under the question
- billscroll aperture: padding-right 0.75rem on #id_drama_scroll inside .applet-scroll so the timestamp column no longer sits beneath the scrollbar
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -651,3 +651,39 @@ class SkyNatusDataViewTest(TestCase):
|
||||
self.client.logout()
|
||||
response = self.client.get("/dashboard/sky/data")
|
||||
self.assertRedirects(response, "/?next=/dashboard/sky/data", fetch_redirect_response=False)
|
||||
|
||||
|
||||
class SetPronounsViewTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(email="they@test.io")
|
||||
self.client.force_login(self.user)
|
||||
self.url = reverse("set_pronouns")
|
||||
|
||||
def test_post_valid_choice_persists_on_user(self):
|
||||
response = self.client.post(self.url, data={"pronouns": "misogyny"})
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.user.refresh_from_db()
|
||||
self.assertEqual(self.user.pronouns, "misogyny")
|
||||
|
||||
def test_post_each_valid_choice(self):
|
||||
for key in ("pluralism", "bawlmorese", "misogyny", "misandry", "misanthropy"):
|
||||
with self.subTest(key=key):
|
||||
self.client.post(self.url, data={"pronouns": key})
|
||||
self.user.refresh_from_db()
|
||||
self.assertEqual(self.user.pronouns, key)
|
||||
|
||||
def test_post_invalid_choice_returns_400_and_does_not_change_user(self):
|
||||
original = self.user.pronouns
|
||||
response = self.client.post(self.url, data={"pronouns": "bogus"})
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.user.refresh_from_db()
|
||||
self.assertEqual(self.user.pronouns, original)
|
||||
|
||||
def test_get_returns_405(self):
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 405)
|
||||
|
||||
def test_requires_login(self):
|
||||
self.client.logout()
|
||||
response = self.client.post(self.url, data={"pronouns": "misogyny"})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
@@ -18,4 +18,5 @@ urlpatterns = [
|
||||
path('sky/preview', views.sky_preview, name='sky_preview'),
|
||||
path('sky/save', views.sky_save, name='sky_save'),
|
||||
path('sky/data', views.sky_natus_data, name='sky_natus_data'),
|
||||
path('set-pronouns', views.set_pronouns, name='set_pronouns'),
|
||||
]
|
||||
|
||||
@@ -179,6 +179,20 @@ def set_profile(request):
|
||||
request.user.save(update_fields=["username"])
|
||||
return redirect("/")
|
||||
|
||||
|
||||
@login_required(login_url="/")
|
||||
def set_pronouns(request):
|
||||
from django.http import HttpResponseNotAllowed
|
||||
from apps.lyric.models import PRONOUN_TABLE
|
||||
if request.method != "POST":
|
||||
return HttpResponseNotAllowed(["POST"])
|
||||
choice = request.POST.get("pronouns", "")
|
||||
if choice not in PRONOUN_TABLE:
|
||||
return HttpResponse(status=400)
|
||||
request.user.pronouns = choice
|
||||
request.user.save(update_fields=["pronouns"])
|
||||
return HttpResponse(status=204)
|
||||
|
||||
@login_required(login_url="/")
|
||||
def toggle_applets(request):
|
||||
checked = request.POST.getlist("applets")
|
||||
|
||||
Reference in New Issue
Block a user