-
-
Notifications
You must be signed in to change notification settings - Fork 323
Add Daily Challenges Feature with 24-Hour Reset Timer #5245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThis PR introduces a daily challenges feature that assigns randomized daily tasks to users tied to their check-in process. Changes include new DailyChallenge and UserDailyChallenge models, a service layer for challenge lifecycle management, timezone field support, enhanced check-in views with server-side validation and AJAX submission, management commands for challenge generation and timezone validation, and database migrations with challenge seeding. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CheckInView as Check-in View
participant DCS as DailyChallengeService
participant DB as Database
participant Points as Points System
User->>CheckInView: POST daily check-in (status_report data)
CheckInView->>DB: get_or_create DailyStatusReport for today
alt Duplicate submission
CheckInView-->>User: 400 error (already submitted)
else First submission
CheckInView->>DCS: check_and_complete_challenges(user, status_report)
DCS->>DB: get_active_challenges_for_user(today)
DB-->>DCS: active challenges list
loop For each active challenge
DCS->>DCS: _check_X_validator (e.g., early_checkin, positive_mood)
alt Challenge criteria met
DCS->>DB: UserDailyChallenge.mark_completed()
activate DB
DB->>DB: Begin transaction (SELECT FOR UPDATE)
DB->>DB: Update status to 'completed'
DB->>Points: Create Points entry (points_awarded)
DB->>DB: Commit transaction
deactivate DB
DCS->>DCS: Accumulate completed_challenges & total_points
end
end
DCS-->>CheckInView: completed_challenges, total_points
CheckInView->>DB: Compute next_challenge_at (now + CHALLENGE_RESET_HOURS)
CheckInView->>DB: Update UserDailyChallenge.next_challenge_at
CheckInView-->>User: 200 JSON {completed_challenges, total_points, next_challenge_at}
User->>User: Display success message & countdown timer
end
Estimated code review effortπ― 4 (Complex) | β±οΈ ~60 minutes Pre-merge checks and finishing touchesβ Failed checks (2 warnings)
β Passed checks (3 passed)
β¨ Finishing touches
π§ͺ Generate unit tests (beta)
π Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro Knowledge base: Disabled due to π Files selected for processing (1)
π§ Files skipped from review as they are similar to previous changes (1)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
π Hi @Pritz395! This pull request needs a peer review before it can be merged. Please request a review from a team member who is not:
Once a valid peer review is submitted, this check will pass automatically. Thank you! |
β Pre-commit checks failedThe pre-commit hooks found issues that need to be fixed. Please run the following commands locally to fix them: # Install pre-commit if you haven't already
pip install pre-commit
# Run pre-commit on all files
pre-commit run --all-files
# Or run pre-commit on staged files only
pre-commit runAfter running these commands, the pre-commit hooks will automatically fix most issues. π‘ Tip: You can set up pre-commit to run automatically on every commit by running: pre-commit installPre-commit outputFor more information, see the pre-commit documentation. |
ae2b831 to
730ca95
Compare
β Tests failedThe Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests. How to run tests locally# Install dependencies
poetry install --with dev
# Run all tests
poetry run python manage.py test
# Run tests with verbose output
poetry run python manage.py test -v 3
# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_methodTest output (last 100 lines)For more information, see the Django testing documentation. |
β Pre-commit checks failedThe pre-commit hooks found issues that need to be fixed. Please run the following commands locally to fix them: # Install pre-commit if you haven't already
pip install pre-commit
# Run pre-commit on all files
pre-commit run --all-files
# Or run pre-commit on staged files only
pre-commit runAfter running these commands, the pre-commit hooks will automatically fix most issues. π‘ Tip: You can set up pre-commit to run automatically on every commit by running: pre-commit installPre-commit output (last 100 lines)For more information, see the pre-commit documentation. |
730ca95 to
5104ce4
Compare
β Tests failedThe Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests. How to run tests locally# Install dependencies
poetry install --with dev
# Run all tests
poetry run python manage.py test
# Run tests with verbose output
poetry run python manage.py test -v 3
# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_methodTest output (last 100 lines)For more information, see the Django testing documentation. |
β Pre-commit checks failedThe pre-commit hooks found issues that need to be fixed. Please run the following commands locally to fix them: # Install pre-commit if you haven't already
pip install pre-commit
# Run pre-commit on all files
pre-commit run --all-files
# Or run pre-commit on staged files only
pre-commit runAfter running these commands, the pre-commit hooks will automatically fix most issues. π‘ Tip: You can set up pre-commit to run automatically on every commit by running: pre-commit installPre-commit output (last 100 lines)For more information, see the pre-commit documentation. |
β Tests failedThe Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests. How to run tests locally# Install dependencies
poetry install --with dev
# Run all tests
poetry run python manage.py test
# Run tests with verbose output
poetry run python manage.py test -v 3
# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_methodTest output (last 100 lines)For more information, see the Django testing documentation. |
β Pre-commit checks failedThe pre-commit hooks found issues that need to be fixed. Please run the following commands locally to fix them: # Install pre-commit if you haven't already
pip install pre-commit
# Run pre-commit on all files
pre-commit run --all-files
# Or run pre-commit on staged files only
pre-commit runAfter running these commands, the pre-commit hooks will automatically fix most issues. π‘ Tip: You can set up pre-commit to run automatically on every commit by running: pre-commit installPre-commit output (last 100 lines)For more information, see the pre-commit documentation. |
|
@coderabbitai why are the pre-commit and run-tests failing? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and canβt be posted inline due to platform limitations.
β οΈ Outside diff range comments (1)
website/views/organization.py (1)
1364-1393: Handle one checkβin per user/day without relying on DB errors
DailyStatusReportnow hasunique_together = ("user", "date")(see migration 0261), but this view still callsDailyStatusReport.objects.create(...)withdate=now().date(). A second submission on the same day will raise anIntegrityError, be caught by the outerexcept Exception, and result in a redirect/HTML response instead of the expected JSON β the frontend will just see a generic βerror submittingβ state.To make the βone checkβin per dayβ rule explicit and userβfriendly, consider updating or upserting instead of blind create:
- daily_status = DailyStatusReport.objects.create( - user=request.user, - date=now().date(), - previous_work=previous_work, - next_plan=next_plan, - blockers=blockers, - goal_accomplished=goal_accomplished, - current_mood=current_mood, - ) + daily_status, _ = DailyStatusReport.objects.update_or_create( + user=request.user, + date=now().date(), + defaults={ + "previous_work": previous_work, + "next_plan": next_plan, + "blockers": blockers, + "goal_accomplished": goal_accomplished, + "current_mood": current_mood, + }, + )You can then customize the response when
_isFalse(update vs first submit) if you want to surface an βedited todayβs checkβinβ message.
π§Ή Nitpick comments (11)
website/templates/base.html (1)
98-106: Consider proper Tailwind CSS build instead of suppressing CDN warnings.Suppressing the Tailwind CDN warning masks a legitimate production concern. The CDN version lacks tree-shaking, increasing bundle size and load time. Consider setting up a proper Tailwind build process with PostCSS for production.
If CDN usage is intentional for development simplicity, this workaround is functional but should be documented with a TODO for future migration.
website/models.py (2)
958-964: Index on(user, challenge_date)is redundant withunique_together.The
unique_togetherconstraint already creates an index on(user, challenge_date). The explicit index declaration on line 962 is redundant and adds minor overhead during writes.indexes = [ - models.Index(fields=["user", "challenge_date"]), models.Index(fields=["status", "challenge_date"]), ]
1114-1118: Consider adding timezone validation.Invalid timezone strings will cause runtime errors when used with
pytz. A validator could prevent this:from django.core.exceptions import ValidationError import pytz def validate_timezone(value): if value not in pytz.all_timezones: raise ValidationError(f"'{value}' is not a valid timezone.") timezone = models.CharField( max_length=50, default="UTC", validators=[validate_timezone], help_text="User's timezone (e.g., 'Asia/Kolkata', 'America/New_York'). Defaults to UTC.", )Alternatively, validation can be handled at the service/form layer.
website/views/organization.py (3)
1395-1405: Scopenext_challenge_atupdates to the current dayβs assigned challengeRight now the bulk update:
UserDailyChallenge.objects.filter( user=request.user, status="assigned", ).update(next_challenge_at=next_challenge_time)touches all assigned challenges for the user, including any stale rows from previous days. That makes
next_challenge_atdrift on historical assignments and can complicate future logic that might use it for analytics or expiry.Given the service and UI operate on a single challenge per
challenge_date, it would be cleaner to limit this to todayβs challenge:- UserDailyChallenge.objects.filter( - user=request.user, - status="assigned", - ).update(next_challenge_at=next_challenge_time) + UserDailyChallenge.objects.filter( + user=request.user, + status="assigned", + challenge_date=now().date(), + ).update(next_challenge_at=next_challenge_time)This keeps
next_challenge_atsemantically tied to β24h from this checkβinβ for the current day only.
1407-1453: Align challenge date handling with timezone utilities and avoid duplicate importsMinor points about the challenge completion block:
- You use
date.today()to filter todayβs completed challenges, while other parts of the file consistently usetimezone.now()/now().date(). For consistency with Djangoβs timezone handling (and to avoid surprises if server vs configured timezone diverge), itβs safer to usetimezone.now().date()or the samenow().date()helper used above.UserDailyChallengeis imported twice within the same function (once before thenext_challenge_atupdate and again inside this block). Importing once at the top of the module (or once at the top of the view) would be simpler.Suggested tightening:
- from datetime import date - - from website.models import UserDailyChallenge - user_challenges = ( UserDailyChallenge.objects.filter( user=request.user, - challenge_date=date.today(), + challenge_date=now().date(), status="completed", )Functionality is fine asβis; this is just to keep things consistent and tidy.
2231-2308: Challenge/timer orchestration looks sound; consider expiring stale assignmentsThe new
add_sizzle_checkINlogic correctly:
- Surfaces yesterdayβs report or last checkβin as context.
- Uses
DailyChallengeService.get_active_challenges_for_userto both fetch and autoβassign a challenge when 24h has elapsed.- Computes
next_challenge_atas β24 hours from the most recent checkβinβ with a reasonable fallback to any preβsetnext_challenge_at.One thing to consider for longβterm hygiene:
UserDailyChallengerows withstatus="assigned"andchallenge_date < todayare never marked as expired, and theyβre also the ones currently being bulkβupdated insizzle_daily_log. Over time this can leave a tail of stale βassignedβ rows that are never surfaced to the user.If you expect daily challenges to be strictly one per calendar day, it may be worth:
- Marking older assigned challenges as
"expired"when a new dayβs challenge is allocated, or- Having
get_active_challenges_for_userfilterchallenge_date=todayonly (it already does), and explicitly expire older ones in_check_and_assign_new_challenge_if_needed.Not strictly required for correctness right now, but it will keep the challenge history cleaner and queries more predictable.
website/templates/sizzle/add_sizzle_checkin.html (3)
205-241: Make blockers selection truly required (server logic already depends on it)The blockers section is labeled as required, but the
<select name="blockers">does not have arequiredattribute, and thereβs no clientβside validation hook beyond that. A user can submit without picking anything, which results inblockers = ""on the backend and makes it impossible to satisfy the βcomplete_all_fieldsβ challenge.Consider:
- Adding
requiredto the<select>:- <select id="blockers_card" - name="blockers" + <select id="blockers_card" + name="blockers" + required
- Optionally, adding serverβside validation in
sizzle_daily_logto reject empty blockers with a clear error message rather than silently storing an empty string.This keeps UX aligned with the βRequiredβ badge and ensures challenge logic behaves predictably.
545-567: Keep word counts in sync when autofilling from previous checkβinsThe autofill buttons populate
previous_work_cardfromyesterday_report.next_planorlast_checkin.next_plan, but they donβt re-runupdateWordCount, so the counter chip can remain at β0 wordsβ until the user types.Youβre already calling
updateWordCount(...)onDOMContentLoadedand on textareaoninput. For consistency, you can call it after setting the value:fillFromPreviousBtn.addEventListener('click', () => { const previousWorkTextarea = document.getElementById('previous_work_card'); previousWorkTextarea.value = "{{ yesterday_report.next_plan|escapejs }}"; previousWorkTextarea.focus(); + updateWordCount('previous_work_card', 'previous_work_count'); }); ... fillFromLastCheckinBtn.addEventListener('click', () => { const previousWorkTextarea = document.getElementById('previous_work_card'); previousWorkTextarea.value = "{{ last_checkin.next_plan|escapejs }}"; previousWorkTextarea.focus(); + updateWordCount('previous_work_card', 'previous_work_count'); });Pure UX polish, but it makes the guidance more trustworthy.
Also applies to: 525-528
672-677: Error state styling on the success banner doesnβt fully take effectIn the error paths you mutate classes on
#success-message:successMessage.classList.remove('bg-green-50', 'border-green-200', 'dark:bg-green-900/20', 'dark:border-green-800'); successMessage.classList.add('bg-red-50', 'border-red-200', 'dark:bg-red-900/20', 'dark:border-red-800');However, those color/border classes live on the inner container (
<div class="p-4 bg-green-50 ... border ...">), not on the outer#success-messagewrapper (class="mb-6 hidden transition-..."). As a result, the error styling wonβt match what you expect β the inner box will remain green even when you add red classes to the wrapper.If you want to reuse the same box for success and error states, either:
- Move the background/border classes to the
#success-messageelement and adjust the markup, or- Target the inner div via an
idand toggle classes on that instead.Functionality is fine; this is just visual correctness.
Also applies to: 695-700, 16-32
website/services/daily_challenge_service.py (2)
88-117: Minor: avoid double querying for active challengesIn
_assign_random_challengeyou first checkactive_challenges.exists()and then immediately materializelist(active_challenges), which hits the database twice:active_challenges = DailyChallenge.objects.filter(is_active=True) if not active_challenges.exists(): ... challenge_list = list(active_challenges) if not challenge_list: ... selected_challenge = random.choice(challenge_list)Given the table should be small, you can simplify and hit the DB once:
- active_challenges = DailyChallenge.objects.filter(is_active=True) - if not active_challenges.exists(): - logger.warning(f"No active challenges available for user {user.username}") - return None - - # Randomly select a challenge type - challenge_list = list(active_challenges) + challenge_list = list(DailyChallenge.objects.filter(is_active=True)) if not challenge_list: logger.warning(f"Empty challenge list for user {user.username}") return NoneBehavior stays the same, just avoids redundant work.
243-253: Consider user timezones for early check-in and clarify streak milestone docstringTwo small consistency points:
_check_early_checkincomparesdaily_status_report.created.time()against a fixed10:00threshold in server timezone. Now thatUserProfilehas atimezonefield (added in the same migration), you might want to eventually interpret βbefore 10 AMβ in the userβs local timezone by convertingcreatedto that tz before extractingtime().
_check_streak_milestoneβs docstring mentions milestones(7, 15, 30 days), butmilestone_streaksactually includes[7, 15, 30, 100, 180, 365]. Either update the docstring to match or trim the list to the documented values so future readers arenβt surprised.Neither blocks functionality, but tightening these will make the behavior more predictable and selfβdocumenting.
Also applies to: 290-299
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
π Files selected for processing (6)
website/migrations/0261_dailychallenge_userdailychallenge.py(1 hunks)website/models.py(3 hunks)website/services/daily_challenge_service.py(1 hunks)website/templates/base.html(3 hunks)website/templates/sizzle/add_sizzle_checkin.html(2 hunks)website/views/organization.py(3 hunks)
π§° Additional context used
𧬠Code graph analysis (2)
website/services/daily_challenge_service.py (1)
website/models.py (6)
UserDailyChallenge(910-1002)UserProfile(1039-1257)DailyStatusReport(1703-1717)DailyChallenge(862-907)is_active(3480-3485)mark_completed(966-999)
website/views/organization.py (2)
website/models.py (2)
DailyStatusReport(1703-1717)UserDailyChallenge(910-1002)website/services/daily_challenge_service.py (3)
DailyChallengeService(13-311)check_and_complete_challenges(153-240)get_active_challenges_for_user(17-35)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Run Tests
- GitHub Check: docker-test
π Additional comments (4)
website/templates/base.html (1)
374-376: LGTM!Good defensive check to scope AJAX handling only to the modal form, preventing interference with the main page form's native submission behavior.
website/models.py (2)
862-908: LGTM!The
DailyChallengemodel is well-structured as a lookup table for challenge types. The unique constraint onchallenge_typeensures data integrity, and theis_activeflag provides flexibility for enabling/disabling challenges without deletion.
1713-1715: Verify that the migration handles existing duplicate records before deployment.The
unique_togetherconstraint on(user, date)is syntactically correct and will enforce one check-in per user per day at the database level. However, this constraint addition requires a data migration to handle any existing duplicate recordsβconfirm the migration exists and processes them appropriately.website/services/daily_challenge_service.py (1)
153-240: Challenge completion flow is robust and idempotentThe
check_and_complete_challengesimplementation:
- Filters challenges by
challenge_date = daily_status_report.dateandstatus="assigned", so you only evaluate relevant entries.- Maps challenge types to explicit, wellβscoped predicates and logs unknown types instead of failing hard.
- Wraps each perβchallenge evaluation in a
try/exceptand relies onuser_challenge.mark_completed()for atomic status/points updates, which handles doubleβcompletion gracefully.This is a solid pattern for βevaluate a small set of rules, award points onceβ logic.
|
Tip For best results, initiate chat on the files or code changes. π§© Analysis chainπ Script executed: Length of output: 2821 π Script executed: Length of output: 711 π Script executed: Length of output: 464 π Script executed: Length of output: 1596 @Pritz395: Good news! The checks are not actually failing. Here's the current status: β
Run pre-commit - PASSED (completed successfully in 1m3s) Neither check has failed. The "Run Tests" check is still executing and hasn't completed yet. It's been running for several minutes, which is normal for a comprehensive test suite. What to do:
The tests may take some time depending on the test suite size. If the tests do fail when they complete, feel free to tag me again and I'll help identify the specific issues! π |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
β»οΈ Duplicate comments (1)
website/templates/sizzle/add_sizzle_checkin.html (1)
588-735: UnescapedinnerHTMLusage for challenge titles/descriptions/messages is still an XSS riskYouβre still building HTML strings with values like
challenge.title,challenge.description, anddata.messageand assigning them via.innerHTML. If any of those fields ever contain<script>or even just HTML tags (e.g., from admin-defined challenge text or error messages that echo user input), theyβll be interpreted as HTML and can become an XSS vector.This is the same issue flagged in the earlier review and hasnβt been addressed yet.
A minimal fix is to introduce a small escaping helper and run all interpolated strings through it before concatenating:
@@ - // Word count function - function updateWordCount(textareaId, countId) { + // Simple HTML-escape helper for safely injecting text into innerHTML + function escapeHtml(str) { + if (str === null || str === undefined) return ''; + return String(str) + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + // Word count function + function updateWordCount(textareaId, countId) { @@ - data.completed_challenges.forEach(challenge => { + data.completed_challenges.forEach(challenge => { challengesHtml += ` <div class="flex items-center justify-between p-2 bg-green-100 dark:bg-green-900/30 rounded"> - <span class="text-sm font-medium text-green-800 dark:text-green-200">${challenge.title}</span> - <span class="text-sm font-bold text-green-600 dark:text-green-400">+${challenge.points} points</span> + <span class="text-sm font-medium text-green-800 dark:text-green-200">${escapeHtml(challenge.title)}</span> + <span class="text-sm font-bold text-green-600 dark:text-green-400">+${escapeHtml(String(challenge.points))} points</span> @@ - data.completed_challenges.forEach(challenge => { + data.completed_challenges.forEach(challenge => { completedHtml += ` @@ - <h4 class="font-medium text-gray-900 dark:text-gray-200">β ${challenge.title}</h4> - <p class="text-sm text-gray-600 dark:text-gray-400 mt-1">${challenge.description}</p> + <h4 class="font-medium text-gray-900 dark:text-gray-200">β ${escapeHtml(challenge.title)}</h4> + <p class="text-sm text-gray-600 dark:text-gray-400 mt-1">${escapeHtml(challenge.description)}</p> @@ - challengesCompleted.innerHTML = ` + challengesCompleted.innerHTML = ` <p class="text-sm text-red-700 dark:text-red-300"> - ${data.message || 'An error occurred. Please try again.'} + ${escapeHtml(data.message || 'An error occurred. Please try again.')} @@ - challengesCompleted.innerHTML = ` + challengesCompleted.innerHTML = ` <p class="text-sm text-red-700 dark:text-red-300"> - An error occurred. Please try again. + ${escapeHtml('An error occurred. Please try again.')}Alternatively, for maximum safety, stop building HTML strings and instead
document.createElement(...)nodes and set.textContentfor all user-/admin-controlled text.
π§Ή Nitpick comments (6)
website/views/organization.py (4)
1382-1397: Consider adding server-side checks for other βrequiredβ fields
goal_accomplishedandcurrent_moodare only enforced via HTMLrequiredattributes;previous_work/next_plancan also be empty if a client bypasses the browser checks. Thatβs not a security issue, but it means users could submit an incomplete check-in and then be surprised when βcomplete all fieldsβ or βpositive moodβ challenges donβt complete.If you want strict invariants on stored reports, add simple server-side validation (e.g., reject when any required field is blank or mood is missing) before creating
DailyStatusReport.
1399-1408:next_challenge_atupdate assumes a single active assignment per userUpdating
next_challenge_aton allstatus="assigned"UserDailyChallengerows for the user is fine as long as your service logic guarantees at most one active assignment at a time. If you ever support multiple simultaneous challenges per user, this blanket update may not reflect the intended cooldown per assignment.If the invariant is βone assigned challenge per user per dayβ, consider either:
- Filtering by
challenge_date=now().date()here, or- Centralizing cooldown management inside
DailyChallengeServiceso this view only creates the status report.
1410-1447: Challenge completion response works but re-queries could be simplifiedThe pattern of calling
DailyChallengeService.check_and_complete_challenges(...)to get a list of titles, then immediately re-queryingUserDailyChallengefor today and filtering by those titles is correct but a bit indirect and does two DB hits.If you want to tighten this up:
- Have
check_and_complete_challengesreturn the completedUserDailyChallengeinstances (or a list of{title, points, description}dicts) so you donβt need a second query, or- Reuse the
user_challengesqueryset as the source of truth and let the service annotate which ones completed.Not a blocker; current implementation is clear and safe.
2235-2308: add_sizzle_checkIN: data loading and timer computation look consistentThe view correctly:
- Prefers
yesterday_report, falls back to most recent check-in, and exposesall_checkinsfor history.- Uses
DailyChallengeService.get_active_challenges_for_userand todayβs date to load active and completed challenges.- Computes
next_challenge_atfrom the most recentDailyStatusReport.createdor, if no check-in exists yet, from any existing assignedUserDailyChallenge.next_challenge_at.You hit
DailyStatusReport.objects.filter(user=request.user)multiple times (yesterday,last_checkin,last_checkin_for_timer); if this endpoint ever gets hot, you could consolidate to one query and derive all three concepts from it, but thatβs an optimization, not a requirement.website/templates/sizzle/add_sizzle_checkin.html (2)
248-281: Autofill from yesterday/last check-in is useful; consider syncing word-count UIThe βFill from Previous Check-inβ / βFill from Last Check-inβ buttons improve usability by reusing prior
next_plantext, but after programmatic assignment you never callupdateWordCount, so the word counter pill can still display β0 wordsβ while the textarea is full.You can keep the UI consistent by invoking
updateWordCount('previous_work_card', 'previous_work_count')right after settingpreviousWorkTextarea.valuein both click handlers.
521-547: Word count logic is fine; class reset is intentional but drops some initial stylingThe
updateWordCounthelper correctly trims, splits, and counts words and applies different class sets based on thresholds. Note that each update overwritesclassName, so you lose the initialrounded-full,shadow-sm, and border classes defined in the markup.If you want to preserve those, consider toggling only the color-related classes instead of replacing the whole class list. As-is, itβs functionally fine.
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
π Files selected for processing (2)
website/templates/sizzle/add_sizzle_checkin.html(2 hunks)website/views/organization.py(3 hunks)
π§° Additional context used
𧬠Code graph analysis (1)
website/views/organization.py (2)
website/models.py (2)
DailyStatusReport(1703-1717)UserDailyChallenge(910-1002)website/services/daily_challenge_service.py (3)
DailyChallengeService(13-311)check_and_complete_challenges(153-240)get_active_challenges_for_user(17-35)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run Tests
π Additional comments (4)
website/views/organization.py (1)
1364-1383: Blockers handling and validation now correctly enforce a non-empty valueThe new
blockers_type/blockers_otherlogic plus the 400 JSON response when no option is selected closes the earlier gap where an empty blockers value could be persisted and break thecomplete_all_fieldschallenge. Mapping"no_blockers"β"no blockers"also aligns with the challenge serviceβs"no blockers"/"no_blockers"checks.No further changes needed here.
website/templates/sizzle/add_sizzle_checkin.html (3)
210-247: Blockers UI now matches backend validation and challenge semanticsThe
blockers_card<select>is markedrequired, includes an explicit"no_blockers"option, and the βOtherβ textarea is only shown and required when that option is chosen. Combined with the server-side enforcement insizzle_daily_log, this resolves the earlier blockers-empty bug and ensures βno_blockersβ and βcomplete_all_fieldsβ challenges can be completed reliably.Looks solid.
407-476: Check-in history table wiring matches new data modelThe history tableβs columns (date/time, goals, mood, blockers, previous work, next plan) line up with the fields provided by
all_checkinsin the view. Usingtruncatewordsplustitleattributes provides a reasonable balance between readability and access to full content.No issues here from a data/logic standpoint.
483-519: Countdown timer implementation is straightforward and correctly handles expired windowsThe timer pulls
data-next-challenge(ISO 8601), computes the remaining delta, switches to βAvailable now!β whendiff <= 0, and flips text color classes between red and green appropriately. Guarding on the elementβs presence and only starting the interval when present keeps it safe.All good.
|
@DonnieBLT Not sure if we should close this or merge this for now and implement the standalone later. The feature itself is fully working though. |
|
π¬ Reminder: Unresolved Conversations Hi @Pritz395! This pull request has 1 unresolved conversation that need to be addressed. Please review and resolve the pending discussions so we can move forward with merging this PR. Thank you! π |
1 similar comment
|
π¬ Reminder: Unresolved Conversations Hi @Pritz395! This pull request has 1 unresolved conversation that need to be addressed. Please review and resolve the pending discussions so we can move forward with merging this PR. Thank you! π |
|
π¬ Reminder: Unresolved Conversations Hi @Pritz395! This pull request has 1 unresolved conversation that need to be addressed. Please review and resolve the pending discussions so we can move forward with merging this PR. Thank you! π |
1 similar comment
|
π¬ Reminder: Unresolved Conversations Hi @Pritz395! This pull request has 1 unresolved conversation that need to be addressed. Please review and resolve the pending discussions so we can move forward with merging this PR. Thank you! π |
|
π¬ Reminder: Unresolved Conversations Hi @Pritz395! This pull request has 1 unresolved conversation that need to be addressed. Please review and resolve the pending discussions so we can move forward with merging this PR. Thank you! π |
2 similar comments
|
π¬ Reminder: Unresolved Conversations Hi @Pritz395! This pull request has 1 unresolved conversation that need to be addressed. Please review and resolve the pending discussions so we can move forward with merging this PR. Thank you! π |
|
π¬ Reminder: Unresolved Conversations Hi @Pritz395! This pull request has 1 unresolved conversation that need to be addressed. Please review and resolve the pending discussions so we can move forward with merging this PR. Thank you! π |
Overview
Implements a daily challenges system for check-ins with randomized challenge assignments and a 24-hour reset timer.
Features:
Files Changed:
website/models.py- Added DailyChallenge and UserDailyChallenge modelswebsite/services/daily_challenge_service.py- NEW: Challenge logic and validation servicewebsite/views/organization.py- Integrated challenges into check-in viewswebsite/templates/sizzle/add_sizzle_checkin.html- Updated UI with challenges displaywebsite/templates/base.html- Suppressed Tailwind CDN warningwebsite/migrations/0261_dailychallenge_userdailychallenge.py- Combined migration for models and next_challenge_at fieldTechnical Details:
Related Issue
Closes #2572
Visual Demo
Screen.Recording.2025-12-11.at.7.57.48.PM.mov
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
βοΈ Tip: You can customize this high-level summary in your review settings.