mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-18 20:39:36 +02:00
feat: Add achievements app with models, admin, and management command for seeding data
This commit is contained in:
parent
e93909e7ea
commit
a00d2abe0d
12 changed files with 160 additions and 0 deletions
0
backend/server/achievements/__init__.py
Normal file
0
backend/server/achievements/__init__.py
Normal file
9
backend/server/achievements/admin.py
Normal file
9
backend/server/achievements/admin.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from django.contrib import admin
|
||||
from allauth.account.decorators import secure_admin_login
|
||||
from achievements.models import Achievement, UserAchievement
|
||||
|
||||
admin.autodiscover()
|
||||
admin.site.login = secure_admin_login(admin.site.login)
|
||||
|
||||
admin.site.register(Achievement)
|
||||
admin.site.register(UserAchievement)
|
6
backend/server/achievements/apps.py
Normal file
6
backend/server/achievements/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AchievementsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'achievements'
|
0
backend/server/achievements/management/__init__.py
Normal file
0
backend/server/achievements/management/__init__.py
Normal file
|
@ -0,0 +1,66 @@
|
|||
import json
|
||||
from django.core.management.base import BaseCommand
|
||||
from achievements.models import Achievement
|
||||
|
||||
US_STATE_CODES = [
|
||||
'US-AL', 'US-AK', 'US-AZ', 'US-AR', 'US-CA', 'US-CO', 'US-CT', 'US-DE',
|
||||
'US-FL', 'US-GA', 'US-HI', 'US-ID', 'US-IL', 'US-IN', 'US-IA', 'US-KS',
|
||||
'US-KY', 'US-LA', 'US-ME', 'US-MD', 'US-MA', 'US-MI', 'US-MN', 'US-MS',
|
||||
'US-MO', 'US-MT', 'US-NE', 'US-NV', 'US-NH', 'US-NJ', 'US-NM', 'US-NY',
|
||||
'US-NC', 'US-ND', 'US-OH', 'US-OK', 'US-OR', 'US-PA', 'US-RI', 'US-SC',
|
||||
'US-SD', 'US-TN', 'US-TX', 'US-UT', 'US-VT', 'US-VA', 'US-WA', 'US-WV',
|
||||
'US-WI', 'US-WY'
|
||||
]
|
||||
|
||||
ACHIEVEMENTS = [
|
||||
{
|
||||
"name": "First Adventure",
|
||||
"key": "achievements.first_adventure",
|
||||
"type": "adventure_count",
|
||||
"description": "Log your first adventure!",
|
||||
"condition": {"type": "adventure_count", "value": 1},
|
||||
},
|
||||
{
|
||||
"name": "Explorer",
|
||||
"key": "achievements.explorer",
|
||||
"type": "adventure_count",
|
||||
"description": "Log 10 adventures.",
|
||||
"condition": {"type": "adventure_count", "value": 10},
|
||||
},
|
||||
{
|
||||
"name": "Globetrotter",
|
||||
"key": "achievements.globetrotter",
|
||||
"type": "country_count",
|
||||
"description": "Visit 5 different countries.",
|
||||
"condition": {"type": "country_count", "value": 5},
|
||||
},
|
||||
{
|
||||
"name": "American Dream",
|
||||
"key": "achievements.american_dream",
|
||||
"type": "country_count",
|
||||
"description": "Visit all 50 states in the USA.",
|
||||
"condition": {"type": "country_count", "items": US_STATE_CODES},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Seeds the database with predefined achievements"
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
for achievement_data in ACHIEVEMENTS:
|
||||
achievement, created = Achievement.objects.update_or_create(
|
||||
name=achievement_data["name"],
|
||||
defaults={
|
||||
"description": achievement_data["description"],
|
||||
"condition": json.dumps(achievement_data["condition"]),
|
||||
"type": achievement_data["type"],
|
||||
"key": achievement_data["key"],
|
||||
},
|
||||
)
|
||||
if created:
|
||||
self.stdout.write(self.style.SUCCESS(f"✅ Created: {achievement.name}"))
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING(f"🔄 Updated: {achievement.name}"))
|
39
backend/server/achievements/migrations/0001_initial.py
Normal file
39
backend/server/achievements/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Generated by Django 5.0.8 on 2025-02-04 04:34
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Achievement',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, unique=True)),
|
||||
('description', models.TextField()),
|
||||
('icon', models.ImageField(blank=True, null=True, upload_to='achievements/')),
|
||||
('condition', models.JSONField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserAchievement',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('earned_at', models.DateTimeField(auto_now_add=True)),
|
||||
('achievement', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='achievements.achievement')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('user', 'achievement')},
|
||||
},
|
||||
),
|
||||
]
|
0
backend/server/achievements/migrations/__init__.py
Normal file
0
backend/server/achievements/migrations/__init__.py
Normal file
33
backend/server/achievements/models.py
Normal file
33
backend/server/achievements/models.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
from django.db import models
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
VALID_ACHIEVEMENT_TYPES = [
|
||||
"adventure_count",
|
||||
"country_count",
|
||||
]
|
||||
|
||||
class Achievement(models.Model):
|
||||
"""Stores all possible achievements"""
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
key = models.CharField(max_length=255, unique=True) # Used for frontend lookups, e.g. "achievements.first_adventure"
|
||||
type = models.CharField(max_length=255) # adventure_count, country_count, etc.
|
||||
description = models.TextField()
|
||||
icon = models.ImageField(upload_to="achievements/", null=True, blank=True)
|
||||
condition = models.JSONField() # Stores rules like {"type": "adventure_count", "value": 10}
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class UserAchievement(models.Model):
|
||||
"""Tracks which achievements a user has earned"""
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
achievement = models.ForeignKey(Achievement, on_delete=models.CASCADE)
|
||||
earned_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ("user", "achievement") # Prevent duplicates
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user.username} - {self.achievement.name}"
|
3
backend/server/achievements/tests.py
Normal file
3
backend/server/achievements/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
3
backend/server/achievements/views.py
Normal file
3
backend/server/achievements/views.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
|
@ -61,6 +61,7 @@ INSTALLED_APPS = (
|
|||
'users',
|
||||
'integrations',
|
||||
'django.contrib.gis',
|
||||
'achievements',
|
||||
# 'widget_tweaks',
|
||||
# 'slippers',
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue