major player game data schema update

This commit is contained in:
zman 2023-12-05 10:59:57 -05:00
parent cd89bba8ad
commit 244af4792f
9 changed files with 329 additions and 83 deletions

View File

@ -4,7 +4,7 @@
<title>Simple Frontend</title> <title>Simple Frontend</title>
<script> <script>
function fetchData() { function fetchData() {
fetch('http://localhost:8000/games') fetch('http://localhost:8000/api/games')
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
// Assuming 'data' is an array of games // Assuming 'data' is an array of games

View File

@ -9,15 +9,15 @@ from server.models import (
Game, Game,
Guild, Guild,
Player, Player,
PlayerGameProfile,
WowProfile,
ValorantProfile,
PlayerGuild, PlayerGuild,
PlayerGameData,
GuildGameDataSchema,
WowClasses,
) )
def populate_games(): def populate_games():
games = ["World of Warcraft", "Valorant"] games = ["World of Warcraft", "Counter Strike: Global Offensive"]
for game in games: for game in games:
g = Game.objects.get_or_create(name=game)[0] g = Game.objects.get_or_create(name=game)[0]
@ -47,47 +47,170 @@ def populate_player_guilds():
)[0] )[0]
def populate_player_game_profiles(): def populate_wow_classes():
player_game_profiles = [ wow_classes = [
{"player": "Pixel", "game": "World of Warcraft"}, "Warrior",
{"player": "Zman", "game": "World of Warcraft"}, "Mage",
{"player": "Skip", "game": "Valorant"}, "Rogue",
{"player": "Skip", "game": "World of Warcraft"}, "Priest",
"Warlock",
"Druid",
"Hunter",
"Shaman",
"Paladin",
] ]
for profile in player_game_profiles: for wow_class in wow_classes:
p = PlayerGameProfile.objects.get_or_create( w = WowClasses.objects.get_or_create(name=wow_class)[0]
player=Player.objects.get(name=profile["player"]),
game=Game.objects.get(name=profile["game"]),
)[0]
def populate_wow_profiles(): def populate_guild_game_data_schema():
wow_profiles = [ wow_gamer_schema = {
{"player": "Pixel", "level": 60, "class_name": "Mage"}, "$schema": "http://json-schema.org/draft-07/schema#",
{"player": "Zman", "level": 60, "class_name": "Warrior"}, "type": "object",
{"player": "Skip", "level": 60, "class_name": "Rogue"}, "properties": {
] "playerName": {"type": "string"},
for profile in wow_profiles: "playerDiscordId": {"type": "string"},
p = WowProfile.objects.get_or_create( "playerAvailabilities": {
player_game_profile=PlayerGameProfile.objects.get( "type": "array",
player=Player.objects.get(name=profile["player"]), "items": {
game=Game.objects.get(name="World of Warcraft"), "type": "object",
), "properties": {
level=profile["level"], "day": {"type": "string"},
class_name=profile["class_name"], "time": {"type": "string"},
)[0] "timeZone": {"type": "string"},
},
"required": ["day", "time", "timeZone"],
},
},
"playerRaids": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
},
"required": ["name"],
},
},
"characters": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"class": {"type": "string"},
"level": {"type": "number"},
},
"required": ["name", "class", "level"],
},
},
},
"required": ["playerName"],
}
wow_gamer1 = {
"playerName": "Pixel",
"discordId": "sseagull_caw",
"playerAvailabilities": [
{"day": "Monday", "time": "20:00", "timeZone": "America/New_York"},
{"day": "Wednesday", "time": "20:00", "timeZone": "America/New_York"},
{"day": "Friday", "time": "20:00", "timeZone": "America/New_York"},
],
"playerRaids": [{"name": "Molten Core"}, {"name": "Blackwing Lair"}],
"characters": [
{"name": "Pixelwar", "class": "Warrior", "level": 60},
{"name": "Pixelmage", "class": "Mage", "level": 60},
],
}
wow_gamer2 = {
"playerName": "Zman",
"discordId": "zmanplex",
"playerAvailabilities": [
{"day": "Monday", "time": "20:30", "timeZone": "America/Chicago"},
{"day": "Wednesday", "time": "20:30", "timeZone": "America/Chicago"},
{"day": "Friday", "time": "20:30", "timeZone": "America/Chicago"},
],
"playerRaids": [{"name": "Naxxramas"}, {"name": "Blackwing Lair"}],
"characters": [
{"name": "Zmanlock", "class": "Warlock", "level": 60},
{"name": "Zmanhunt", "class": "Hunter", "level": 60},
],
}
csgo_epic_nerd_schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"playerName": {"type": "string"},
"playerDiscordId": {"type": "string"},
"playerRank": {"type": "string"},
"playerFavoriteMaps": {
"type": "array",
"items": {"type": "string"},
},
"playerFavoriteWeapons": {
"type": "array",
"items": {"type": "string"},
},
"playerAvailabilities": {
"type": "array",
"items": {
"type": "object",
"properties": {
"day": {"type": "string"},
"time": {"type": "string"},
"timeZone": {"type": "string"},
},
"required": ["day", "time", "timeZone"],
},
},
},
"required": ["playerName"],
}
csgo_epic_nerd1 = {
"playerName": "Skip",
"discordId": "tenpull",
"playerRank": "Global Elite",
"playerFavoriteMaps": ["Dust 2", "Mirage"],
"playerFavoriteWeapons": ["AWP", "AK-47"],
"playerAvailabilities": [
{"day": "Monday", "time": "20:00", "timeZone": "America/New_York"},
{"day": "Wednesday", "time": "20:00", "timeZone": "America/New_York"},
{"day": "Friday", "time": "20:00", "timeZone": "America/New_York"},
],
}
def populate_valorant_profiles(): # get or create schema
valorant_profiles = [{"player": "Skip", "rank": "Diamond"}] s = GuildGameDataSchema.objects.get_or_create(
for profile in valorant_profiles: game=Game.objects.get(name="World of Warcraft"),
p = ValorantProfile.objects.get_or_create( guild=Guild.objects.get(name="Gamers"),
player_game_profile=PlayerGameProfile.objects.get( schema=wow_gamer_schema,
player=Player.objects.get(name=profile["player"]), )[0]
game=Game.objects.get(name="Valorant"),
), # get or create player game data
rank=profile["rank"], p1 = PlayerGameData.objects.get_or_create(
)[0] player=Player.objects.get(name=wow_gamer1["playerName"]),
game=Game.objects.get(name="World of Warcraft"),
data=wow_gamer1,
)[0]
p2 = PlayerGameData.objects.get_or_create(
player=Player.objects.get(name=wow_gamer2["playerName"]),
game=Game.objects.get(name="World of Warcraft"),
data=wow_gamer2,
)[0]
# get or create schema
s = GuildGameDataSchema.objects.get_or_create(
game=Game.objects.get(name="Counter Strike: Global Offensive"),
guild=Guild.objects.get(name="Epic Nerds"),
schema=csgo_epic_nerd_schema,
)[0]
# get or create player game data
p1 = PlayerGameData.objects.get_or_create(
player=Player.objects.get(name=csgo_epic_nerd1["playerName"]),
game=Game.objects.get(name="Counter Strike: Global Offensive"),
data=csgo_epic_nerd1,
)[0]
def populate(): def populate():
@ -95,9 +218,9 @@ def populate():
populate_games() populate_games()
populate_guilds() populate_guilds()
populate_players() populate_players()
populate_player_game_profiles() populate_player_guilds()
populate_wow_profiles() populate_wow_classes()
populate_valorant_profiles() populate_guild_game_data_schema()
if __name__ == "__main__": if __name__ == "__main__":

Binary file not shown.

View File

@ -0,0 +1,75 @@
# Generated by Django 4.2.7 on 2023-12-05 14:47
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('server', '0003_remove_player_guild_playerguild'),
]
operations = [
migrations.CreateModel(
name='GuildGameDataSchema',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('schema', models.JSONField()),
('game', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='schemas', to='server.game')),
('guild', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='schemas', to='server.guild')),
],
options={
'verbose_name': 'Guild Game Data Schema',
'verbose_name_plural': 'Guild Game Data Schemas',
},
),
migrations.CreateModel(
name='PlayerGameData',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='date created')),
('data', models.JSONField()),
('game', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='player_profiles', to='server.game')),
('player', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='game_profiles', to='server.player')),
],
options={
'verbose_name': 'Player Game Data',
'verbose_name_plural': 'Player Game Data',
},
),
migrations.CreateModel(
name='WowClasses',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=200)),
('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='date created')),
],
options={
'verbose_name': 'WoW Class',
'verbose_name_plural': 'WoW Classes',
},
),
migrations.RemoveField(
model_name='valorantprofile',
name='player_game_profile',
),
migrations.RemoveField(
model_name='wowprofile',
name='player_game_profile',
),
migrations.AddField(
model_name='playerguild',
name='role',
field=models.CharField(default='member', max_length=200),
),
migrations.DeleteModel(
name='PlayerGameProfile',
),
migrations.DeleteModel(
name='ValorantProfile',
),
migrations.DeleteModel(
name='WowProfile',
),
]

View File

@ -5,6 +5,7 @@ class Guild(models.Model):
""" """
This model is used to store the guild data. This model is used to store the guild data.
""" """
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
tag = models.CharField(max_length=10) tag = models.CharField(max_length=10)
@ -22,6 +23,7 @@ class Player(models.Model):
""" """
This model is used to store the player data. This model is used to store the player data.
""" """
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
creation_date = models.DateTimeField("date created", auto_now_add=True) creation_date = models.DateTimeField("date created", auto_now_add=True)
@ -38,10 +40,13 @@ class PlayerGuild(models.Model):
""" """
This model is used to store the player's guild data. This model is used to store the player's guild data.
""" """
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="guilds") player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="guilds")
guild = models.ForeignKey(Guild, on_delete=models.CASCADE, related_name="players") guild = models.ForeignKey(Guild, on_delete=models.CASCADE, related_name="players")
role = models.CharField(max_length=200) # admin, officer, member, etc. eventually tie to permissions and account stuff role = models.CharField(
max_length=200, default="member"
) # admin, officer, member, etc. eventually tie to permissions and account stuff
creation_date = models.DateTimeField("date created", auto_now_add=True) creation_date = models.DateTimeField("date created", auto_now_add=True)
def __str__(self): def __str__(self):
@ -56,6 +61,7 @@ class Game(models.Model):
""" """
This model is used to store the game data. This model is used to store the game data.
""" """
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
creation_date = models.DateTimeField("date created", auto_now_add=True) creation_date = models.DateTimeField("date created", auto_now_add=True)
@ -74,6 +80,7 @@ class PlayerGameData(models.Model):
Anything can be stored in the data field, but it will Anything can be stored in the data field, but it will
be validated against the schema before being stored. be validated against the schema before being stored.
""" """
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
player = models.ForeignKey( player = models.ForeignKey(
Player, on_delete=models.CASCADE, related_name="game_profiles" Player, on_delete=models.CASCADE, related_name="game_profiles"
@ -100,13 +107,10 @@ class GuildGameDataSchema(models.Model):
Player Game Data will be validated against this schema. Player Game Data will be validated against this schema.
""" """
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
guild = models.ForeignKey( guild = models.ForeignKey(Guild, on_delete=models.CASCADE, related_name="schemas")
Guild, on_delete=models.CASCADE, related_name="schemas" game = models.ForeignKey(Game, on_delete=models.CASCADE, related_name="schemas")
)
game = models.ForeignKey(
Game, on_delete=models.CASCADE, related_name="schemas"
)
schema = models.JSONField() schema = models.JSONField()
def __str__(self): def __str__(self):
@ -122,6 +126,7 @@ class WowClasses(models.Model):
Example of a model that could be used to pre-populate Example of a model that could be used to pre-populate
dropdowns in the UI. dropdowns in the UI.
""" """
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
creation_date = models.DateTimeField("date created", auto_now_add=True) creation_date = models.DateTimeField("date created", auto_now_add=True)
@ -131,4 +136,4 @@ class WowClasses(models.Model):
class Meta: class Meta:
verbose_name = "WoW Class" verbose_name = "WoW Class"
verbose_name_plural = "WoW Classes" verbose_name_plural = "WoW Classes"

View File

@ -1,3 +1,5 @@
from jsonschema import validate
from jsonschema.exceptions import ValidationError
from rest_framework import serializers from rest_framework import serializers
from .models import ( from .models import (
Guild, Guild,
@ -39,11 +41,21 @@ class PlayerGameDataSerializer(serializers.ModelSerializer):
def validate_data(self, value): def validate_data(self, value):
game_id = self.initial_data["game"] game_id = self.initial_data["game"]
# pull schema from guildgameschema try:
schema = GuildGameDataSchema.objects.get(game=game_id) schema_obj = GuildGameDataSchema.objects.get(
# validate data against schema game=game_id, guild=self.initial_data["guild"]
# if valid, return value )
# if not valid, raise serializers.ValidationError("Invalid data") schema = schema_obj.schema
validate(value, schema)
except GuildGameDataSchema.DoesNotExist:
raise serializers.ValidationError(
"Schema does not exist for this guild and game"
)
except ValidationError as e:
raise serializers.ValidationError(e.message)
return value return value
@ -65,4 +77,4 @@ class WowClassesSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = WowClasses model = WowClasses
fields = ["id", "name", "creation_date"] fields = ["id", "name", "creation_date"]
read_only_fields = ["id", "creation_date"] read_only_fields = ["id", "creation_date"]

View File

@ -53,6 +53,7 @@ INSTALLED_APPS = [
"rest_framework", "rest_framework",
"server", "server",
"corsheaders", "corsheaders",
"drf_yasg",
] ]
MIDDLEWARE = [ MIDDLEWARE = [

View File

@ -1,26 +1,56 @@
from django.urls import path, include from django.urls import path, include, re_path
from django.contrib import admin from django.contrib import admin
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from .views import ( from .views import (
GameViewSet, GameViewSet,
GuildViewSet, GuildViewSet,
PlayerViewSet, PlayerViewSet,
PlayerGameProfileViewSet,
WowProfileViewSet,
ValorantProfileViewSet,
hello_world, hello_world,
PlayerGameDataViewSet,
GuildGameDataSchemaViewSet,
PlayerGuildViewSet,
WowClassesViewSet,
) )
router = DefaultRouter() router = DefaultRouter()
router.register(r"games", GameViewSet) router.register(r"games", GameViewSet)
router.register(r"guilds", GuildViewSet) router.register(r"guilds", GuildViewSet)
router.register(r"players", PlayerViewSet) router.register(r"players", PlayerViewSet)
router.register(r"player-game-profiles", PlayerGameProfileViewSet) router.register(r"playergamedata", PlayerGameDataViewSet)
router.register(r"wow-profiles", WowProfileViewSet) router.register(r"guildgamedataschema", GuildGameDataSchemaViewSet)
router.register(r"valorant-profiles", ValorantProfileViewSet) router.register(r"playerguild", PlayerGuildViewSet)
router.register(r"wowclasses", WowClassesViewSet)
schema_view = get_schema_view(
openapi.Info(
title="Guild Data API",
default_version="v1",
description="API for Guild Data",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="yourmom@lole.com"),
license=openapi.License(name="License"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)
urlpatterns = [ urlpatterns = [
path("", hello_world, name="index"), path("", hello_world, name="index"),
path("", include(router.urls)),
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("api/", include(router.urls)),
# Swagger
re_path(
r"^swagger(?P<format>\.json|\.yaml)$",
schema_view.without_ui(cache_timeout=0),
name="schema-json",
),
re_path(
r"^swagger/$",
schema_view.with_ui("swagger", cache_timeout=0),
name="schema-swagger-ui",
),
] ]

View File

@ -4,19 +4,19 @@ from .models import (
Game, Game,
Guild, Guild,
Player, Player,
PlayerGameProfile,
WowProfile,
ValorantProfile,
PlayerGuild, PlayerGuild,
PlayerGameData,
GuildGameDataSchema,
WowClasses,
) )
from .serializers import ( from .serializers import (
GameSerializer, GameSerializer,
GuildSerializer, GuildSerializer,
PlayerSerializer, PlayerSerializer,
PlayerGameProfileSerializer,
WowProfileSerializer,
ValorantProfileSerializer,
PlayerGuildSerializer, PlayerGuildSerializer,
PlayerGameDataSerializer,
GuildGameDataSchemaSerializer,
WowClassesSerializer,
) )
@ -39,21 +39,21 @@ class PlayerViewSet(viewsets.ModelViewSet):
serializer_class = PlayerSerializer serializer_class = PlayerSerializer
class PlayerGameProfileViewSet(viewsets.ModelViewSet): class PlayerGameDataViewSet(viewsets.ModelViewSet):
queryset = PlayerGameProfile.objects.all() queryset = PlayerGameData.objects.all()
serializer_class = PlayerGameProfileSerializer serializer_class = PlayerGameDataSerializer
class WowProfileViewSet(viewsets.ModelViewSet): class GuildGameDataSchemaViewSet(viewsets.ModelViewSet):
queryset = WowProfile.objects.all() queryset = GuildGameDataSchema.objects.all()
serializer_class = WowProfileSerializer serializer_class = GuildGameDataSchemaSerializer
class ValorantProfileViewSet(viewsets.ModelViewSet):
queryset = ValorantProfile.objects.all()
serializer_class = ValorantProfileSerializer
class PlayerGuildViewSet(viewsets.ModelViewSet): class PlayerGuildViewSet(viewsets.ModelViewSet):
queryset = PlayerGuild.objects.all() queryset = PlayerGuild.objects.all()
serializer_class = PlayerGuildSerializer serializer_class = PlayerGuildSerializer
class WowClassesViewSet(viewsets.ModelViewSet):
queryset = WowClasses.objects.all()
serializer_class = WowClassesSerializer