Skip to main content

Engine for sending Kudos and Rewards to users, built on top of aiohttp + navigator-api.

Project description

NAV-Rewards

Engine for sending Kudos and Rewards to users, built on top of aiohttp + navigator-api.

PyPI version Python 3.9+ License: MIT

NAV-Rewards is a complete rewards and recognition system that integrates seamlessly with aiohttp applications. It includes:

  • ๐ŸŽ–๏ธ Badge System - User-assignable and automated badges
  • ๐Ÿ† Achievement Tracking - Rule-based achievements with custom conditions
  • ๐Ÿ“Š Attendance Rewards - Time-based and performance-based rewards
  • ๐ŸŽฒ Lottery System - Random user selection with configurable rules
  • ๐ŸŽฏ Workflow Badges - Multi-step achievement tracking
  • ๐Ÿ—ณ๏ธ Nomination System - Employee of the month style voting
  • ๐Ÿ’ฌ Kudos System - Quick peer-to-peer recognition
  • โฐ Scheduler - APScheduler-based automated reward evaluation
  • ๐Ÿ“ง Notifications - Email, MS Teams, and Telegram integration
  • ๐Ÿ“Š Event Manager - RabbitMQ-based event dispatching

๐Ÿ“ฆ Installation

Via pip

pip install nav-rewards

With all optional dependencies

# Install with event manager (RabbitMQ) support
pip install nav-rewards[events]

# Install everything
pip install nav-rewards[all]

From source

git clone https://github.com/phenobarbital/nav-rewards.git
cd nav-rewards
pip install -e .

๐Ÿ—„๏ธ Database Setup

Prerequisites

  • PostgreSQL 12+ (required)
  • Redis (required for caching and session management)
  • RabbitMQ (optional, for event management)

1. Create Database Schema

NAV-Rewards requires a PostgreSQL database with specific schemas. Run the DDL script:

psql -U your_user -d your_database -f rewards/docs/ddl.sql

Or manually execute the schema creation from rewards/docs/ddl.sql:

-- Create the rewards schema
CREATE SCHEMA rewards AUTHORIZATION your_user;

-- Create base tables
CREATE TABLE rewards.reward_categories (...);
CREATE TABLE rewards.reward_groups (...);
CREATE TABLE rewards.reward_types (...);
CREATE TABLE rewards.rewards (...);
CREATE TABLE rewards.users_rewards (...);
CREATE TABLE rewards.points (...);
-- ... (see ddl.sql for complete schema)

2. Database Configuration

Create an environment configuration file at env/.env:

# env/.env

# ============================================
# Database Configuration
# ============================================
DBHOST=localhost
DBPORT=5432
DBNAME=navigator
DBUSER=your_database_user
DBPWD=your_database_password

# ============================================
# Redis Configuration
# ============================================
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=1
REDIS_SESSION_DB=0

# ============================================
# RabbitMQ Configuration (Optional)
# ============================================
RABBITMQ_HOST=localhost
RABBITMQ_PORT=5672
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=guest

# ============================================
# Rewards System
# ============================================
ENABLE_REWARDS=true
REWARD_SCHEDULER=true
REWARD_MIDDLEWARE=true
ENABLE_EVENT_MANAGER=true

# Timezone for scheduling
TIMEZONE=America/New_York

# ============================================
# MS Teams Integration (Optional)
# ============================================
REWARDS_CLIENT_ID=your_ms_teams_client_id
REWARDS_CLIENT_SECRET=your_ms_teams_client_secret
REWARDS_TENANT_ID=your_ms_teams_tenant_id
BOT_REWARDS_ID=your_bot_app_id
BOT_REWARDS_SECRET=your_bot_app_secret

# ============================================
# Notification Services (Optional)
# ============================================
# Email via SMTP
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@example.com
SMTP_PASSWORD=your_email_password

# Telegram Bot
TELEGRAM_BOT_TOKEN=your_telegram_bot_token
TELEGRAM_CHAT_ID=your_telegram_chat_id

# ============================================
# Application Settings
# ============================================
ENVIRONMENT=production
PRODUCTION=true
DEBUG=false

Directory Structure

your-project/
โ”œโ”€โ”€ env/
โ”‚   โ””โ”€โ”€ .env                  # Environment variables
โ”œโ”€โ”€ my_rewards/
โ”‚   โ””โ”€โ”€ rewards.json          # Your reward definitions
โ”œโ”€โ”€ rewards/                  # NAV-Rewards package
โ””โ”€โ”€ app.py                    # Your application

๐Ÿš€ Quick Start

1. Basic Integration

from aiohttp import web
from rewards.engine import RewardsEngine

# Create your aiohttp app
app = web.Application()

# Initialize Rewards Engine
rewards_engine = RewardsEngine(app)

# Setup routes and middleware
rewards_engine.setup()

# Register startup/shutdown handlers
app.on_startup.append(rewards_engine.reward_startup)
app.on_shutdown.append(rewards_engine.reward_shutdown)

if __name__ == '__main__':
    web.run_app(app, host='0.0.0.0', port=8080)

2. Load Rewards from JSON

Create my_rewards/rewards.json:

[
    {
        "reward_id": 1001,
        "reward": "Welcome Badge",
        "description": "Welcome to the platform!",
        "points": 10,
        "reward_type": "Automated Badge",
        "reward_category": "Recognition",
        "reward_group": "Core Badges",
        "multiple": true,
        "icon": "https://example.com/welcome-badge.png"
    }
]

Load rewards on startup:

from rewards.storages import JsonStorage

# In your rewards_engine setup
json_storage = JsonStorage(
    json_file='my_rewards/rewards.json'
)
await rewards_engine.add_storage(json_storage)

๐Ÿ“ Writing Rewards

Reward Structure

Every reward has this basic structure:

{
    "reward_id": 1000,
    "reward": "Badge Name",
    "description": "Badge description",
    "points": 100,
    "reward_type": "Badge Type",
    "reward_category": "Category",
    "reward_group": "Group Name",
    "multiple": false,
    "timeframe": null,
    "icon": "https://example.com/icon.png",
    "rules": [],
    "conditions": {},
    "availability_rule": {},
    "assigner": [],
    "awardee": []
}

Core Fields

Field Type Required Description
reward_id int โœ… Unique identifier
reward string โœ… Display name
description string โŒ Detailed description
points int โŒ Points awarded (default: 10)
reward_type string โŒ Type of reward (see below)
reward_category string โœ… Category (must exist in DB)
reward_group string โŒ Grouping for organization
multiple bool โŒ Can be awarded multiple times (default: false)
timeframe string โŒ Frequency: daily, weekly, monthly, quarterly
icon string โŒ URL or path to badge icon

Reward Types

1. User Badge

Manual badges that users can assign to each other.

{
    "reward_id": 1005,
    "reward": "Employee of the Month",
    "description": "Outstanding performance this month",
    "points": 200,
    "reward_type": "User Badge",
    "reward_category": "Recognition",
    "reward_group": "Monthly Awards",
    "assigner": [
        {
            "job_code": ["MANAGER", "DIRECTOR"]
        }
    ],
    "awardee": [
        {
            "job_code": ["EMPLOYEE", "ASSOCIATE"]
        }
    ]
}

Permissions:

  • assigner: Who can give this badge (by job_code or groups)
  • awardee: Who can receive this badge

2. Automated Badge

Triggered automatically by system events (login, specific actions).

{
    "reward_id": 1007,
    "reward": "Welcome to Navigator",
    "description": "10 points every time you log in",
    "multiple": true,
    "points": 10,
    "reward_type": "Automated Badge",
    "reward_category": "Recognition",
    "reward_group": "Daily Rewards"
}

3. Achievement Badge

Rule-based badges that evaluate conditions and thresholds.

{
    "reward_id": 3000,
    "reward": "Consistency King",
    "description": "Maintained 30-day login streak",
    "points": 1000,
    "reward_type": "Achievement Badge",
    "reward_category": "Engagement",
    "rules": [
        [
            "AchievementRule",
            {
                "function_path": "rewards.functions.engagement.get_login_streak",
                "threshold": 30,
                "operator": "gte"
            }
        ]
    ]
}

4. Computed Badge (Lottery/Random)

Scheduled badges awarded to random users or based on computations.

{
    "reward_id": 1010,
    "reward": "Daily Lottery Winner",
    "description": "Lucky winner of the daily lottery!",
    "points": 500,
    "reward_type": "Computed Badge",
    "reward_category": "Lottery",
    "multiple": true,
    "timeframe": "daily",
    "rules": [
        [
            "RandomUsers",
            {
                "count": 1,
                "exclude_recent_winners": true,
                "recent_winner_days": 7
            }
        ]
    ],
    "job": {
        "trigger": "cron",
        "cron": {
            "hour": 16,
            "minute": 0
        },
        "id": "daily_lottery"
    }
}

5. Workflow Badge

Multi-step achievements that track user progress.

{
    "reward_id": 7000,
    "reward": "Onboarding Champion",
    "description": "Complete the onboarding process",
    "points": 2000,
    "reward_type": "Workflow Badge",
    "reward_category": "Training",
    "multiple": false,
    "workflow": [
        {"step": "First Login"},
        {"step": "Complete Profile"},
        {"step": "Take Training"}
    ],
    "completion_callbacks": [
        "rewards.callbacks.send_completion_email"
    ]
}

6. Nomination Badge

Voting-based awards (Employee of the Month style).

{
    "reward_id": 8000,
    "reward": "Employee of the Month",
    "description": "Voted by peers as Employee of the Month",
    "points": 5000,
    "reward_type": "Nomination Badge",
    "reward_category": "Recognition",
    "nomination_config": {
        "nomination_duration_days": 7,
        "voting_duration_days": 5,
        "allow_self_nomination": false,
        "max_nominations_per_user": 2,
        "max_votes_per_user": 1
    }
}

๐ŸŽฏ Rules System

Rules define the conditions for awarding badges. They're specified in the rules array.

Rule Format

"rules": [
    ["RuleName", {"param1": "value1", "param2": "value2"}]
]

Available Rules

1. AchievementRule

Evaluates a custom function against a threshold.

{
    "rules": [
        [
            "AchievementRule",
            {
                "function_path": "rewards.functions.engagement.get_login_streak",
                "threshold": 30,
                "operator": "gte"
            }
        ]
    ]
}

Operators:

  • gte: greater than or equal
  • gt: greater than
  • lte: less than or equal
  • lt: less than
  • eq: equal
  • ne: not equal

Custom Achievement Functions:

Create rewards/functions/engagement.py:

async def get_login_streak(user, env, conn, **kwargs):
    """Calculate user's login streak in days."""
    query = """
    SELECT COUNT(DISTINCT DATE(login_date))
    FROM auth.user_logins
    WHERE user_id = $1
      AND login_date > NOW() - INTERVAL '30 days'
    """
    result = await conn.fetchval(query, user.user_id)
    return result or 0

2. AttendanceRule

Evaluates attendance patterns.

{
    "rules": [
        [
            "AttendanceRule",
            {
                "count": 30,
                "unit": "day",
                "dataset": "employee_attendance",
                "filter_by": "attendance_date",
                "match": "perfect",
                "only_weekdays": true
            }
        ]
    ]
}

Parameters:

  • count: Number of time units
  • unit: day, week, month
  • period_type: fixed or rolling (default: fixed)
  • match: perfect (100%) or partial (with deviation)
  • deviation: Allowed missed days for partial match
  • only_weekdays: Count only Monday-Friday

Examples:

// Perfect monthly attendance
{
    "reward": "Perfect Attendance - Monthly",
    "rules": [
        [
            "AttendanceRule",
            {
                "count": 1,
                "unit": "month",
                "dataset": "employee_attendance",
                "match": "perfect",
                "only_weekdays": true
            }
        ]
    ]
}

// Good attendance (max 2 missed days in 30 days)
{
    "reward": "Excellent Attendance",
    "rules": [
        [
            "AttendanceRule",
            {
                "count": 30,
                "unit": "day",
                "dataset": "employee_attendance",
                "match": "partial",
                "deviation": 2,
                "only_weekdays": true
            }
        ]
    ]
}

// Rolling 30-day perfect attendance
{
    "reward": "Rolling 30-Day Streak",
    "rules": [
        [
            "AttendanceRule",
            {
                "count": 30,
                "unit": "day",
                "period_type": "rolling",
                "dataset": "employee_attendance",
                "match": "perfect",
                "only_weekdays": true
            }
        ]
    ]
}

3. RandomUsers

Selects random users for lottery-style rewards.

{
    "rules": [
        [
            "RandomUsers",
            {
                "count": 5,
                "filters": {
                    "departments": ["Sales", "Marketing"],
                    "job_codes": ["ASSOCIATE"],
                    "min_tenure_days": 90
                },
                "exclude_recent_winners": true,
                "recent_winner_days": 30
            }
        ]
    ]
}

4. Birthday

Awards badge on user's birthday.

{
    "reward": "Happy Birthday!",
    "rules": [["Birthday"]]
}

5. EmploymentDuration

Awards badge on employment anniversary.

{
    "reward": "1 Year Anniversary",
    "rules": [["EmploymentDuration"]]
}

6. WorkAnniversary

Similar to EmploymentDuration but with more options.

{
    "reward": "5 Year Milestone",
    "rules": [
        [
            "WorkAnniversary",
            {
                "years": 5
            }
        ]
    ]
}

โฐ Scheduling Rewards

Use the job field to schedule automatic reward evaluation.

Cron-based Scheduling

{
    "reward_id": 1010,
    "reward": "Daily Lottery",
    "reward_type": "Computed Badge",
    "rules": [["RandomUsers", {"count": 1}]],
    "job": {
        "trigger": "cron",
        "cron": {
            "hour": 16,
            "minute": 0
        },
        "id": "daily_lottery_4pm",
        "name": "Daily Lottery at 4 PM",
        "timezone": "America/New_York"
    }
}

Interval-based Scheduling

{
    "job": {
        "trigger": "interval",
        "interval": {
            "hours": 2
        },
        "id": "check_every_2_hours"
    }
}

Date-based (One-time)

{
    "job": {
        "trigger": "date",
        "run_date": "2025-12-25 10:00:00",
        "id": "christmas_special",
        "skip_past_dates": true
    }
}

Advanced Cron Examples

// Every Monday at 9 AM
{
    "cron": {
        "day_of_week": "mon",
        "hour": 9,
        "minute": 0
    }
}

// Last day of month at 4 PM
{
    "cron": {
        "day": "last",
        "hour": 16,
        "minute": 0
    }
}

// Every weekday at noon
{
    "cron": {
        "day_of_week": "mon-fri",
        "hour": 12,
        "minute": 0
    }
}

๐ŸŽฏ Availability Rules

Control when badges are available.

Date-based Availability

{
    "reward": "Holiday Special",
    "availability_rule": {
        "start_date": "12-01",
        "end_date": "12-31"
    }
}

Time-based Availability

{
    "reward": "Early Bird Special",
    "availability_rule": {
        "dow": [1, 2, 3, 4, 5],
        "start_time": "06:00:00",
        "end_time": "09:00:00"
    }
}

dow: Day of week (0=Monday, 6=Sunday)


๐Ÿ“Š Complete Examples

Example 1: Monthly Perfect Attendance

{
    "reward_id": 5001,
    "reward": "Perfect Attendance - Monthly",
    "description": "Perfect weekday attendance for the entire month",
    "points": 1000,
    "reward_type": "Achievement Badge",
    "reward_category": "Attendance",
    "reward_group": "Monthly Badges",
    "multiple": true,
    "timeframe": "monthly",
    "icon": "https://example.com/perfect-attendance.png",
    "rules": [
        [
            "AttendanceRule",
            {
                "count": 1,
                "unit": "month",
                "dataset": "employee_attendance",
                "filter_by": "attendance_date",
                "match": "perfect",
                "only_weekdays": true
            }
        ]
    ]
}

Example 2: Monthly Mega Lottery

{
    "reward_id": 1013,
    "reward": "Monthly Mega Lottery",
    "description": "Big monthly lottery - 5 lucky winners!",
    "points": 5000,
    "reward_type": "Computed Badge",
    "reward_category": "Lottery",
    "reward_group": "Monthly Rewards",
    "multiple": true,
    "timeframe": "monthly",
    "icon": "https://example.com/lottery.png",
    "rules": [
        [
            "RandomUsers",
            {
                "count": 5,
                "exclude_recent_winners": true,
                "recent_winner_days": 30
            }
        ]
    ],
    "job": {
        "trigger": "cron",
        "cron": {
            "day": "last",
            "hour": 16,
            "minute": 0
        },
        "id": "monthly_mega_lottery",
        "name": "Monthly Mega Lottery"
    }
}

Example 3: Login Streak Achievement

{
    "reward_id": 3000,
    "reward": "Consistency King",
    "description": "Maintained 30-day login streak",
    "points": 1000,
    "reward_type": "Achievement Badge",
    "reward_category": "Engagement",
    "reward_group": "Streaks",
    "icon": "https://example.com/streak.png",
    "rules": [
        [
            "AchievementRule",
            {
                "function_path": "rewards.functions.engagement.get_login_streak",
                "threshold": 30,
                "operator": "gte"
            }
        ]
    ]
}

Example 4: Onboarding Workflow

{
    "reward_id": 7002,
    "reward": "Sales Mastery",
    "description": "Complete the sales training program",
    "points": 2000,
    "reward_type": "Workflow Badge",
    "reward_category": "Training",
    "reward_group": "Certifications",
    "multiple": true,
    "auto_enroll": true,
    "auto_evaluation": true,
    "workflow": [
        {
            "step": "Product Knowledge",
            "condition": {
                "function_path": "rewards.functions.training.check_product_knowledge",
                "threshold": 80,
                "operator": "gte"
            }
        },
        {
            "step": "Sales Techniques",
            "condition": {
                "function_path": "rewards.functions.training.check_sales_training",
                "threshold": 100,
                "operator": "eq"
            }
        },
        {
            "step": "First Sale",
            "condition": {
                "function_path": "rewards.functions.sales.check_first_sale",
                "threshold": 1,
                "operator": "gte"
            }
        }
    ],
    "completion_callbacks": [
        "rewards.callbacks.training.send_certificate"
    ]
}

Example 5: Employee of the Month Nomination

{
    "reward_id": 8001,
    "reward": "Employee of the Month - Sales",
    "description": "Voted by peers as top sales performer",
    "points": 5000,
    "reward_type": "Nomination Badge",
    "reward_category": "Recognition",
    "reward_group": "Monthly Awards",
    "multiple": true,
    "timeframe": "monthly",
    "nomination_config": {
        "nomination_duration_days": 7,
        "voting_duration_days": 5,
        "allow_self_nomination": false,
        "max_nominations_per_user": 3,
        "max_votes_per_user": 1,
        "eligible_nominators": {
            "job_codes": ["ASSOCIATE", "MANAGER"],
            "min_tenure_days": 90
        },
        "eligible_nominees": {
            "job_codes": ["ASSOCIATE"],
            "departments": ["Sales"]
        }
    },
    "job": {
        "trigger": "cron",
        "cron": {
            "day": 1,
            "hour": 9,
            "minute": 0
        },
        "id": "eotm_sales_start"
    }
}

๐Ÿ”ง Advanced Configuration

Custom Achievement Functions

Create custom functions in rewards/functions/:

# rewards/functions/custom.py

async def check_sales_target(user, env, conn, target=100, **kwargs):
    """Check if user met sales target."""
    query = """
    SELECT COUNT(*)
    FROM sales.transactions
    WHERE user_id = $1
      AND created_at >= $2
      AND created_at < $3
    """
    count = await conn.fetchval(
        query,
        user.user_id,
        env.start_date,
        env.end_date
    )
    return count >= target

Register in your reward:

{
    "rules": [
        [
            "AchievementRule",
            {
                "function_path": "rewards.functions.custom.check_sales_target",
                "threshold": 100,
                "operator": "gte",
                "function_params": {
                    "target": 100
                }
            }
        ]
    ]
}

Multiple Rules (AND Logic)

All rules must pass:

{
    "reward": "Elite Performer",
    "rules": [
        [
            "AchievementRule",
            {
                "function_path": "rewards.functions.sales.get_sales_count",
                "threshold": 100,
                "operator": "gte"
            }
        ],
        [
            "AttendanceRule",
            {
                "count": 1,
                "unit": "month",
                "match": "perfect"
            }
        ]
    ]
}

Message Templates

Use Jinja2 templates in messages:

{
    "reward": "Top Seller",
    "message": "Congratulations {{user.display_name}}! You made {{args.sales_count}} sales this month and ranked #{{args.rank}}!",
    "rules": [
        ["BestSeller"]
    ]
}

๐Ÿ“– API Endpoints

NAV-Rewards automatically creates REST endpoints:

GET    /rewards/api/v1/rewards              # List all rewards
GET    /rewards/api/v1/rewards/{id}         # Get specific reward
POST   /rewards/api/v1/rewards              # Create reward
PUT    /rewards/api/v1/rewards/{id}         # Update reward
DELETE /rewards/api/v1/rewards/{id}         # Delete reward

GET    /rewards/api/v1/users_rewards        # List user rewards
POST   /api/v1/badge_assign                 # Assign a badge

GET    /rewards/api/v1/reward_categories    # List categories
GET    /rewards/api/v1/reward_groups        # List groups
GET    /rewards/api/v1/reward_types         # List types

GET    /kudos/api/v1/user_kudos             # List kudos
POST   /kudos/api/v1/user_kudos             # Send kudos
GET    /kudos/api/v1/kudos_tags             # List kudos tags

๐Ÿค– MS Teams Bot Integration

NAV-Rewards includes a Teams bot for badge assignment:

from rewards.bot.badge import BadgeBot

# In your rewards engine setup
badge_bot = BadgeBot(
    bot_name="BadgeBot",
    id='badgebot',
    app=app,
    client_id=REWARDS_CLIENT_ID,
    client_secret=REWARDS_CLIENT_SECRET
)
badge_bot.setup(app)

Bot Commands:

  • /badge - Award a badge to a user
  • /kudos - Send kudos to a colleague

๐Ÿ“š Additional Resources


๐Ÿค Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


๐Ÿ’ฐ Support

If you find this project useful, please consider:


๐Ÿ‘จโ€๐Ÿ’ป Author

Jesus Lara Gimenez Email: jesuslarag@gmail.com GitHub: @phenobarbital


Built with โค๏ธ using aiohttp and Navigator

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nav_rewards-0.1.4.tar.gz (470.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nav_rewards-0.1.4-py3-none-any.whl (226.4 kB view details)

Uploaded Python 3

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page