← Back to Modules

dbbasic-ratings Specification

Version: 1.0 Status: Specification Author: DBBasic Project Date: November 2025

Links: - PyPI: https://pypi.org/project/dbbasic-ratings/ (not yet published) - GitHub: https://github.com/askrobots/dbbasic-ratings (not yet published) - Specification: http://dbbasic.com/ratings-spec


Philosophy

"Reputation is the sum of verifiable transactions, not subjective opinions."

The critical insight: Most rating systems are broken because they're either too simple (5 stars) or too complex (100 dimension matrices). The answer is in between: structured categories + aggregate scores + transparency.

Design Principles

  1. Bidirectional: Both parties rate each other (prevents gaming)
  2. Categorical: Rate different aspects (quality, communication, timeliness)
  3. Verifiable: Ratings tied to actual transactions/projects
  4. Transparent: See who rated whom and why
  5. Aggregated: Overall reputation score computed from all ratings

The Problem with Most Rating Systems

Too Simple: "5 Stars"

User A: ⭐⭐⭐⭐⭐

What does this mean? - Good work? Fast delivery? Easy to work with? All of the above? - No context, no categories, no nuance

Too Complex: "LinkedIn Skills Endorsements"

User A has 147 endorsements for:
- Python (43 endorsements)
- JavaScript (28 endorsements)
- Leadership (76 endorsements)

Problems: - Not tied to actual work - Can be gamed (endorsement circles) - No negative feedback possible - Meaningless numbers

The Sweet Spot: Categorical + Aggregated

User A worked with User B on Project X

User B rates User A:
- Quality of Work: 5/5
- Communication: 4/5
- Timeliness: 5/5
- Overall: 4.7/5

Comment: "Excellent developer, slightly slow to respond to messages"

Why this works: 1. Tied to actual project 2. Multiple dimensions 3. Allows nuanced feedback 4. Aggregates to simple score


TSV Storage Schema

ratings.tsv

id  from_user_id    to_user_id  project_id  category    score   max_score   comment created_at  updated_at

Fields: - id: Unique rating ID (e.g., "r001", "r002") - from_user_id: User giving the rating - to_user_id: User being rated - project_id: Related project (ties rating to actual work) - category: What aspect is being rated (quality, communication, timeliness, value, professionalism) - score: Numeric score (1-5 typically) - max_score: Maximum possible score (allows different scales) - comment: Optional text feedback - created_at: When rating was created - updated_at: When rating was last modified

Example Data

id  from_user_id    to_user_id  project_id  category    score   max_score   comment created_at  updated_at
r001    u042    u055    p100    quality 5   5   Excellent code quality  2025-11-01T10:00:00 2025-11-01T10:00:00
r002    u042    u055    p100    communication   4   5   Good but sometimes slow to respond  2025-11-01T10:01:00 2025-11-01T10:01:00
r003    u042    u055    p100    timeliness  5   5   Delivered on time   2025-11-01T10:02:00 2025-11-01T10:02:00
r004    u055    u042    p100    quality 5   5   Clear requirements  2025-11-01T11:00:00 2025-11-01T11:00:00
r005    u055    u042    p100    communication   5   5   Very responsive 2025-11-01T11:01:00 2025-11-01T11:01:00
r006    u055    u042    p100    professionalism 5   5   Great to work with  2025-11-01T11:02:00 2025-11-01T11:02:00

reputation_cache.tsv

Pre-computed reputation scores for fast lookups:

user_id total_ratings   avg_quality avg_communication   avg_timeliness  avg_professionalism avg_value   overall_score   updated_at

Fields: - user_id: User being rated - total_ratings: Number of completed rating sets - avg_quality: Average quality score across all ratings - avg_communication: Average communication score - avg_timeliness: Average timeliness score - avg_professionalism: Average professionalism score - avg_value: Average value score - overall_score: Weighted average of all categories - updated_at: When cache was last updated

Example Data

user_id total_ratings   avg_quality avg_communication   avg_timeliness  avg_professionalism avg_value   overall_score   updated_at
u042    15  4.8 4.9 4.7 5.0 4.8 4.84    2025-11-01T12:00:00
u055    23  4.9 4.6 5.0 4.8 4.7 4.80    2025-11-01T12:00:00

Core API

Python API

from dbbasic_ratings import Ratings

# Initialize
ratings = Ratings('/path/to/data')

# Create rating
rating_id = ratings.create(
    from_user_id='u042',
    to_user_id='u055',
    project_id='p100',
    category='quality',
    score=5,
    max_score=5,
    comment='Excellent work'
)

# Get ratings for a user
user_ratings = ratings.get_ratings_for_user('u055')

# Get ratings by a user
given_ratings = ratings.get_ratings_by_user('u042')

# Get ratings for a project
project_ratings = ratings.get_ratings_for_project('p100')

# Get reputation score
reputation = ratings.get_reputation('u055')
# Returns: {
#   'user_id': 'u055',
#   'total_ratings': 23,
#   'avg_quality': 4.9,
#   'avg_communication': 4.6,
#   'avg_timeliness': 5.0,
#   'overall_score': 4.80
# }

# Check if rating exists
can_rate = ratings.can_rate(
    from_user_id='u042',
    to_user_id='u055',
    project_id='p100'
)

# Update rating
ratings.update(
    rating_id='r001',
    score=4,
    comment='Updated assessment'
)

# Recompute reputation cache
ratings.recompute_reputation('u055')

Standard Rating Categories

For Freelancers (receiving work)

  1. Quality: Technical skill, attention to detail
  2. Communication: Responsiveness, clarity
  3. Timeliness: Meets deadlines, delivers on time
  4. Professionalism: Easy to work with, reliable

For Clients (posting work)

  1. Quality: Clear requirements, good specifications
  2. Communication: Responsive, available for questions
  3. Professionalism: Respectful, pays on time
  4. Value: Fair pricing, reasonable expectations

Flexible Categories

The system allows custom categories per project type:

# Standard categories
FREELANCER_CATEGORIES = ['quality', 'communication', 'timeliness', 'professionalism']
CLIENT_CATEGORIES = ['quality', 'communication', 'professionalism', 'value']

# Custom per project
ratings.create(
    from_user_id='u042',
    to_user_id='u055',
    project_id='p100',
    category='creativity',  # Custom category
    score=5,
    max_score=5
)

Rating Rules

1. Bidirectional Ratings

Both parties must be able to rate each other:

# Client rates freelancer
ratings.create(from_user_id='u042', to_user_id='u055', project_id='p100', ...)

# Freelancer rates client
ratings.create(from_user_id='u055', to_user_id='u042', project_id='p100', ...)

2. Project-Tied Ratings

Ratings must be tied to actual projects:

# Valid: rating tied to project
rating_id = ratings.create(
    from_user_id='u042',
    to_user_id='u055',
    project_id='p100',
    ...
)

# Invalid: no project context
# This would be rejected or flagged

3. One Rating Set Per Project

Each user can only rate another user once per project per category:

# First rating: OK
ratings.create(from_user_id='u042', to_user_id='u055', project_id='p100', category='quality', score=5)

# Second rating same category: UPDATE existing
ratings.create(from_user_id='u042', to_user_id='u055', project_id='p100', category='quality', score=4)
# This updates r001 instead of creating new rating

# Different category: OK
ratings.create(from_user_id='u042', to_user_id='u055', project_id='p100', category='communication', score=4)

4. Rating Periods

Ratings can only be given: - After project completion - Within reasonable time window (e.g., 30 days)

# Check if rating period is valid
can_rate = ratings.can_rate_project(
    project_id='p100',
    from_user_id='u042',
    to_user_id='u055'
)
# Returns: (can_rate: bool, reason: str)

Reputation Score Calculation

Simple Average (Default)

overall_score = (
    avg_quality +
    avg_communication +
    avg_timeliness +
    avg_professionalism
) / 4

Weighted Average (Configurable)

WEIGHTS = {
    'quality': 0.4,
    'communication': 0.2,
    'timeliness': 0.3,
    'professionalism': 0.1
}

overall_score = sum(
    ratings[category] * WEIGHTS[category]
    for category in WEIGHTS
)

Recency Weighting (Optional)

More recent ratings count more:

# Ratings in last 6 months: 100% weight
# Ratings 6-12 months: 75% weight
# Ratings 12-24 months: 50% weight
# Ratings 24+ months: 25% weight

Web Integration

With dbbasic-web

# api/ratings/create.py
from dbbasic_ratings import Ratings
from dbbasic_web.responses import json_response

def handle(request):
    ratings = Ratings('/data')

    rating_id = ratings.create(
        from_user_id=request.user['id'],
        to_user_id=request.form['to_user_id'],
        project_id=request.form['project_id'],
        category=request.form['category'],
        score=int(request.form['score']),
        max_score=5,
        comment=request.form.get('comment', '')
    )

    return json_response({'rating_id': rating_id})

# api/reputation/[user_id].py
def handle(request, user_id):
    ratings = Ratings('/data')
    reputation = ratings.get_reputation(user_id)
    return json_response(reputation)

Display Components

<!-- User reputation badge -->
<div class="reputation-badge">
    <span class="score">4.8</span>
    <span class="stars">⭐⭐⭐⭐⭐</span>
    <span class="count">(23 ratings)</span>
</div>

<!-- Detailed breakdown -->
<div class="reputation-details">
    <div class="category">
        <span class="name">Quality</span>
        <span class="score">4.9/5</span>
        <div class="bar" style="width: 98%"></div>
    </div>
    <div class="category">
        <span class="name">Communication</span>
        <span class="score">4.6/5</span>
        <div class="bar" style="width: 92%"></div>
    </div>
    <div class="category">
        <span class="name">Timeliness</span>
        <span class="score">5.0/5</span>
        <div class="bar" style="width: 100%"></div>
    </div>
</div>

CLI Commands

View reputation

dbbasic ratings reputation u055
# Output:
# User: u055
# Total Ratings: 23
# Overall Score: 4.80/5
#
# Quality:        4.9/5 ⭐⭐⭐⭐⭐
# Communication:  4.6/5 ⭐⭐⭐⭐
# Timeliness:     5.0/5 ⭐⭐⭐⭐⭐
# Professionalism: 4.8/5 ⭐⭐⭐⭐⭐

List ratings

# Ratings received by user
dbbasic ratings list u055 --received

# Ratings given by user
dbbasic ratings list u055 --given

# Ratings for project
dbbasic ratings list --project p100

Create rating

dbbasic ratings create \
    --from u042 \
    --to u055 \
    --project p100 \
    --category quality \
    --score 5 \
    --comment "Excellent work"

Recompute cache

# Recompute for specific user
dbbasic ratings recompute u055

# Recompute all users
dbbasic ratings recompute --all

Admin Interface

Auto-discovered by dbbasic-admin:

# Automatic CRUD interface at /admin/ratings/
# Shows:
# - Recent ratings
# - Filter by user, project, category
# - Reputation leaderboard
# - Flag suspicious ratings (all 5s, gaming patterns)

Advanced Features

1. Rating Verification

Detect gaming attempts:

# Pattern detection
suspicious = ratings.detect_suspicious_patterns('u055')
# Returns:
# - All ratings from same IP
# - Circular rating patterns (A→B→C→A)
# - Too many max scores
# - Rating velocity (too many too fast)

2. Rating Requirements

Require minimum rating counts before displaying:

# Don't show reputation until 3+ ratings
if reputation['total_ratings'] >= 3:
    display_reputation(reputation)
else:
    display_message("Not enough ratings yet")

3. Category Customization

Different project types can use different categories:

CATEGORIES = {
    'software_development': ['quality', 'communication', 'timeliness', 'technical_skill'],
    'design': ['creativity', 'communication', 'timeliness', 'professionalism'],
    'writing': ['quality', 'communication', 'originality', 'timeliness'],
}

Implementation Size

Target: ~100 lines of Python

dbbasic_ratings/
├── __init__.py         # 10 lines
├── ratings.py          # 60 lines (create, get, update ratings)
├── reputation.py       # 20 lines (compute reputation scores)
└── cli.py             # 10 lines (command-line interface)

Total: ~100 lines

Why This Design?

1. Bidirectional = Harder to Game

If both parties rate each other, you can't just create fake accounts to boost your score:

User A creates fake User B
User A rates User B: ⭐⭐⭐⭐⭐
User B rates User A: ⭐⭐⭐⭐⭐

But now User B has only 1 rating (suspicious)
And User A only worked with User B (suspicious)

2. Project-Tied = Verifiable

Ratings tied to actual projects mean: - Can't rate without working together - Can verify project completion - Context for rating visible

3. Categorical = Nuanced

Instead of "good" or "bad", you get: - Great work but slow to respond - Fast delivery but needs more QA - Clear feedback for improvement

4. Cached Reputation = Fast

Pre-computed scores mean: - No calculation on every page load - Can sort by reputation efficiently - Update async when new ratings added

5. Immune to Volume/Value Manipulation

The BayouBid Principle: Reputation cannot be gamed through transaction volume or value.

Traditional rating systems fail because:

Amazon: 1000 5-star reviews on $1 items = looks reputable
Upwork: One $100K project = instant top-tier freelancer
eBay: Buy cheap items, self-rate = boost score

How we prevent this:

Volume Manipulation Prevention

You can't boost reputation by doing many small transactions:

# Each unique user only counts once toward reputation
# 100 transactions with same user = 1 rating relationship

User A completes 100 small jobs with User B
User B rates User A once (after last job or periodically)
Result: User A has 1 rating from User B, not 100

Value Manipulation Prevention

High-value transactions don't count more than low-value ones:

# $10 project rating = same weight as $10,000 project rating
# Transaction value doesn't affect score calculation

rating_weight = 1.0  # Always 1, regardless of project value
# NOT: rating_weight = project_value / 1000

What Actually Matters

  1. Diversity of Partners: Worked with many different people
  2. Quality Consistency: Maintains standards across all projects
  3. Time-Weighted: Recent performance matters more
  4. Categorical Balance: Good across all dimensions (quality, communication, etc.)

Implementation

def calculate_reputation(user_id):
    # Get all unique users who rated this user
    unique_raters = set(rating['from_user_id'] for rating in ratings)

    # Each rater contributes equally (no volume manipulation)
    scores_by_rater = {}
    for rater in unique_raters:
        # Get average of all ratings from this rater
        rater_ratings = [r for r in ratings if r['from_user_id'] == rater]
        scores_by_rater[rater] = sum(r['score'] for r in rater_ratings) / len(rater_ratings)

    # Overall score = average across unique raters (no value manipulation)
    overall_score = sum(scores_by_rater.values()) / len(scores_by_rater)

    return {
        'overall_score': overall_score,
        'unique_raters': len(unique_raters),  # Diversity metric
        'total_ratings': len(ratings)         # Activity metric (separate)
    }

Example Scenarios

Scenario 1: Volume Gaming Attempt

User A does 50 quick jobs with User B (trying to boost score)
Result: Counts as 1 rating relationship
Reputation impact: 1/N where N = total unique partners

Scenario 2: Value Gaming Attempt

User A completes one $50K project
User B completes fifty $1K projects
Both get same score (5.0) from satisfied clients
Result: Same reputation value (5.0), not weighted by $$

Scenario 3: Legitimate High Performer

User C works with 30 different clients
Maintains 4.8+ average across all dimensions
Result: Strong reputation from diversity + consistency

Key Insight: Reputation is about breadth of positive experiences and consistency across dimensions, not volume or value of transactions.


grep-able Data

# Find all ratings for user u055
grep "u055" data/ratings.tsv

# Find all 5-star quality ratings
grep "quality\t5\t5" data/ratings.tsv

# Find ratings for project p100
grep "p100" data/ratings.tsv

# Get user reputation
grep "u055" data/reputation_cache.tsv

Integration with Other Modules

With dbbasic-jobs

# After job completion
job = jobs.get('j100')
if job['status'] == 'completed':
    # Allow both parties to rate
    ratings.enable_rating_period(
        project_id=job['id'],
        user_a=job['client_id'],
        user_b=job['freelancer_id'],
        duration_days=30
    )

With dbbasic-follows

# Show reputation of people you follow
for user_id in follows.get_following(current_user):
    reputation = ratings.get_reputation(user_id)
    print(f"{user_id}: {reputation['overall_score']}/5")

With dbbasic-admin

Auto-discovered admin interface: - Moderate ratings - Flag suspicious patterns - View reputation leaderboards - Export rating data


Status

Version: 1.0 Specification Implementation: Not yet started Expected Size: ~100 lines Dependencies: dbbasic-tsv only


Last Updated: November 2025