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
"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.
User A: ⭐⭐⭐⭐⭐
What does this mean? - Good work? Fast delivery? Easy to work with? All of the above? - No context, no categories, no nuance
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
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
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
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
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
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
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')
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
)
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', ...)
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
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)
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)
overall_score = (
avg_quality +
avg_communication +
avg_timeliness +
avg_professionalism
) / 4
WEIGHTS = {
'quality': 0.4,
'communication': 0.2,
'timeliness': 0.3,
'professionalism': 0.1
}
overall_score = sum(
ratings[category] * WEIGHTS[category]
for category in WEIGHTS
)
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
# 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)
<!-- 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>
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 ⭐⭐⭐⭐⭐
# 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
dbbasic ratings create \
--from u042 \
--to u055 \
--project p100 \
--category quality \
--score 5 \
--comment "Excellent work"
# Recompute for specific user
dbbasic ratings recompute u055
# Recompute all users
dbbasic ratings recompute --all
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)
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)
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")
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'],
}
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
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)
Ratings tied to actual projects mean: - Can't rate without working together - Can verify project completion - Context for rating visible
Instead of "good" or "bad", you get: - Great work but slow to respond - Fast delivery but needs more QA - Clear feedback for improvement
Pre-computed scores mean: - No calculation on every page load - Can sort by reputation efficiently - Update async when new ratings added
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:
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
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
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)
}
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.
# 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
# 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
)
# 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")
Auto-discovered admin interface: - Moderate ratings - Flag suspicious patterns - View reputation leaderboards - Export rating data
Version: 1.0 Specification Implementation: Not yet started Expected Size: ~100 lines Dependencies: dbbasic-tsv only
Last Updated: November 2025