Skip to content

Gamification features (draft)#517

Open
xXPinkmagicXx wants to merge 242 commits intomasterfrom
gamification
Open

Gamification features (draft)#517
xXPinkmagicXx wants to merge 242 commits intomasterfrom
gamification

Conversation

@xXPinkmagicXx
Copy link
Copy Markdown
Member

@xXPinkmagicXx xXPinkmagicXx commented Apr 2, 2026

Gamification of Zeeguu

In this PR we have added the backend for features such as friends, friend streak, badges, friend leaderboard, classroom leaderboard.

In one image we have added:
image

Database changes

See changes in tools/migration

  • 26-02-19--add_badges.sql
  • 26-02-24-a-add_username.sql
  • 26-02-24-b-add_username.py
  • 26-02-24--friendship_system.sql
  • 26-02-26--add_max_streak_to_user_language.sql
  • 26-02-28--insert_default_badges.sql
  • 26-02-28--add_user_avatar.sql

We have added new tables:

  • friend
  • friend_requests
  • badge
  • badge_level
  • user_badge_level
  • user_badge_progress
  • user_avatar

Testing added

  • test_friends.py (core/model)
  • test_friends.py (api)
  • test_badges.py

Issues and dicussions

Also see sub issues

Breakdown PRs of features

Gamification feature flag

Friends and Profile

Badges

Leaderboards

Gamification Feature - Branch Summary

This branch introduces a social and gamification layer to the Zeeguu platform, including a Friend System, Badge System, User Avatars, and Usernames.


Database Migrations

File Description
26-02-19--add_badges.sql Creates badge, badge_level, user_badge_level, and user_badge_progress tables
26-02-24-a-add_username.sql Adds username column to the user table (unique, case-sensitive, UTF-8)
26-02-24-b-add_username.py Backfill migration: auto-generates adjective_noun1234 usernames for all existing users
26-02-24--friendship_system.sql Creates friend and friend_requests tables
26-02-26--add_max_streak_to_user_language.sql Adds max_streak and max_streak_date columns to user_language; seeds from existing daily_streak values
26-02-28--insert_default_badges.sql Seeds 7 badges with 5 levels each into badge and badge_level
26-03-13--add_user_avatar.sql Creates user_avatar table

New Database Tables

friend

Stores accepted friendships between two users. Each row represents a directional link (user_idfriend_id). Tracks:

  • friend_streak — incremented when both friends practice on the same day
  • friend_streak_last_updated — timestamp of the last streak update
  • deleted_at — soft delete support

friend_requests

Stores pending friend requests from a sender to a receiver, with timestamps for creation (created_at) and response (responded_at).

badge

Defines available badges, each identified by a unique code (e.g. TRANSLATED_WORDS, STREAK_COUNT).

badge_level

Defines the levels (1–5) for each badge with a target_value threshold and an optional icon_name.

user_badge_level

Records which badge levels a user has achieved, with achieved_at timestamp and an is_shown flag to track whether the achievement notification has been displayed.

user_badge_progress

Tracks a user's current progress value toward each badge (e.g. number of words translated, current streak length).

user_avatar

Stores a user's avatar configuration: image_name, character_color, and background_color.


New Models

  • Friend (zeeguu/core/model/friend.py) — Friendship record with streak logic. update_friend_streak() checks both users' most recent practice date across all languages and increments or resets accordingly.
  • FriendRequest (zeeguu/core/model/friend_request.py) — Manages the full friend request lifecycle: send, accept, reject, and delete.
  • Badge / BadgeLevel (zeeguu/core/model/badge.py, badge_level.py) — Badge definitions and tiered levels with BadgeCode enum for type-safe references.
  • UserBadgeLevel (zeeguu/core/model/user_badge_level.py) — Tracks achieved badge levels per user, with helper to mark all unseen achievements as shown.
  • UserBadgeProgress (zeeguu/core/model/user_badge_progress.py) — Tracks per-user progress metrics for each badge.
  • UserAvatar (zeeguu/core/model/user_avatar.py) — Stores and updates a user's avatar style (image, character color, background color).

Badge System

A new zeeguu/core/badges/badge_progress.py module provides two functions used throughout the codebase:

  • increment_badge_progress(session, badge_code, user_id, increment=1) — increments a user's progress counter and automatically awards any newly unlocked badge levels.
  • update_badge_progress(session, badge_code, user_id, current_value) — overwrites the progress value (used for absolute metrics like streak count).

Badge progress is hooked into existing core actions:

Trigger Badge
Word translated TRANSLATED_WORDS
Exercise answered correctly CORRECT_EXERCISES
Audio lesson completed COMPLETED_AUDIO_LESSONS
Daily streak updated STREAK_COUNT
Word learned (moved to known) LEARNED_WORDS
Article reading tracked READ_ARTICLES

The NUMBER_OF_FRIENDS badge is defined and seeded but progress is tracked separately via the friend system.

Default badges seeded (7 badges × 5 levels each):

  • Meaning Builder (Translated Words): 10 / 100 / 500 / 1000 / 2500
  • Practice Builder (Correct Exercises): 10 / 250 / 1000 / 5000 / 20000
  • Sound Scholar (Audio Lessons): 1 / 25 / 50 / 150 / 300
  • Consistency Champion (Streak): 7 / 30 / 90 / 180 / 365 days
  • Word Collector (Learned Words): 1 / 10 / 50 / 250 / 750
  • Active Reader (Articles): 5 / 25 / 100 / 500 / 1000
  • Influencer (Friends): 1 / 3 / 5 / 7 / 10

New API Endpoints

Friend System (/zeeguu/api/endpoints/friends.py)

Method Endpoint Description
GET /get_friends Get all friends of the current user
GET /get_friends/<username> Get friends for another user (requires being their friend)
GET /get_number_of_received_friend_requests Count pending incoming friend requests
GET /get_received_friend_requests List pending incoming friend requests with sender avatars
GET /get_sent_friend_requests List outgoing friend requests
POST /send_friend_request Send a friend request by receiver username
POST /delete_friend_request Cancel a sent friend request
POST /accept_friend_request Accept an incoming friend request and create a friendship
POST /reject_friend_request Reject an incoming friend request
POST /unfriend Remove an existing friendship
GET /search_users Search for users by name or username

Friend responses include: user name/username, friendship details (including friend_streak), avatar, and language streak info.

Badge System (/zeeguu/api/endpoints/badges.py)

Method Endpoint Description
GET /badges Get all badges and progress for the current user
GET /badges/<username> Get badges for another user (requires being their friend)
GET /badges/count_not_shown Count newly achieved badge levels not yet displayed
POST /badges/update_not_shown Mark all unseen badge achievements as shown

User Profile Updates (extended in /zeeguu/api/endpoints/user.py)

  • PUT /update_account now also accepts username, avatar_image_name, avatar_character_color, and avatar_background_color to let users customize their public identity.

Changes to Existing Models

User

  • Added username field — unique, case-sensitive (UTF-8 bin collation), max 50 chars.
  • Auto-generated on account creation in adjective_noun1234 format (e.g. brave_tiger5678), supporting up to ~3.6M unique combinations.
  • Validated via @validates("username").

UserLanguage

  • Added max_streak and max_streak_date columns.
  • max_streak is automatically updated whenever daily_streak exceeds the previous maximum.
  • Used in the friends endpoint to surface each friend's best-ever streak per language.

xXPinkmagicXx and others added 30 commits February 19, 2026 13:27
Previously, the daily streak was updated on ANY authenticated API call
(via the @requires_session decorator), causing streaks to continue even
when users just opened the app without practicing.

Changes:
- Add reset_streak_if_broken() to UserLanguage - resets streak to 0 if
  user hasn't practiced in 2+ days (called on login)
- Change @requires_session to call reset_streak_if_broken() instead of
  update_streak_if_needed() - only resets broken streaks, doesn't increment
- Add update_user_streak() helper function for practice endpoints
- Add streak updates to actual practice endpoints:
  - /report_exercise_outcome (completing exercises)
  - /reading_session_start (starting to read)
  - /listening_session_start (starting to listen)
  - /get_one_translation (translating words while reading)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Saves max_streak and max_streak_date before resetting daily_streak,
so users can see their all-time best even after breaking a streak.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
MODIFY username VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_bin;

-- This is maybe needed
SET SQL_SAFE_UPDATES = 0;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it needed to disable the safe updates?
It was needed the local database, is it also needed in prod?

zeeguu.core.model.db.session.add(user)
zeeguu.core.model.db.session.commit()
return "OK"
except ValueError as e:
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe some comments here that describe the error cases and when/why they happen?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we log the errors too?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the new docs might be enough, but we probably should add the logging



class UserAvatar(db.Model):
"""
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I descriptive comment here

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We looked at some of the other models and they don't really use top-level docs, I think the comments on the methods should be enough

@xXPinkmagicXx xXPinkmagicXx marked this pull request as ready for review April 7, 2026 19:05
Copy link
Copy Markdown
Member

@mircealungu mircealungu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi folks, I started reviewing your code, and spent the last 1h trying to make sense of the DB model for badges. This means that the model is not clear enough.

I left you some comments in there. Please look at them, and propose a new DB model that takes them into account.

You don't have to reimplement just yet. Maybe propose a new design and let's discuss it first. Don't overthink the format — a simple text description is enough:

  • activity_type: id, metric (e.g. TRANSLATED_WORDS), description
  • badge: id, activity_type_id, level, threshold, name ("Word Wizard"), icon
    ... etc.


CREATE TABLE badge (
id INT AUTO_INCREMENT PRIMARY KEY,
code VARCHAR(100) NOT NULL,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's a badge 'code'? Add a comment for the definition of a column with such a generic name.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now i see, so "code" is the thing that you're measuring, e.g. "TRANSLATED_WORDS". it should be called measured_activity or metric.

@@ -0,0 +1,40 @@
-- tools/migrations/26-02-19--add_badge_and_user_badge_tables.sql

CREATE TABLE badge (
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not a badge. it's a "badge category". e.g. "reading streak" is not a badge, it's rather a badge family or a badge category. or badge class. or am I wrong/

UNIQUE(code)
);

CREATE TABLE badge_level (
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the actual. badge I think?

CREATE TABLE badge (
id INT AUTO_INCREMENT PRIMARY KEY,
code VARCHAR(100) NOT NULL,
name VARCHAR(100) NOT NULL,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit too limited, isn't it? Because now all the badges I get for reading will have the same name. Or am I wrong? I don't think with that current design we can do something like:

  • Level 1 (10 words): "Word Curious"
  • Level 2 (100 words): "Translation Enthusiast"
  • Level 3 (500 words): "Lexical Leader"
  • Level 4 (1000 words): "Word Wizard"
  • Level 5 (2500 words): "Polyglot Machine"

This would make a lot of sense, wouldn't it?
And it shouldn't be a big change.

target_value INT NOT NULL,
icon_name VARCHAR(255),
UNIQUE(badge_id, level),
FOREIGN KEY (badge_id) REFERENCES badge(id)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This design works well for counter-based badges, but keep in mind it doesn't naturally support one-off badges like "completed your first article" or "set up your profile." For those you'd have to create a badge with a single level where target_value = 1 which works mechanically but is a hack. Is this intentional? If this is meant to be the badge system long-term, it's worth thinking about now.

FOREIGN KEY (badge_id) REFERENCES badge(id)
);

CREATE TABLE user_badge_level (
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a badge that the user achieves, should be named user_badge. that will be natural if you rename the tables above too.

FOREIGN KEY (badge_level_id) REFERENCES badge_level(id)
);

CREATE TABLE user_badge_progress (
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is also confusing - my impression is that this is not a user_badge progress but rather, a user_metric -- that tracks "how many words has this user translaterd". the fact that the 'metric badges' read from this table does not make this table a badge table. also, this makes again more sense if instead of 'badge_id' you'd say "badge_family_id"... Although, this also feels wrong. I guess this kind of suggests that our top table could become an "activity_type". especially if it loses the name which moves to the individuyal "badge".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants