senior's post-demo changes: parent report in the student's own account, Jalali birth date, invoices, math-only tutor, priced quotas
- remove the family app from the root (separate parent accounts, invite codes, parental settings); progress.0006 drops its tables, permissions and migration history, and Profile.parent_phone, the two family notification types and ?userId= on /api/progress/nodes go with it - GET /api/user/weekly-report: last 7 days next to the 7 before (estimated minutes, questions, accuracy, islands, tutor questions) for the "parent report" page - profile birthDate in Jalali (accounts/jalali.py), ages 5-99, birthYear derived from it - GET /api/payment/invoices; orders now snapshot list price and plan title - tutor answers only math: server pre-filter, scope rule in the prompt (tutor-v11), refusal marker replaced with fixed text; self-harm gets a help line instead - streamed answers now record token usage (it was always 0) - daily tutor quota: AI_DAILY_QUOTA_FREE (default 3) vs Plan.ai_daily_quota - delete_users management command for wiping test accounts across every app - docs/ai-cost-and-guardrails.md: measured cost per question and guardrail results Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,9 +4,9 @@
|
||||
یک کلید طبیعی/یکتا (phone، device_id، code، idempotency_key و ...) انجام
|
||||
میشه، نه با create ساده.
|
||||
|
||||
هدف: پوشش کمینهی جریانهای اصلی API در هر ۱۱ اپ (accounts، content،
|
||||
هدف: پوشش کمینهی جریانهای اصلی API در هر ۱۰ اپ (accounts، content،
|
||||
progress، ai_tutor، subscription، payment، discount، notifications،
|
||||
affiliate، support، family) با کمترین حجم دادهی لازم برای اینکه هر
|
||||
affiliate، support) با کمترین حجم دادهی لازم برای اینکه هر
|
||||
endpoint اصلی بدون نیاز به ساختن دستی پیشنیازهاش قابل تست باشه.
|
||||
|
||||
⚠️ این دستور فقط برای محیط لوکال/staging است، نه پروداکشن:
|
||||
@@ -44,8 +44,6 @@ from accounts.models import OTPCode, Profile, Session, User
|
||||
# چون این فقط دیتای لوکال/staging است نه پروداکشن، همین کافیه. با پیشوند
|
||||
# یکسان و بازهی اختصاصی، شناساییِ رکوردهای seed در دیتابیس هم ساده میمونه.
|
||||
MAIN_PHONE = '09120000001'
|
||||
PARENT_PHONE = '09120000002'
|
||||
CHILD_PHONE = '09120000003'
|
||||
REFERRED_PHONE = '09120000004'
|
||||
GUEST_DEVICE_ID = 'seed-guest-device-0001'
|
||||
|
||||
@@ -101,14 +99,13 @@ class Command(BaseCommand):
|
||||
from affiliate.models import AffiliateAccount, Conversion
|
||||
from content.models import Grade, NodeProgress, UserAnswer, UserLessonCompletion
|
||||
from discount.models import DiscountCode, Offer, Redemption, UserOffer
|
||||
from family.models import FamilyLink
|
||||
from notifications.models import Notification
|
||||
from payment.models import Order
|
||||
from progress.models import LeaderboardEntry, StreakRecord, UserHearts, UserStats
|
||||
from subscription.models import Plan, Subscription
|
||||
from support.models import BugReport, Ticket
|
||||
|
||||
phones = [MAIN_PHONE, PARENT_PHONE, CHILD_PHONE, REFERRED_PHONE]
|
||||
phones = [MAIN_PHONE, REFERRED_PHONE]
|
||||
user_ids = list(User.objects.filter(phone__in=phones).values_list('pk', flat=True))
|
||||
guest = User.objects.filter(device_id=GUEST_DEVICE_ID, is_guest=True).first()
|
||||
if guest:
|
||||
@@ -132,7 +129,6 @@ class Command(BaseCommand):
|
||||
Conversion.objects.filter(referred_user_id__in=user_ids).delete()
|
||||
Ticket.objects.filter(user_id__in=user_ids).delete()
|
||||
BugReport.objects.filter(user_id__in=user_ids).delete()
|
||||
FamilyLink.objects.filter(parent_user_id__in=user_ids).delete()
|
||||
|
||||
from progress.models import Character
|
||||
Character.objects.filter(slug__startswith='seed-').delete()
|
||||
@@ -154,7 +150,7 @@ class Command(BaseCommand):
|
||||
|
||||
# -- seed -------------------------------------------------------------
|
||||
def _seed(self):
|
||||
main_user, guest_user, parent_user, child_user, referred_user = self._seed_accounts()
|
||||
main_user, guest_user, referred_user = self._seed_accounts()
|
||||
self._seed_content_and_progress(main_user)
|
||||
self._seed_characters()
|
||||
self._seed_ai_tutor(main_user)
|
||||
@@ -163,12 +159,10 @@ class Command(BaseCommand):
|
||||
self._seed_notifications(main_user)
|
||||
self._seed_affiliate(main_user, referred_user)
|
||||
self._seed_support(main_user)
|
||||
self._seed_family(parent_user, child_user)
|
||||
|
||||
tokens = [
|
||||
('کاربر اصلی (ثبتنامشده)', main_user, issue_access_token(main_user.pk)),
|
||||
('کاربر مهمان', guest_user, issue_access_token(guest_user.pk)),
|
||||
('والد', parent_user, issue_access_token(parent_user.pk)),
|
||||
]
|
||||
return {'tokens': tokens}
|
||||
|
||||
@@ -179,7 +173,7 @@ class Command(BaseCommand):
|
||||
defaults=dict(name=f'{SEED_TAG} کاربر اصلی', is_guest=False, grade_id=None),
|
||||
)
|
||||
Profile.objects.get_or_create(
|
||||
user=main_user, defaults=dict(birth_year=2015, parent_phone=PARENT_PHONE, bio=f'{SEED_TAG} پروفایل تستی')
|
||||
user=main_user, defaults=dict(birth_year=1394, bio=f'{SEED_TAG} پروفایل تستی')
|
||||
)
|
||||
Session.objects.get_or_create(
|
||||
user=main_user,
|
||||
@@ -206,18 +200,12 @@ class Command(BaseCommand):
|
||||
),
|
||||
)
|
||||
|
||||
parent_user, _ = User.objects.get_or_create(
|
||||
phone=PARENT_PHONE, defaults=dict(name=f'{SEED_TAG} والد', is_guest=False)
|
||||
)
|
||||
child_user, _ = User.objects.get_or_create(
|
||||
phone=CHILD_PHONE, defaults=dict(name=f'{SEED_TAG} فرزند', is_guest=False)
|
||||
)
|
||||
referred_user, _ = User.objects.get_or_create(
|
||||
phone=REFERRED_PHONE, defaults=dict(name=f'{SEED_TAG} کاربر معرفیشده', is_guest=False)
|
||||
)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('accounts: 5 کاربر (اصلی/مهمان/والد/فرزند/معرفیشده) آماده شد.'))
|
||||
return main_user, guest_user, parent_user, child_user, referred_user
|
||||
self.stdout.write(self.style.SUCCESS('accounts: 3 کاربر (اصلی/مهمان/معرفیشده) آماده شد.'))
|
||||
return main_user, guest_user, referred_user
|
||||
|
||||
# -- content + progress --------------------------------------------------
|
||||
def _seed_content_and_progress(self, main_user):
|
||||
@@ -469,28 +457,3 @@ class Command(BaseCommand):
|
||||
)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('support: 1 تیکت با یک پیام، و 1 گزارش باگ آماده شد.'))
|
||||
|
||||
# -- family -----------------------------------------------------------------------
|
||||
def _seed_family(self, parent_user, child_user):
|
||||
from family.models import FamilyLink, ParentalSettings, WeeklyReportLog
|
||||
|
||||
link, _ = FamilyLink.objects.get_or_create(
|
||||
parent_user_id=parent_user.pk,
|
||||
child_phone=CHILD_PHONE,
|
||||
defaults=dict(
|
||||
child_user_id=child_user.pk,
|
||||
invite_code='SEEDFAM01',
|
||||
status='accepted',
|
||||
accepted_at=timezone.now(),
|
||||
),
|
||||
)
|
||||
ParentalSettings.objects.get_or_create(
|
||||
family_link=link, defaults=dict(daily_limit_minutes=60)
|
||||
)
|
||||
WeeklyReportLog.objects.get_or_create(
|
||||
family_link=link,
|
||||
week_start=(timezone.now() - timedelta(days=timezone.now().weekday())).date(),
|
||||
defaults=dict(sent_at=timezone.now()),
|
||||
)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('family: 1 اتصالِ خانواده (پذیرفتهشده) با تنظیمات و گزارشِ هفتگی آماده شد.'))
|
||||
|
||||
Reference in New Issue
Block a user