From fea7c0eeb9b611a4395836e58d7a894b0d64f59c Mon Sep 17 00:00:00 2001 From: Neill Cox Date: Sun, 9 Mar 2025 17:58:06 +1100 Subject: [PATCH] Tidy up after a long absence --- .gitignore | 2 + django_gurps/settings.py | 41 +- docs/design.md | 29 + .../__pycache__/apps.cpython-312.pyc | Bin 489 -> 0 bytes .../__pycache__/forms.cpython-312.pyc | Bin 1500 -> 0 bytes gurps_character/admin.py | 7 +- gurps_character/forms.py | 12 +- .../management/commands/clear_data.py | 32 +- gurps_character/migrations/0001_initial.py | 165 +- .../migrations/0002_alter_gm_gm.py | 25 + .../migrations/0002_gurpscharacter_details.py | 18 - ..._campaign_gm_alter_gm_campaign_and_more.py | 67 + gurps_character/migrations/0003_gamesystem.py | 28 - ...gurpscharacter_player_campaign_and_more.py | 57 - .../migrations/0005_campaign_gm.py | 20 - .../__pycache__/0001_initial.cpython-312.pyc | Bin 981 -> 0 bytes ...002_gurpscharacter_details.cpython-312.pyc | Bin 781 -> 0 bytes .../0003_gamesystem.cpython-312.pyc | Bin 1034 -> 0 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 177 -> 0 bytes gurps_character/models.py | 512 +++-- .../templates/campaigns/details.html | 21 + gurps_character/templates/campaigns/list.html | 18 + gurps_character/templates/home.html | 8 +- gurps_character/views.py | 25 +- gurps_fixture.json | 1705 +++++++++++++++++ pyproject.toml | 14 + requirements.txt | 13 +- requirements_dev.txt | 1 + 28 files changed, 2444 insertions(+), 376 deletions(-) create mode 100644 docs/design.md delete mode 100644 gurps_character/__pycache__/apps.cpython-312.pyc delete mode 100644 gurps_character/__pycache__/forms.cpython-312.pyc create mode 100644 gurps_character/migrations/0002_alter_gm_gm.py delete mode 100644 gurps_character/migrations/0002_gurpscharacter_details.py create mode 100644 gurps_character/migrations/0003_alter_campaign_gm_alter_gm_campaign_and_more.py delete mode 100644 gurps_character/migrations/0003_gamesystem.py delete mode 100644 gurps_character/migrations/0004_gurpscharacter_player_campaign_and_more.py delete mode 100644 gurps_character/migrations/0005_campaign_gm.py delete mode 100644 gurps_character/migrations/__pycache__/0001_initial.cpython-312.pyc delete mode 100644 gurps_character/migrations/__pycache__/0002_gurpscharacter_details.cpython-312.pyc delete mode 100644 gurps_character/migrations/__pycache__/0003_gamesystem.cpython-312.pyc delete mode 100644 gurps_character/migrations/__pycache__/__init__.cpython-312.pyc create mode 100644 gurps_character/templates/campaigns/details.html create mode 100644 gurps_character/templates/campaigns/list.html create mode 100644 gurps_fixture.json create mode 100644 pyproject.toml create mode 100644 requirements_dev.txt diff --git a/.gitignore b/.gitignore index 7a0a341..03baf9d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,7 @@ db.sqlite3 .hidden_notes.txt env/ *pyc +*.py[cod] __pycache__/ xxxthemexxx/ +gurps_vars.sh diff --git a/django_gurps/settings.py b/django_gurps/settings.py index 2e29cbc..7cf1c57 100644 --- a/django_gurps/settings.py +++ b/django_gurps/settings.py @@ -21,29 +21,28 @@ BASE_DIR = Path(__file__).resolve().parent.parent # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "django-insecure-x%rs&gy9iphje-l+!^!g@s@w$4@oc0^honvnr-!edwm+uujiu2" +SECRET_KEY = ( + "django-insecure-x%rs&gy9iphje-l+!^!g@s@w$4@oc0^honvnr-!edwm+uujiu2" +) # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [ - "gcs.neill.id.au", - "localhost", - "127.0.0.1", - ] + "gcs.neill.id.au", + "localhost", + "127.0.0.1", +] CSRF_TRUSTED_ORIGINS = [ "https://gcs.neill.id.au", - ] +] AUTHENTICATION_BACKENDS = [ - # Needed to log in by username in Django admin, regardless of `allauth` - 'django.contrib.auth.backends.ModelBackend', - + "django.contrib.auth.backends.ModelBackend", # `allauth` specific authentication methods, such as login by email - 'allauth.account.auth_backends.AuthenticationBackend', - + "allauth.account.auth_backends.AuthenticationBackend", ] # Application definition @@ -55,20 +54,18 @@ INSTALLED_APPS = [ "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", - - 'allauth', - 'allauth.account', - 'allauth.socialaccount', + "allauth", + "allauth.account", + "allauth.socialaccount", # ... include the providers you want to enable: - 'allauth.socialaccount.providers.amazon', - 'allauth.socialaccount.providers.apple', - 'allauth.socialaccount.providers.google', - + "allauth.socialaccount.providers.amazon", + "allauth.socialaccount.providers.apple", + "allauth.socialaccount.providers.google", "django.contrib.staticfiles", # "tailwind", # "theme", "django_browser_reload", - 'django_bootstrap_icons', + "django_bootstrap_icons", ] MIDDLEWARE = [ @@ -97,7 +94,7 @@ TEMPLATES = [ "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", - 'django.template.context_processors.request', + "django.template.context_processors.request", ], }, }, @@ -169,7 +166,7 @@ DATABASES = { } } -TAILWIND_APP_NAME = 'theme' +TAILWIND_APP_NAME = "theme" INTERNAL_IPS = [ "127.0.0.1", diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 0000000..2b642a7 --- /dev/null +++ b/docs/design.md @@ -0,0 +1,29 @@ +# Design Notes + +## Stories + +As a user I want to upload a character so my GM can see it + +As a user I want to update a character so my GM can see it + +As a GM I want to invite players + +As a GM I want to review characters and pssibly appreove them + +As a GM I want to remove players from my campaign + +As a GM I want to send feedback on a character to a players + +As an admin I want to create campaigns + +As an admin I want to invite people to GM a campaign + +As an admin I want to remove players + +As an admin I want to remove campaigns + +As an admin I want to remove GM from a campaign + +As a player I want to add session notes to a campaign. (It would be really cool if multiple players could edit these notes. Bonus points for uploading images) + + diff --git a/gurps_character/__pycache__/apps.cpython-312.pyc b/gurps_character/__pycache__/apps.cpython-312.pyc deleted file mode 100644 index 7e1d579ed90706be9d2d112755c3e9c84c44a91c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 489 zcmZ8dze~eF6n=L}rS^x46hW|qi;yK*6-1~NyOqvajx%>nE0!?-9>IeWHfB{1SBIF|k z7zV)b9GJz?t5weVcxF4bPg6K3-|QtxU&iBU7_9kBpsHAP3Qi6_V&G$je2Zb`8a zcLteCbbmq>4N|U3mb)&yrg11;cI8HraiQJwY1qqBc{$~R<=deFkzkD)I<>q`NK7M6 zNZueMk}MM@ZxM2nQBiev86VS3q=aUvB;$h1HoX^E8LhuPZz3b^#eux_MoM1uAl2T8 zD=lLxyiyb?mV5sscr;10o6M9kvUqF&&}{&i!*c)l&E8u!5AH9N`6>EC$I6@4od&Fo qwI#e|vSM({sH&fq_nfJn%2jO~{jv~3?{N6Nfl>GIHs(Y*J4-^_da ze(mjz0KhNfKQPF^pJEf3+I9}_0@wrvB{p*6E7_zZ3}*_KO6P9dPmDsrlX+o0+y zXb+Q$u>h)L+j7g#g{s5|Jr|p-K|FQJ;U(7~Ug8tWE>NpdaLPA1cX*z3a(zW2|8SBB zMRgRr4g;fPb^_qhN}CCw(smnVg@jQF?uBAckkhd`is8;KuVzt10W3>( ziH2q+fG4^*wYu~eH2j?$1-M2eTX=6gfm-}l!Df2Jadg|_)2wzb>r`}0zy1Cl zeM+2M)QhfD)+x(7lzE{A#@&Kb<`RI~!2e=rSygAElF!A>YstGatOe7~(_gP$HS~h( z6FN3c>613IT&B;vOtGKk;hm4O6TVWvojmO$R&9C8{W*Hy3%jghR=AaChT^FUmOalrg<2Jhi{{{Q0ImSn zbzMImPtQ0dmM-U=rSv3siY#Ao)02$5PT8{4v}l#59dmk~&$(%THS;qTxAGOn(|&)a z=Bi%rLaV`?O~vS2xmYp=JbHV*T{W7yI$Sz2xxLr&}L1E>1O0 zO&`iqBH4r#PBvv|eSaVIA$IDI==tBG=MO;CQJ*Wb8^7Mrt~X_(j{b8LL9G7(lA3v{ z^{r2AW;e1=`iK3vcX#908rrp{Dyf-c_eB8T4vh_92kKY`I|Jv(uF5+ZWBh`g@9xIn zcRYOCA14esLXndsFuAC82eyo<)8r=-v}vxVDjBjDzc(kUD;o5c!%GJ7BJJmNxSKUX zo@Sb5tHeyxiT4d)0l#eF_9BY}@1xER+0K^1yhX6EXLda7@pW&rvxOC8yB*~)@Lawae+stj` L{sJLae2{+u72aAH diff --git a/gurps_character/admin.py b/gurps_character/admin.py index bf5197e..f699e60 100644 --- a/gurps_character/admin.py +++ b/gurps_character/admin.py @@ -1,8 +1,13 @@ from django.contrib import admin # Register your models here. -from .models import GURPSCharacter, GameSystem, Campaign +from .models import GURPSCharacter, GameSystem, Campaign, GM, Player, CampaignPlayer admin.site.register(GURPSCharacter) admin.site.register(GameSystem) admin.site.register(Campaign) + + +admin.site.register(GM) +admin.site.register(Player) +admin.site.register(CampaignPlayer) diff --git a/gurps_character/forms.py b/gurps_character/forms.py index 772fe64..710585c 100644 --- a/gurps_character/forms.py +++ b/gurps_character/forms.py @@ -2,7 +2,7 @@ import json from django import forms from django.core.exceptions import ValidationError - + def validate_file(value): if value.size > 10**6: raise ValidationError("File too large") @@ -13,16 +13,16 @@ def validate_file(value): raise ValidationError("Not a GCS file - json decode") try: - version = data['version'] + version = data["version"] except KeyError: - import bpdb;bpdb.set_trace() raise ValidationError("Not a GCS file - key error") if version < 4: raise ValidationError( - f"The file version ({version}) is too old. Please use a newer version (5.20.3) of GCS." - ) + f"The file version ({version}) is too old. Please use a newer " + "version (5.20.3) of GCS." + ) class UploadFileForm(forms.Form): - file = forms.FileField(validators=[validate_file]) + file = forms.FileField(validators=[validate_file]) diff --git a/gurps_character/management/commands/clear_data.py b/gurps_character/management/commands/clear_data.py index 358f7c7..e1ee7dd 100644 --- a/gurps_character/management/commands/clear_data.py +++ b/gurps_character/management/commands/clear_data.py @@ -1,24 +1,24 @@ -from django.contrib.auth.models import User +from django.db import connection from django.core.management import BaseCommand -def create_users(): - user = User.objects.create_user( - username="neill", - is_superuser=True, - first_name="Neill", - last_name="Cox", - email="neill@neill.id.au", - is_staff=True, - is_active=True, - date_joined="2024-07-31T00:00:00.000Z", - password="password", - ) - # user.save() +def drop_tables(): + with connection.cursor() as cursor: + for table in [ + "gurps_character", + "gurps_character_campaign", + "gurps_character_campaign_gm", + "gurps_character_campaignplayer", + "gurps_character_gm", + "gurps_character_player", + "gurps_character_gamesystem", + "gurps_character_gurpscharacter"]: + cursor.execute(f'DROP TABLE IF EXISTS {table} cascade') + cursor.execute("DELETE FROM django_migrations WHERE app = 'gurps_character'") class Command(BaseCommand): - help = "Load some initial data" + help = "Drop all the tables!" def handle(self, *args, **options): - create_users() + drop_tables() diff --git a/gurps_character/migrations/0001_initial.py b/gurps_character/migrations/0001_initial.py index 581e4bd..4e9e0bf 100644 --- a/gurps_character/migrations/0001_initial.py +++ b/gurps_character/migrations/0001_initial.py @@ -1,14 +1,120 @@ -# Generated by Django 5.0.1 on 2024-01-08 10:25 +# Generated by Django 5.0.6 on 2024-09-07 01:08 +import django.db.models.deletion +from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): + initial = True - dependencies = [] + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] operations = [ + migrations.CreateModel( + name="GameSystem", + 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(null=True)), + ], + ), + migrations.CreateModel( + name="Campaign", + 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(null=True)), + ( + "game_system", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="gurps_character.gamesystem", + ), + ), + ], + ), + migrations.CreateModel( + name="GM", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "campaign", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="campaign", + to="gurps_character.campaign", + ), + ), + ( + "gm", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + migrations.AddField( + model_name="campaign", + name="gm", + field=models.ManyToManyField( + related_name="GM", to="gurps_character.gm" + ), + ), + migrations.CreateModel( + name="Player", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "status", + models.CharField(max_length=255, unique=True), + ), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), migrations.CreateModel( name="GURPSCharacter", fields=[ @@ -23,6 +129,61 @@ class Migration(migrations.Migration): ), ("uuid", models.CharField(max_length=128, unique=True)), ("name", models.CharField(max_length=255, unique=True)), + ("details", models.JSONField()), + ( + "campaign", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="gurps_character.campaign", + ), + ), + ( + "player", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="gurps_character.player", + ), + ), + ], + ), + migrations.CreateModel( + name="CampaignPlayer", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "status", + models.CharField( + choices=[ + ("I", "Invited"), + ("A", "Accepted"), + ("D", "Declined"), + ] + ), + ), + ( + "campaign", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="gurps_character.campaign", + ), + ), + ( + "player", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="gurps_character.player", + ), + ), ], ), ] diff --git a/gurps_character/migrations/0002_alter_gm_gm.py b/gurps_character/migrations/0002_alter_gm_gm.py new file mode 100644 index 0000000..fcbf6fa --- /dev/null +++ b/gurps_character/migrations/0002_alter_gm_gm.py @@ -0,0 +1,25 @@ +# Generated by Django 5.0.6 on 2024-09-07 01:22 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("gurps_character", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name="gm", + name="gm", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/gurps_character/migrations/0002_gurpscharacter_details.py b/gurps_character/migrations/0002_gurpscharacter_details.py deleted file mode 100644 index f1f69a9..0000000 --- a/gurps_character/migrations/0002_gurpscharacter_details.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.0.1 on 2024-01-10 07:52 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("gurps_character", "0001_initial"), - ] - - operations = [ - migrations.AddField( - model_name="gurpscharacter", - name="details", - field=models.JSONField(default={}), - preserve_default=False, - ), - ] diff --git a/gurps_character/migrations/0003_alter_campaign_gm_alter_gm_campaign_and_more.py b/gurps_character/migrations/0003_alter_campaign_gm_alter_gm_campaign_and_more.py new file mode 100644 index 0000000..3a20bac --- /dev/null +++ b/gurps_character/migrations/0003_alter_campaign_gm_alter_gm_campaign_and_more.py @@ -0,0 +1,67 @@ +# Generated by Django 5.1.7 on 2025-03-08 09:56 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("gurps_character", "0002_alter_gm_gm"), + ] + + operations = [ + migrations.AlterField( + model_name="campaign", + name="gm", + field=models.ManyToManyField( + blank=True, related_name="GM", to="gurps_character.gm" + ), + ), + migrations.AlterField( + model_name="gm", + name="campaign", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="campaign", + to="gurps_character.campaign", + ), + ), + migrations.AlterField( + model_name="player", + name="status", + field=models.CharField( + choices=[ + ("I", "Invited"), + ("A", "Accepted"), + ("D", "Declined"), + ("R", "Requested"), + ] + ), + ), + migrations.CreateModel( + name="CampaignNotes", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("timestamp", models.DateTimeField(auto_now_add=True)), + ("notes", models.TextField(null=True)), + ( + "campaign", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="gurps_character.campaign", + ), + ), + ], + ), + ] diff --git a/gurps_character/migrations/0003_gamesystem.py b/gurps_character/migrations/0003_gamesystem.py deleted file mode 100644 index b26628c..0000000 --- a/gurps_character/migrations/0003_gamesystem.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.0.1 on 2024-01-16 07:12 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("gurps_character", "0002_gurpscharacter_details"), - ] - - operations = [ - migrations.CreateModel( - name="GameSystem", - 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(null=True)), - ], - ), - ] diff --git a/gurps_character/migrations/0004_gurpscharacter_player_campaign_and_more.py b/gurps_character/migrations/0004_gurpscharacter_player_campaign_and_more.py deleted file mode 100644 index e89b49f..0000000 --- a/gurps_character/migrations/0004_gurpscharacter_player_campaign_and_more.py +++ /dev/null @@ -1,57 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-14 09:14 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("gurps_character", "0003_gamesystem"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AddField( - model_name="gurpscharacter", - name="player", - field=models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - to=settings.AUTH_USER_MODEL, - ), - ), - migrations.CreateModel( - name="Campaign", - 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(null=True)), - ( - "game_system", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="gurps_character.gamesystem", - ), - ), - ], - ), - migrations.AddField( - model_name="gurpscharacter", - name="campaign", - field=models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - to="gurps_character.campaign", - ), - ), - ] diff --git a/gurps_character/migrations/0005_campaign_gm.py b/gurps_character/migrations/0005_campaign_gm.py deleted file mode 100644 index c9d8a19..0000000 --- a/gurps_character/migrations/0005_campaign_gm.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-14 09:25 - -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("gurps_character", "0004_gurpscharacter_player_campaign_and_more"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AddField( - model_name="campaign", - name="gm", - field=models.ManyToManyField(to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/gurps_character/migrations/__pycache__/0001_initial.cpython-312.pyc b/gurps_character/migrations/__pycache__/0001_initial.cpython-312.pyc deleted file mode 100644 index b48ce8a1ea44a16e458bc512fe636c13c1a42744..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 981 zcmZ`%&ubGw82x5*~OD@AUGu>|7AL;C_G@dF{ z@YtJze}S|IFW!3>p#dRbPJ*Z20;vdIl-V>^p?-(q&CYu}-+SMD8XwmI06ub*UuKFRNUevhI0Ho~zz~DH<LX5s7nOZWe4ht1T@n${uT_2NJN%^jy3a_oFRgU zLNZ6{p)x$(lE_%fE?&aU5_F^;N`IL8f0?;|nT~v9jC2mF>+NxKf@>=yzZ-m__Vmfx z`tr8T>_$u(mt0ci(!&R)!VNo#L)>80jw#`}7IS@@wed6B=2}FVYkTes%8logZH5uW zf$dYSB#BE-_eQzeyV#?F6K``h3EZ88az)Tif29N_ta5eBr5=e^%^c6-^e-559b-Qv zi6^ju@lIlULWnURa|1VaZI2s-wrD_T&~RzQ#9(=DIc;`DjF5?3xpCih?j>bZcDh%%*;iq* z(ksm$RJ*15PHDb(%k0hF?#&8tre!9}{Y=&VU?WAJ9Eq5zsr(%9sKQ4W#R8R7? zUSaCe@BJyGG;x->qUn9PP*h(&Vl$(@R!xO#Lw5^gGYvA$ZrJ&>prza!BBSix;aHZ~ kEC3*3={f-PWrWZV6_9zRmXUg(ck^?d{M>gaBQ;h32GhO!hyVZp diff --git a/gurps_character/migrations/__pycache__/0002_gurpscharacter_details.cpython-312.pyc b/gurps_character/migrations/__pycache__/0002_gurpscharacter_details.cpython-312.pyc deleted file mode 100644 index 79d2c458572f70ba7d395d91be55bd2e6f330360..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 781 zcmZ8f&ubGw6n?Y6vb&@;E<)ARODM`BWVby_!ApfwW5L^AhV9NY-LSu$-AHp&=)q%e z-aLwu{u$mp1%!k-37)(KQV~4K%qE8V4#W4&dvE4_-^|yI4I2R9%h3n=5dr)VWEJ`n zEcOB5JrE!$ffNNORRRTp0?&Xb7eG{s2F9vs9b>h|fjZJ${ih(V!tFHfv#^TutmHa&T^wg|6^9Aml<9T0L1-1mN$Da1Ps3qS z9k?2|r7)a@Ddn16_0yOpgtrPtOUhnTysYlI7B{i1#F*O{r#TrW0yi;!H4GEcEf}E% z%?Qn+n3ha9%gv{R9EekyP{ggnmoIvfb2~5S(&Nvt5OV)S-dUbfFN^YX@0jHS8dart zOj(&{Vd9ZNnDuik#d-fZ;H@9b^L>A>uKH&IUOLb%MofGHxd>2-UvmN9&Bt?9xnsOJ zn%iJ)zq6-idt$a{TaRY9@6Vjh<-@7dnK+$Kd*7Vy>_O++(k*+gHXP&aA-lUeu;*&r zs;BId6RBUSuCLq@WwQ33&}u%ZP26W~01!`KZUewvMF{;i0J+yj6B(EGw6!y7?fil! HGGzB(D%i^x diff --git a/gurps_character/migrations/__pycache__/0003_gamesystem.cpython-312.pyc b/gurps_character/migrations/__pycache__/0003_gamesystem.cpython-312.pyc deleted file mode 100644 index 9c7536a67def79c368aa2ee210a501c55e28dd07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1034 zcmZ8f&ubGw6n?Y2S(8oLCLxUmwZ;;u3(_ol(^Ak%f?`Xdf{@Fy%}h5N_eVOrk;ao^ z4<38dqbEU*0O6&J6h#$JU z-0Dz1yy3!${|*55;Ya-bNECANn`a{h5;xu}gZM%}zA(&OAIUIZ80MA^DudioKesfz zr4CEChl`w?C1UgWQ7oRxj@^4q8MW>nRNDzU`)d&_t`G&S^bTz8SnLhT|qDZVo9 n%qlGb0H06)8UTz$gwSspka{NPk$j*G(xra7^b7Jx4zvFOqw5c8 diff --git a/gurps_character/migrations/__pycache__/__init__.cpython-312.pyc b/gurps_character/migrations/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 24fb52f54efc3a9635c95eaaeec38b738260e036..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177 zcmZ9Cu?@m75JkTUNC+u|&~OVNMnKu%WI0yiD6!Gmk;o(zOu#G|g9%W&RLGRP;=Nz| zxhxL=!1L4QzO(*Wwg4WOa8%b9WCLKypS7Yk43@kJcd8_G)}%<4l^DG4sqVCI0ym@F vBK^#!)uK(ZLW65WCl`DqX6IdEKHa8ue;z_T_Vyw>0180N<5MW5qyX>*A~P>v diff --git a/gurps_character/models.py b/gurps_character/models.py index 8b04fe6..40211ae 100644 --- a/gurps_character/models.py +++ b/gurps_character/models.py @@ -1,5 +1,6 @@ from django.db import models from django.contrib.auth.models import User + # Create your models here. @@ -12,120 +13,194 @@ class GameSystem(models.Model): class GM(models.Model): - gm = models.ForeignKey(User, on_delete=models.CASCADE) - campaign = models.ForeignKey("Campaign", null=True, on_delete=models.CASCADE, related_name='campaign') + gm = models.ForeignKey(User, on_delete=models.CASCADE, null=True) + campaign = models.ForeignKey( + "Campaign", + null=True, + blank=True, + on_delete=models.CASCADE, + related_name="campaign", + ) + + def __str__(self): + return self.campaign.name + " - " + self.gm.get_full_name() class Player(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) - status = models.CharField(max_length=255, unique=True) + status = models.CharField( + choices={"I": "Invited", "A": "Accepted", "D": "Declined", "R": "Requested"} + ) -# -# class Players(models.Model): -# player = models.ForeignKey(User, on_delete=models.CASCADE) -# status = models.CharField(max_length=255, unique=True) + def __str__(self): + return self.user.get_full_name() + " - " + self.get_status_display() class Campaign(models.Model): name = models.CharField(max_length=255, unique=True) description = models.TextField(null=True) - game_system = models.ForeignKey(GameSystem, on_delete=models.CASCADE) - gm = models.ManyToManyField('GM', related_name='GM') - player = models.ManyToManyField('Player') + game_system = models.ForeignKey( + GameSystem, on_delete=models.CASCADE + ) + gm = models.ManyToManyField("GM", related_name="GM", blank=True) + # player = models.ManyToManyField('Player') def __str__(self): return self.name +class CampaignPlayer(models.Model): + campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE) + player = models.ForeignKey( + Player, on_delete=models.CASCADE + ) # TODO: Check this behaviour + status = models.CharField( + choices={"I": "Invited", "A": "Accepted", "D": "Declined"} + ) # This should be one of invited, accepted, rejected? + + def __str__(self): + return self.campaign.name + " - " + self.player.user.get_full_name() + " - " + self.get_status_display() + +class CampaignNotes(models.Model): + campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE) + timestamp = models.DateTimeField(auto_now_add=True) + notes = models.TextField(null=True) + class GURPSCharacter(models.Model): uuid = models.CharField(max_length=128, unique=True) name = models.CharField(max_length=255, unique=True) - player = models.ForeignKey(Player, null=True, on_delete=models.CASCADE) - campaign = models.ForeignKey(Campaign, null=True, on_delete=models.CASCADE) + player = models.ForeignKey( + Player, null=True, on_delete=models.CASCADE + ) + campaign = models.ForeignKey( + Campaign, null=True, on_delete=models.CASCADE + ) details = models.JSONField() - # owner => user - # campaign => campaign def __str__(self): - return self.name + " Player " + str(self.player) + " Campaign " + str(self.campaign) + return ( + str(self.name) + + " Player " + + str(self.player) + + " Campaign " + + str(self.campaign) + ) def adv_points(self): - return sum([a['calc']['points'] for a in self.details.get('advantages', []) if a['calc']['points'] > 0]) + return sum( + [ + a["calc"]["points"] + for a in self.details.get("advantages", []) + if a["calc"]["points"] > 0 + ] + ) def disadv_points(self): - return sum([a['calc']['points'] for a in self.details.get('advantages', []) if a['calc']['points'] < -1]) + return sum( + [ + a["calc"]["points"] + for a in self.details.get("advantages", []) + if a["calc"]["points"] < -1 + ] + ) def quirks_points(self): - return sum([a['calc']['points'] for a in self.details.get('advantages', []) if a['calc']['points'] == -1]) + return sum( + [ + a["calc"]["points"] + for a in self.details.get("advantages", []) + if a["calc"]["points"] == -1 + ] + ) def attr_points(self): - return sum([a['calc']['points'] for a in self.details['attributes']]) + return sum( + [a["calc"]["points"] for a in self.details["attributes"]] + ) def skills_points(self): - return sum([s['points'] for s in self.details.get('skills', [])]) + return sum( + [s["points"] for s in self.details.get("skills", [])] + ) def spells_points(self): - return sum([s['points'] for s in self.details.get('spells', [])]) + return sum( + [s["points"] for s in self.details.get("spells", [])] + ) def race_points(self): - return sum([s['points'] for s in self.details.get('race', [])]) # Are we sure? + # Are we sure? + return sum([s["points"] for s in self.details.get("race", [])]) def unspent_points(self): - return self.details['total_points'] - self.adv_points() - \ - self.disadv_points() - self.attr_points() - \ - self.skills_points() - self.spells_points() - \ - self.race_points() - self.quirks_points() + return ( + self.details["total_points"] + - self.adv_points() + - self.disadv_points() + - self.attr_points() + - self.skills_points() + - self.spells_points() + - self.race_points() + - self.quirks_points() + ) def get_primary_attr(self, attr_id): - return [a['calc'] for a in self.details['attributes'] if a['attr_id'] == attr_id][0] + return [ + a["calc"] + for a in self.details["attributes"] + if a["attr_id"] == attr_id + ][0] def st(self): - return self.get_primary_attr('st') + return self.get_primary_attr("st") def iq(self): - return self.get_primary_attr('iq') + return self.get_primary_attr("iq") def dx(self): - return self.get_primary_attr('dx') + return self.get_primary_attr("dx") def ht(self): - return self.get_primary_attr('ht') + return self.get_primary_attr("ht") def will(self): - return self.get_primary_attr('will') + return self.get_primary_attr("will") def fright_check(self): - return self.get_primary_attr('fright_check') + return self.get_primary_attr("fright_check") def per(self): - return self.get_primary_attr('per') + return self.get_primary_attr("per") def vision(self): - return self.get_primary_attr('vision') + return self.get_primary_attr("vision") def hearing(self): - return self.get_primary_attr('hearing') + return self.get_primary_attr("hearing") def taste_smell(self): - return self.get_primary_attr('taste_smell') + return self.get_primary_attr("taste_smell") def touch(self): - return self.get_primary_attr('touch') + return self.get_primary_attr("touch") def basic_move(self): - return self.get_primary_attr('basic_move') + return self.get_primary_attr("basic_move") def basic_speed(self): - return self.get_primary_attr('basic_speed') + return self.get_primary_attr("basic_speed") def hp(self): - return self.get_primary_attr('hp') + return self.get_primary_attr("hp") def fp(self): - return self.get_primary_attr('fp') + return self.get_primary_attr("fp") def weight_carried(self): - items = [i['calc']['extended_weight'] for i in self.details['equipment']] + items = [ + i["calc"]["extended_weight"] + for i in self.details["equipment"] + ] total_weight = 0 for i in items: @@ -150,59 +225,77 @@ class GURPSCharacter(models.Model): dodge = self.details["calc"]["dodge"][0] basic_move = self.basic_move()["value"] return [ - { - "max_load": round(self.basic_lift()), - "move": basic_move, - "dodge": dodge, - "current": "current" * (enc_level == 0) - }, - { - "max_load": round(self.basic_lift()) * 2, - "move": basic_move - 2, - "dodge": dodge - 1, - "current": "current" * (enc_level == 1) - }, - { - "max_load": round(self.basic_lift()) * 3, - "move": basic_move - 3, - "dodge": dodge - 2, - "current": "current" * (enc_level == 2) - }, - { - "max_load": round(self.basic_lift()) * 6, - "move": basic_move - 4, - "dodge": dodge - 3, - "current": "current" * (enc_level == 3) - }, - { - "max_load": round(self.basic_lift()) * 10, - "move": basic_move - 5, - "dodge": dodge - 4, - "current": "current" * (enc_level == 4) - }, - ] - + { + "max_load": round(self.basic_lift()), + "move": basic_move, + "dodge": dodge, + "current": "current" * (enc_level == 0), + }, + { + "max_load": round(self.basic_lift()) * 2, + "move": basic_move - 2, + "dodge": dodge - 1, + "current": "current" * (enc_level == 1), + }, + { + "max_load": round(self.basic_lift()) * 3, + "move": basic_move - 3, + "dodge": dodge - 2, + "current": "current" * (enc_level == 2), + }, + { + "max_load": round(self.basic_lift()) * 6, + "move": basic_move - 4, + "dodge": dodge - 3, + "current": "current" * (enc_level == 3), + }, + { + "max_load": round(self.basic_lift()) * 10, + "move": basic_move - 5, + "dodge": dodge - 4, + "current": "current" * (enc_level == 4), + }, + ] + def basic_lift(self): return float(self.details["calc"]["basic_lift"].split()[0]) def weight_unit(self): - return self.details['settings']['default_weight_units'] + return self.details["settings"]["default_weight_units"] def lift_table(self): return [ - {"value": round(self.basic_lift()), "label": "Basic Lift"}, - {"value": round(self.basic_lift()) * 2, "label": "One-Handed Lift"}, - {"value": round(self.basic_lift()) * 8, "label": "Two-Handed Lift"}, - {"value": round(self.basic_lift()) * 12, "label": "Shove &ersand; Knock Over"}, - {"value": round(self.basic_lift()) * 24, "label": "Running Shove & Knock Over"}, - {"value": round(self.basic_lift()) * 15, "label": "Carry on Back"}, - {"value": round(self.basic_lift()) * 50, "label": "Shift Slightly"}, - ] + {"value": round(self.basic_lift()), "label": "Basic Lift"}, + { + "value": round(self.basic_lift()) * 2, + "label": "One-Handed Lift", + }, + { + "value": round(self.basic_lift()) * 8, + "label": "Two-Handed Lift", + }, + { + "value": round(self.basic_lift()) * 12, + "label": "Shove &ersand; Knock Over", + }, + { + "value": round(self.basic_lift()) * 24, + "label": "Running Shove & Knock Over", + }, + { + "value": round(self.basic_lift()) * 15, + "label": "Carry on Back", + }, + { + "value": round(self.basic_lift()) * 50, + "label": "Shift Slightly", + }, + ] def reaction_modifiers(self): modifiers = [] - for a in self.details.get('advantages', []): + for a in self.details.get("advantages", []): if "features" in a: for f in a["features"]: if f["type"] == "reaction_bonus": @@ -213,7 +306,7 @@ class GURPSCharacter(models.Model): def conditional_modifiers(self): modifiers = [] - for a in self.details.get('advantages', []): + for a in self.details.get("advantages", []): if "features" in a: for f in a["features"]: if f["type"] == "conditional_modifier": @@ -222,10 +315,24 @@ class GURPSCharacter(models.Model): return modifiers def weapons(self): - return [w for w in self.details['equipment'] if "weapons" in w] + \ - [a for a in self.details.get("advantages", []) if "weapons" in a] + \ - [a for a in self.details.get("traits", []) if "weapons" in a] + \ - [s for s in self.details.get("spells", []) if "weapons" in s] + return ( + [w for w in self.details["equipment"] if "weapons" in w] + + [ + a + for a in self.details.get("advantages", []) + if "weapons" in a + ] + + [ + a + for a in self.details.get("traits", []) + if "weapons" in a + ] + + [ + s + for s in self.details.get("spells", []) + if "weapons" in s + ] + ) def melee_weapons(self): mw = [] @@ -237,16 +344,18 @@ class GURPSCharacter(models.Model): else: name = item["name"] mw.append( - { - "name": name, - "usage": weapon["usage"], - "skill_level": weapon["calc"].get("level", 0), - "parry": weapon["calc"].get("parry", "No"), - "block": weapon["calc"].get("block", "No"), - "damage": weapon["calc"]["damage"], - "reach": weapon["calc"].get("reach", ""), - "strength": weapon.get("strength", " ") - } + { + "name": name, + "usage": weapon["usage"], + "skill_level": weapon["calc"].get( + "level", 0 + ), + "parry": weapon["calc"].get("parry", "No"), + "block": weapon["calc"].get("block", "No"), + "damage": weapon["calc"]["damage"], + "reach": weapon["calc"].get("reach", ""), + "strength": weapon.get("strength", " "), + } ) return mw @@ -260,7 +369,7 @@ class GURPSCharacter(models.Model): wpn_range = float(wpn_range[1:]) wpn_range = wpn_range * self.st()["value"] wpn_range = str(wpn_range) - wpn_range = wpn_range[:wpn_range.index(".")] + wpn_range = wpn_range[: wpn_range.index(".")] new_ranges.append(wpn_range) return "/".join(new_ranges) @@ -276,19 +385,21 @@ class GURPSCharacter(models.Model): else: name = item["name"] rw.append( - { - "name": name, - "bulk": weapon.get("bulk", " "), - "usage": weapon.get("usage", " "), - "skill_level": weapon["calc"]["level"], - "damage": weapon["calc"]["damage"], - "strength": weapon.get("strength", " "), - "acc": weapon.get("accuracy", 0), - "range": muscle_range(weapon.get("range", " ")), - "rof": weapon.get("rate_of_fire", " "), - "shots": weapon.get("shots", " "), - "recoil": weapon.get("recoil", " ") - } + { + "name": name, + "bulk": weapon.get("bulk", " "), + "usage": weapon.get("usage", " "), + "skill_level": weapon["calc"]["level"], + "damage": weapon["calc"]["damage"], + "strength": weapon.get("strength", " "), + "acc": weapon.get("accuracy", 0), + "range": muscle_range( + weapon.get("range", " ") + ), + "rof": weapon.get("rate_of_fire", " "), + "shots": weapon.get("shots", " "), + "recoil": weapon.get("recoil", " "), + } ) return rw @@ -299,8 +410,15 @@ class GURPSCharacter(models.Model): cost = advantage["calc"]["points"] name = advantage["name"] - if "categories" in advantage and "Language" in advantage["categories"]: - levels = [m for m in advantage['modifiers'] if "disabled" not in m] + if ( + "categories" in advantage + and "Language" in advantage["categories"] + ): + levels = [ + m + for m in advantage["modifiers"] + if "disabled" not in m + ] notes = "" for level in levels: @@ -315,82 +433,97 @@ class GURPSCharacter(models.Model): elif "notes" in advantage: notes = advantage["notes"] elif "modifiers" in advantage: - notes = [m for m in advantage['modifiers'] if m["cost"] == cost][0]["name"] + notes = [ + m + for m in advantage["modifiers"] + if m["cost"] == cost + ][0]["name"] else: notes = "" - traits.append({"name": name, "notes": notes, "points": cost, "reference": advantage["reference"]}) + traits.append( + { + "name": name, + "notes": notes, + "points": cost, + "reference": advantage["reference"], + } + ) return traits def spells(self): def get_casting_details(spell_details): - level = spell_details['calc']['level'] + level = spell_details["calc"]["level"] if level < 10: descr = ( - "Ritual: Need both hands and both feet " - "free, and must speak .Time: 2x." - ) + "Ritual: Need both hands and both feet " + "free, and must speak .Time: 2x." + ) elif level < 15: descr = ( - "Ritual: Must speak a few quiet words " - "and make a gesture." - ) + "Ritual: Must speak a few quiet words " + "and make a gesture." + ) elif level < 20: - descr = ( - "Ritual: Must speak a word or two or make " - "a small gesture. May move one yard per second " - "while concentrating. Cost: -1." - ) - elif level < 25: - descr = "Ritual: None. Time: / 2 (round up). Cost: -2." - elif level < 30: descr = ( - "Ritual: None. Time: / 4 (round up). Cost: -3." - ) + "Ritual: Must speak a word or two or make " + "a small gesture. May move one yard per second " + "while concentrating. Cost: -1." + ) + elif level < 25: + descr = "Ritual: None. Time: / 2 (round up). Cost: -2." + elif level < 30: + descr = "Ritual: None. Time: / 4 (round up). Cost: -3." else: delta = int((level - 25) / 5) power = 2 + delta # divisor = 2**power cost = 3 + delta - descr = ( - f"Ritual: None. Time: / {power} round up. Cost: " - f"-{cost}" - ) + descr = ( + f"Ritual: None. Time: / { + power} round up. Cost: " + f"-{cost}" + ) return descr spells = [] - for spell in self.details.get('spells', []): + for spell in self.details.get("spells", []): notes = ( - f"{get_casting_details(spell)}
Class: {spell['spell_class']}; " - f"Cost: {spell['casting_cost']}; Maintain: {spell['maintenance_cost']};" - f" Time: {spell['casting_time']}; Duration: {spell['duration']}; " - ) + f"{get_casting_details(spell)}
Class: { + spell['spell_class']}; " + f"Cost: {spell['casting_cost']}; Maintain: { + spell['maintenance_cost']};" + f" Time: {spell['casting_time']}; Duration: { + spell['duration']}; " + ) spells.append( { - 'name': spell["name"], + "name": spell["name"], "college": ", ".join(spell["college"]), - "level": spell["calc"]["level"], + "level": spell["calc"]["level"], "rsl": spell["calc"]["rsl"], "points": spell["points"], "reference": spell["reference"], - "notes": notes + "notes": notes, } ) return spells def skills(self): skills = [] - for skill in self.details.get('skills', []): - skills.append({ - 'name': skill["name"], - "level": skill["calc"]["level"], - "rsl": skill["calc"]["rsl"], - "points": skill["points"], - "reference": skill["reference"] - }) + for skill in self.details.get("skills", []): + skills.append( + { + "name": skill["name"], + "level": skill["calc"]["level"], + "rsl": skill["calc"]["rsl"], + "points": skill["points"], + "reference": skill["reference"], + } + ) return skills def equipment(self): @@ -399,49 +532,52 @@ class GURPSCharacter(models.Model): for item_details in parent["children"]: equipment_details = { - "name": item_details["description"], - "uses": "", - "tech_level": item_details["tech_level"], - "lc": "", - "value": item_details["value"], - "weight": item_details["weight"], - "quantity": item_details.get("quantity", 1), - "ref": item_details["reference"], - "ext_weight": item_details["calc"]["extended_weight"], - "ext_value": item_details["calc"]["extended_value"], - "notes": item_details.get("notes", ""), - "equipped": item_details["equipped"], - "level": level - } + "name": item_details["description"], + "uses": "", + "tech_level": item_details["tech_level"], + "lc": "", + "value": item_details["value"], + "weight": item_details["weight"], + "quantity": item_details.get("quantity", 1), + "ref": item_details["reference"], + "ext_weight": item_details["calc"][ + "extended_weight" + ], + "ext_value": item_details["calc"]["extended_value"], + "notes": item_details.get("notes", ""), + "equipped": item_details["equipped"], + "level": level, + } children.append(equipment_details) if "children" in item_details: children += get_children(item_details, level + 1) return children - equipment = self.details['equipment'] + + equipment = self.details["equipment"] equipment_list = [] for item in equipment: equipment_list.append( - { - "name": item["description"], - "uses": "", - "tech_level": item["tech_level"], - "lc": "", - "level": 0, - "value": item["value"], - "weight": item["weight"], - "quantity": item.get("quantity", 1), - "ref": item.get("reference", ""), - "ext_weight": item["calc"]["extended_weight"], - "ext_value": item["calc"]["extended_value"], - "notes": item.get("notes", ""), - "equipped": item["equipped"] - } + { + "name": item["description"], + "uses": "", + "tech_level": item["tech_level"], + "lc": "", + "level": 0, + "value": item["value"], + "weight": item["weight"], + "quantity": item.get("quantity", 1), + "ref": item.get("reference", ""), + "ext_weight": item["calc"]["extended_weight"], + "ext_value": item["calc"]["extended_value"], + "notes": item.get("notes", ""), + "equipped": item["equipped"], + } ) - if 'children' in item: + if "children" in item: equipment_list += get_children(item) return equipment_list @@ -449,7 +585,9 @@ class GURPSCharacter(models.Model): def hit_locations(self): try: # v2 - return self.details["settings"]["hit_locations"]["locations"] + return self.details["settings"]["hit_locations"][ + "locations" + ] except KeyError: # v4 return self.details["settings"]["body_type"]["locations"] diff --git a/gurps_character/templates/campaigns/details.html b/gurps_character/templates/campaigns/details.html new file mode 100644 index 0000000..c783536 --- /dev/null +++ b/gurps_character/templates/campaigns/details.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% block content %} +

{{ campaign.name }}

+ +

Players

+ +
    + {% for player in campaign.campaignplayer_set.all %} +
  • {{ player }}
  • + {% endfor %} +
+ +

Characters

+ + +{% endblock %} + diff --git a/gurps_character/templates/campaigns/list.html b/gurps_character/templates/campaigns/list.html new file mode 100644 index 0000000..21953e0 --- /dev/null +++ b/gurps_character/templates/campaigns/list.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} +{% block content %} + + +Upload new character +
+ {% csrf_token %} + {{ form }} + +
+{% endblock %} diff --git a/gurps_character/templates/home.html b/gurps_character/templates/home.html index 9668191..48be4cb 100644 --- a/gurps_character/templates/home.html +++ b/gurps_character/templates/home.html @@ -8,11 +8,11 @@ {% if user.is_authenticated %}

Welcome {% if user.first_name %}{{ user.first_name }}{% else %}{{ user.username }}{% endif %}

- {% if user.campaign_set.all %} + {% if user.gm_set.all %}

Campaigns you run

@@ -20,7 +20,7 @@

Characters you play

    - {% for character in user.gurpscharacter_set.all %} + {% for character in user.player.gurpscharacter_set.all %}
  • {{ character.name }} in {{ character.campaign }} {% bs_icon 'eye' %} diff --git a/gurps_character/views.py b/gurps_character/views.py index ab30201..cdcc609 100644 --- a/gurps_character/views.py +++ b/gurps_character/views.py @@ -4,7 +4,7 @@ from django.shortcuts import render from django.http import HttpResponse, HttpResponseRedirect from django.urls import reverse -from .models import GURPSCharacter +from .models import GURPSCharacter, Campaign from .forms import UploadFileForm @@ -20,7 +20,7 @@ def index(request): else: form = UploadFileForm() - context['form'] = form + context["form"] = form return render(request, "characters/list.html", context) @@ -47,7 +47,7 @@ def handle_uploaded_file(f): f.seek(0) # We read the file in the validator data = json.loads(f.read()) - uuid = data['id'] + uuid = data["id"] name = data["profile"]["name"] try: @@ -60,19 +60,26 @@ def handle_uploaded_file(f): character.save() -def download(request, uuid): +def download(_, uuid): mime_type = "application/x-gcs-gcs" character = GURPSCharacter.objects.get(uuid=uuid) - response = HttpResponse(json.dumps(character.details), content_type=mime_type) - response['Content-Disposition'] = "attachment; filename=%s.gcs" % character.name + response = HttpResponse( + json.dumps(character.details), content_type=mime_type + ) + response["Content-Disposition"] = ( + "attachment; filename=%s.gcs" % character.name + ) return response def character(request, uuid): - response = HttpResponse("Charcater") + response = HttpResponse("Character") return response def campaign(request, uuid): - response = HttpResponse("Campaign") - return response + campaign = Campaign.objects.get(id=uuid) + + context = {"campaign": campaign} + + return render(request, "campaigns/details.html", context) diff --git a/gurps_fixture.json b/gurps_fixture.json new file mode 100644 index 0000000..a03f689 --- /dev/null +++ b/gurps_fixture.json @@ -0,0 +1,1705 @@ +[ + { + "model": "gurps_character.gamesystem", + "pk": 1, + "fields": { + "name": "GURPS", + "description": "The default game system" + } + }, + { + "model": "gurps_character.gm", + "pk": 1, + "fields": { + "gm": 5, + "campaign": 1 + } + }, + { + "model": "gurps_character.player", + "pk": 1, + "fields": { + "user": 5, + "status": "A" + } + }, + { + "model": "gurps_character.campaign", + "pk": 1, + "fields": { + "name": "Test Campaign", + "description": "A Test campaign", + "game_system": 1, + "gm": [] + } + }, + { + "model": "gurps_character.campaignplayer", + "pk": 1, + "fields": { + "campaign": 1, + "player": 1, + "status": "A" + } + }, + { + "model": "gurps_character.gurpscharacter", + "pk": 1, + "fields": { + "uuid": "21bff17b-f17c-4ea7-acdc-8408bd362fee", + "name": "Cristobal Foshee", + "player": 1, + "campaign": 1, + "details": { + "id": "21bff17b-f17c-4ea7-acdc-8408bd362fee", + "calc": { + "move": [ + 6, + 4, + 3, + 2, + 1 + ], + "dodge": [ + 9, + 8, + 7, + 6, + 5 + ], + "swing": "1d+2", + "thrust": "1d-1", + "basic_lift": "29 lb" + }, + "type": "character", + "traits": [ + { + "id": "72d37757-7aca-49b3-9663-9908c7a13bad", + "calc": { + "points": 0 + }, + "name": "Natural Attacks", + "type": "trait", + "weapons": [ + { + "id": "277837fb-eaa4-423e-a8da-7356d9c832db", + "calc": { + "level": 14, + "damage": "1d-2 cr" + }, + "type": "melee_weapon", + "reach": "C", + "usage": "Bite", + "damage": { + "st": "thr", + "base": "-1", + "type": "cr" + }, + "defaults": [ + { + "type": "dx" + }, + { + "name": "Brawling", + "type": "skill" + } + ] + }, + { + "id": "05b83da4-4d0d-42c3-95b4-8b7db5786a47", + "calc": { + "block": "10", + "level": 14, + "parry": "10", + "damage": "1d-2 cr" + }, + "type": "melee_weapon", + "block": "0", + "parry": "0", + "reach": "C", + "usage": "Punch", + "damage": { + "st": "thr", + "base": "-1", + "type": "cr" + }, + "defaults": [ + { + "type": "dx" + }, + { + "name": "Boxing", + "type": "skill" + }, + { + "name": "Brawling", + "type": "skill" + }, + { + "name": "Karate", + "type": "skill" + } + ] + }, + { + "id": "cdbed0e5-44e1-465a-8e3d-823a6f401919", + "calc": { + "level": 12, + "damage": "1d-1 cr" + }, + "type": "melee_weapon", + "reach": "C,1", + "usage": "Kick", + "damage": { + "st": "thr", + "type": "cr" + }, + "defaults": [ + { + "type": "dx", + "modifier": -2 + }, + { + "name": "Brawling", + "type": "skill", + "modifier": -2 + }, + { + "name": "Kicking", + "type": "skill" + }, + { + "name": "Karate", + "type": "skill", + "modifier": -2 + } + ] + } + ], + "reference": "B271" + } + ], + "profile": { + "age": "15", + "eyes": "Brown", + "hair": "Black", + "name": "Cristobal Foshee", + "skin": "Pale", + "gender": "Male", + "height": "5'3\"", + "weight": "118 lb", + "birthday": "August 7", + "handedness": "Right", + "tech_level": "3", + "player_name": "Neill Cox" + }, + "version": 4, + "settings": { + "page": { + "paper_size": "letter", + "top_margin": "0.25 in", + "left_margin": "0.25 in", + "orientation": "portrait", + "right_margin": "0.25 in", + "bottom_margin": "0.25 in" + }, + "body_type": { + "name": "Humanoid", + "roll": "3d", + "locations": [ + { + "id": "eye", + "calc": { + "dr": { + "all": 0 + }, + "roll_range": "-" + }, + "table_name": "Eyes", + "choice_name": "Eyes", + "description": "An attack that misses by 1 hits the torso instead. Only\nimpaling (imp), piercing (pi-, pi, pi+, pi++), and\ntight-beam burning (burn) attacks can target the eye – and\nonly from the front or sides. Injury over HP÷10 blinds the\neye. Otherwise, treat as skull, but without the extra DR!", + "hit_penalty": -9 + }, + { + "id": "skull", + "calc": { + "dr": { + "all": 2 + }, + "roll_range": "3-4" + }, + "slots": 2, + "dr_bonus": 2, + "table_name": "Skull", + "choice_name": "Skull", + "description": "An attack that misses by 1 hits the torso instead. Wounding\nmodifier is x4. Knockdown rolls are at -10. Critical hits\nuse the Critical Head Blow Table (B556). Exception: These\nspecial effects do not apply to toxic (tox) damage.", + "hit_penalty": -7 + }, + { + "id": "face", + "calc": { + "dr": { + "all": 0 + }, + "roll_range": "5" + }, + "slots": 1, + "table_name": "Face", + "choice_name": "Face", + "description": "An attack that misses by 1 hits the torso instead. Jaw,\ncheeks, nose, ears, etc. If the target has an open-faced\nhelmet, ignore its DR. Knockdown rolls are at -5. Critical\nhits use the Critical Head Blow Table (B556). Corrosion\n(cor) damage gets a x1½ wounding modifier, and if it\ninflicts a major wound, it also blinds one eye (both eyes on\ndamage over full HP). Random attacks from behind hit the\nskull instead.", + "hit_penalty": -5 + }, + { + "id": "leg", + "calc": { + "dr": { + "all": 0 + }, + "roll_range": "6-7" + }, + "slots": 2, + "table_name": "Right Leg", + "choice_name": "Leg", + "description": "Reduce the wounding multiplier of large piercing (pi+), huge\npiercing (pi++), and impaling (imp) damage to x1. Any major\nwound (loss of over ½ HP from one blow) cripples the limb.\nDamage beyond that threshold is lost.", + "hit_penalty": -2 + }, + { + "id": "arm", + "calc": { + "dr": { + "all": 0 + }, + "roll_range": "8" + }, + "slots": 1, + "table_name": "Right Arm", + "choice_name": "Arm", + "description": "Reduce the wounding multiplier of large piercing (pi+), huge\npiercing (pi++), and impaling (imp) damage to x1. Any major\nwound (loss of over ½ HP from one blow) cripples the limb.\nDamage beyond that threshold is lost. If holding a shield,\ndouble the penalty to hit: -4 for shield arm instead of -2.", + "hit_penalty": -2 + }, + { + "id": "torso", + "calc": { + "dr": { + "all": 0 + }, + "roll_range": "9-10" + }, + "slots": 2, + "table_name": "Torso", + "choice_name": "Torso" + }, + { + "id": "groin", + "calc": { + "dr": { + "all": 0 + }, + "roll_range": "11" + }, + "slots": 1, + "table_name": "Groin", + "choice_name": "Groin", + "description": "An attack that misses by 1 hits the torso instead. Human\nmales and the males of similar species suffer double shock\nfrom crushing (cr) damage, and get -5 to knockdown rolls.\nOtherwise, treat as a torso hit.", + "hit_penalty": -3 + }, + { + "id": "arm", + "calc": { + "dr": { + "all": 0 + }, + "roll_range": "12" + }, + "slots": 1, + "table_name": "Left Arm", + "choice_name": "Arm", + "description": "Reduce the wounding multiplier of large piercing (pi+), huge\npiercing (pi++), and impaling (imp) damage to x1. Any major\nwound (loss of over ½ HP from one blow) cripples the limb.\nDamage beyond that threshold is lost. If holding a shield,\ndouble the penalty to hit: -4 for shield arm instead of -2.", + "hit_penalty": -2 + }, + { + "id": "leg", + "calc": { + "dr": { + "all": 0 + }, + "roll_range": "13-14" + }, + "slots": 2, + "table_name": "Left Leg", + "choice_name": "Leg", + "description": "Reduce the wounding multiplier of large piercing (pi+), huge\npiercing (pi++), and impaling (imp) damage to x1. Any major\nwound (loss of over ½ HP from one blow) cripples the limb.\nDamage beyond that threshold is lost.", + "hit_penalty": -2 + }, + { + "id": "hand", + "calc": { + "dr": { + "all": 0 + }, + "roll_range": "15" + }, + "slots": 1, + "table_name": "Hand", + "choice_name": "Hand", + "description": "If holding a shield, double the penalty to hit: -8 for\nshield hand instead of -4. Reduce the wounding multiplier of\nlarge piercing (pi+), huge piercing (pi++), and impaling\n(imp) damage to x1. Any major wound (loss of over ⅓ HP\nfrom one blow) cripples the extremity. Damage beyond that\nthreshold is lost.", + "hit_penalty": -4 + }, + { + "id": "foot", + "calc": { + "dr": { + "all": 0 + }, + "roll_range": "16" + }, + "slots": 1, + "table_name": "Foot", + "choice_name": "Foot", + "description": "Reduce the wounding multiplier of large piercing (pi+), huge\npiercing (pi++), and impaling (imp) damage to x1. Any major\nwound (loss of over ⅓ HP from one blow) cripples the\nextremity. Damage beyond that threshold is lost.", + "hit_penalty": -4 + }, + { + "id": "neck", + "calc": { + "dr": { + "all": 0 + }, + "roll_range": "17-18" + }, + "slots": 2, + "table_name": "Neck", + "choice_name": "Neck", + "description": "An attack that misses by 1 hits the torso instead. Neck and\nthroat. Increase the wounding multiplier of crushing (cr)\nand corrosion (cor) attacks to x1½, and that of cutting\n(cut) damage to x2. At the GM’s option, anyone killed by a\ncutting (cut) blow to the neck is decapitated!", + "hit_penalty": -5 + }, + { + "id": "vitals", + "calc": { + "dr": { + "all": 0 + }, + "roll_range": "-" + }, + "table_name": "Vitals", + "choice_name": "Vitals", + "description": "An attack that misses by 1 hits the torso instead. Heart,\nlungs, kidneys, etc. Increase the wounding modifier for an\nimpaling (imp) or any piercing (pi-, pi, pi+, pi++) attack\nto x3. Increase the wounding modifier for a tight-beam\nburning (burn) attack to x2. Other attacks cannot target the\nvitals.", + "hit_penalty": -3 + } + ] + }, + "attributes": [ + { + "id": "st", + "name": "ST", + "type": "integer", + "full_name": "Strength", + "attribute_base": "10", + "cost_per_point": 10, + "cost_adj_percent_per_sm": 10 + }, + { + "id": "dx", + "name": "DX", + "type": "integer", + "full_name": "Dexterity", + "attribute_base": "10", + "cost_per_point": 20 + }, + { + "id": "iq", + "name": "IQ", + "type": "integer", + "full_name": "Intelligence", + "attribute_base": "10", + "cost_per_point": 20 + }, + { + "id": "ht", + "name": "HT", + "type": "integer", + "full_name": "Health", + "attribute_base": "10", + "cost_per_point": 10 + }, + { + "id": "will", + "name": "Will", + "type": "integer", + "attribute_base": "$iq", + "cost_per_point": 5 + }, + { + "id": "fright_check", + "name": "Fright Check", + "type": "integer", + "attribute_base": "$will", + "cost_per_point": 2 + }, + { + "id": "per", + "name": "Per", + "type": "integer", + "full_name": "Perception", + "attribute_base": "$iq", + "cost_per_point": 5 + }, + { + "id": "vision", + "name": "Vision", + "type": "integer", + "attribute_base": "$per", + "cost_per_point": 2 + }, + { + "id": "hearing", + "name": "Hearing", + "type": "integer", + "attribute_base": "$per", + "cost_per_point": 2 + }, + { + "id": "taste_smell", + "name": "Taste & Smell", + "type": "integer", + "attribute_base": "$per", + "cost_per_point": 2 + }, + { + "id": "touch", + "name": "Touch", + "type": "integer", + "attribute_base": "$per", + "cost_per_point": 2 + }, + { + "id": "basic_speed", + "name": "Basic Speed", + "type": "decimal", + "attribute_base": "($dx+$ht)/4", + "cost_per_point": 20 + }, + { + "id": "basic_move", + "name": "Basic Move", + "type": "integer", + "attribute_base": "floor($basic_speed)", + "cost_per_point": 5 + }, + { + "id": "fp", + "name": "FP", + "type": "pool", + "full_name": "Fatigue Points", + "thresholds": [ + { + "ops": [ + "halve_move", + "halve_dodge", + "halve_st" + ], + "state": "Unconscious", + "expression": "-$fp" + }, + { + "ops": [ + "halve_move", + "halve_dodge", + "halve_st" + ], + "state": "Collapse", + "expression": "0", + "explanation": "Roll vs. Will to do anything besides talk or rest; failure causes unconsciousness\nEach FP you lose below 0 also causes 1 HP of injury\nMove, Dodge and ST are halved (B426)" + }, + { + "ops": [ + "halve_move", + "halve_dodge", + "halve_st" + ], + "state": "Tired", + "expression": "round($fp/3)", + "explanation": "Move, Dodge and ST are halved (B426)" + }, + { + "state": "Tiring", + "expression": "$fp-1" + }, + { + "state": "Rested", + "expression": "$fp" + } + ], + "attribute_base": "$ht", + "cost_per_point": 3 + }, + { + "id": "hp", + "name": "HP", + "type": "pool", + "full_name": "Hit Points", + "thresholds": [ + { + "ops": [ + "halve_move", + "halve_dodge" + ], + "state": "Dead", + "expression": "round(-$hp*5)" + }, + { + "ops": [ + "halve_move", + "halve_dodge" + ], + "state": "Dying #4", + "expression": "round(-$hp*4)", + "explanation": "Roll vs. HT to avoid death\nRoll vs. HT-4 every second to avoid falling unconscious\nMove and Dodge are halved (B419)" + }, + { + "ops": [ + "halve_move", + "halve_dodge" + ], + "state": "Dying #3", + "expression": "round(-$hp*3)", + "explanation": "Roll vs. HT to avoid death\nRoll vs. HT-3 every second to avoid falling unconscious\nMove and Dodge are halved (B419)" + }, + { + "ops": [ + "halve_move", + "halve_dodge" + ], + "state": "Dying #2", + "expression": "round(-$hp*2)", + "explanation": "Roll vs. HT to avoid death\nRoll vs. HT-2 every second to avoid falling unconscious\nMove and Dodge are halved (B419)" + }, + { + "ops": [ + "halve_move", + "halve_dodge" + ], + "state": "Dying #1", + "expression": "-$hp", + "explanation": "Roll vs. HT to avoid death\nRoll vs. HT-1 every second to avoid falling unconscious\nMove and Dodge are halved (B419)" + }, + { + "ops": [ + "halve_move", + "halve_dodge" + ], + "state": "Collapse", + "expression": "0", + "explanation": "Roll vs. HT every second to avoid falling unconscious\nMove and Dodge are halved (B419)" + }, + { + "ops": [ + "halve_move", + "halve_dodge" + ], + "state": "Reeling", + "expression": "round($hp/3)", + "explanation": "Move and Dodge are halved (B419)" + }, + { + "state": "Wounded", + "expression": "$hp-1" + }, + { + "state": "Healthy", + "expression": "$hp" + } + ], + "attribute_base": "$st", + "cost_per_point": 2, + "cost_adj_percent_per_sm": 10 + } + ], + "block_layout": [ + "reactions conditional_modifiers", + "melee", + "ranged", + "traits skills", + "spells", + "equipment", + "other_equipment", + "notes" + ], + "notes_display": "inline", + "show_spell_adj": true, + "modifiers_display": "inline", + "damage_progression": "basic_set", + "default_length_units": "ft_in", + "default_weight_units": "lb", + "skill_level_adj_display": "tooltip", + "user_description_display": "tooltip", + "exclude_unspent_points_from_total": false + }, + "equipment": [ + { + "id": "baa03bd5-d388-4aff-b1a4-0ba1a90c47ce", + "calc": { + "extended_value": 35, + "extended_weight": "3 lb" + }, + "tags": [ + "Tools" + ], + "type": "equipment", + "notes": "For weighing goods", + "value": 35, + "weight": "3 lb", + "equipped": true, + "quantity": 1, + "reference": "B289", + "tech_level": "1", + "description": "Balance and Weights" + } + ], + "attributes": [ + { + "adj": 2, + "calc": { + "value": 12, + "points": 20 + }, + "attr_id": "st" + }, + { + "adj": 4, + "calc": { + "value": 14, + "points": 80 + }, + "attr_id": "dx" + }, + { + "adj": 0, + "calc": { + "value": 10, + "points": 0 + }, + "attr_id": "iq" + }, + { + "adj": 1, + "calc": { + "value": 11, + "points": 10 + }, + "attr_id": "ht" + }, + { + "adj": 0, + "calc": { + "value": 10, + "points": 0 + }, + "attr_id": "will" + }, + { + "adj": 0, + "calc": { + "value": 10, + "points": 0 + }, + "attr_id": "fright_check" + }, + { + "adj": 0, + "calc": { + "value": 10, + "points": 0 + }, + "attr_id": "per" + }, + { + "adj": 0, + "calc": { + "value": 10, + "points": 0 + }, + "attr_id": "vision" + }, + { + "adj": 0, + "calc": { + "value": 10, + "points": 0 + }, + "attr_id": "hearing" + }, + { + "adj": 0, + "calc": { + "value": 10, + "points": 0 + }, + "attr_id": "taste_smell" + }, + { + "adj": 0, + "calc": { + "value": 10, + "points": 0 + }, + "attr_id": "touch" + }, + { + "adj": 0, + "calc": { + "value": 6.25, + "points": 0 + }, + "attr_id": "basic_speed" + }, + { + "adj": 0, + "calc": { + "value": 6, + "points": 0 + }, + "attr_id": "basic_move" + }, + { + "adj": 0, + "calc": { + "value": 11, + "points": 0, + "current": 11 + }, + "attr_id": "fp" + }, + { + "adj": 0, + "calc": { + "value": 12, + "points": 0, + "current": 12 + }, + "attr_id": "hp" + } + ], + "created_date": "2024-01-16T18:39:48+11:00", + "total_points": 150, + "modified_date": "2024-01-16T20:00:32+11:00", + "points_record": [ + { + "when": "2024-01-16T18:39:48+11:00", + "points": 150, + "reason": "Initial points" + } + ] + } + } + }, + { + "model": "admin.logentry", + "pk": 1, + "fields": { + "action_time": "2024-09-07T01:20:55.084Z", + "user": 5, + "content_type": 2, + "object_id": "1", + "object_repr": "GameSystem object (1)", + "action_flag": 1, + "change_message": "[{\"added\": {}}]" + } + }, + { + "model": "admin.logentry", + "pk": 2, + "fields": { + "action_time": "2024-09-07T22:21:57.670Z", + "user": 5, + "content_type": 3, + "object_id": "1", + "object_repr": "Test Campaign", + "action_flag": 1, + "change_message": "[{\"added\": {}}]" + } + }, + { + "model": "admin.logentry", + "pk": 3, + "fields": { + "action_time": "2024-09-07T22:22:48.098Z", + "user": 5, + "content_type": 15, + "object_id": "1", + "object_repr": "GM object (1)", + "action_flag": 1, + "change_message": "[{\"added\": {}}]" + } + }, + { + "model": "admin.logentry", + "pk": 4, + "fields": { + "action_time": "2024-09-07T22:22:56.058Z", + "user": 5, + "content_type": 15, + "object_id": "1", + "object_repr": "GM object (1)", + "action_flag": 2, + "change_message": "[{\"changed\": {\"fields\": [\"Campaign\"]}}]" + } + }, + { + "model": "admin.logentry", + "pk": 5, + "fields": { + "action_time": "2024-09-07T22:25:20.196Z", + "user": 5, + "content_type": 16, + "object_id": "1", + "object_repr": "Player object (1)", + "action_flag": 1, + "change_message": "[{\"added\": {}}]" + } + }, + { + "model": "admin.logentry", + "pk": 6, + "fields": { + "action_time": "2024-09-07T22:26:43.919Z", + "user": 5, + "content_type": 17, + "object_id": "1", + "object_repr": "CampaignPlayer object (1)", + "action_flag": 1, + "change_message": "[{\"added\": {}}]" + } + }, + { + "model": "admin.logentry", + "pk": 7, + "fields": { + "action_time": "2024-09-07T22:35:11.435Z", + "user": 5, + "content_type": 1, + "object_id": "1", + "object_repr": "Cristobal Foshee Player Neill Cox - Accepted Campaign Test Campaign", + "action_flag": 1, + "change_message": "[{\"added\": {}}]" + } + }, + { + "model": "auth.permission", + "pk": 1, + "fields": { + "name": "Can add gurps character", + "content_type": 1, + "codename": "add_gurpscharacter" + } + }, + { + "model": "auth.permission", + "pk": 2, + "fields": { + "name": "Can change gurps character", + "content_type": 1, + "codename": "change_gurpscharacter" + } + }, + { + "model": "auth.permission", + "pk": 3, + "fields": { + "name": "Can delete gurps character", + "content_type": 1, + "codename": "delete_gurpscharacter" + } + }, + { + "model": "auth.permission", + "pk": 4, + "fields": { + "name": "Can view gurps character", + "content_type": 1, + "codename": "view_gurpscharacter" + } + }, + { + "model": "auth.permission", + "pk": 5, + "fields": { + "name": "Can add game system", + "content_type": 2, + "codename": "add_gamesystem" + } + }, + { + "model": "auth.permission", + "pk": 6, + "fields": { + "name": "Can change game system", + "content_type": 2, + "codename": "change_gamesystem" + } + }, + { + "model": "auth.permission", + "pk": 7, + "fields": { + "name": "Can delete game system", + "content_type": 2, + "codename": "delete_gamesystem" + } + }, + { + "model": "auth.permission", + "pk": 8, + "fields": { + "name": "Can view game system", + "content_type": 2, + "codename": "view_gamesystem" + } + }, + { + "model": "auth.permission", + "pk": 9, + "fields": { + "name": "Can add campaign", + "content_type": 3, + "codename": "add_campaign" + } + }, + { + "model": "auth.permission", + "pk": 10, + "fields": { + "name": "Can change campaign", + "content_type": 3, + "codename": "change_campaign" + } + }, + { + "model": "auth.permission", + "pk": 11, + "fields": { + "name": "Can delete campaign", + "content_type": 3, + "codename": "delete_campaign" + } + }, + { + "model": "auth.permission", + "pk": 12, + "fields": { + "name": "Can view campaign", + "content_type": 3, + "codename": "view_campaign" + } + }, + { + "model": "auth.permission", + "pk": 13, + "fields": { + "name": "Can add log entry", + "content_type": 4, + "codename": "add_logentry" + } + }, + { + "model": "auth.permission", + "pk": 14, + "fields": { + "name": "Can change log entry", + "content_type": 4, + "codename": "change_logentry" + } + }, + { + "model": "auth.permission", + "pk": 15, + "fields": { + "name": "Can delete log entry", + "content_type": 4, + "codename": "delete_logentry" + } + }, + { + "model": "auth.permission", + "pk": 16, + "fields": { + "name": "Can view log entry", + "content_type": 4, + "codename": "view_logentry" + } + }, + { + "model": "auth.permission", + "pk": 17, + "fields": { + "name": "Can add permission", + "content_type": 5, + "codename": "add_permission" + } + }, + { + "model": "auth.permission", + "pk": 18, + "fields": { + "name": "Can change permission", + "content_type": 5, + "codename": "change_permission" + } + }, + { + "model": "auth.permission", + "pk": 19, + "fields": { + "name": "Can delete permission", + "content_type": 5, + "codename": "delete_permission" + } + }, + { + "model": "auth.permission", + "pk": 20, + "fields": { + "name": "Can view permission", + "content_type": 5, + "codename": "view_permission" + } + }, + { + "model": "auth.permission", + "pk": 21, + "fields": { + "name": "Can add group", + "content_type": 6, + "codename": "add_group" + } + }, + { + "model": "auth.permission", + "pk": 22, + "fields": { + "name": "Can change group", + "content_type": 6, + "codename": "change_group" + } + }, + { + "model": "auth.permission", + "pk": 23, + "fields": { + "name": "Can delete group", + "content_type": 6, + "codename": "delete_group" + } + }, + { + "model": "auth.permission", + "pk": 24, + "fields": { + "name": "Can view group", + "content_type": 6, + "codename": "view_group" + } + }, + { + "model": "auth.permission", + "pk": 25, + "fields": { + "name": "Can add user", + "content_type": 7, + "codename": "add_user" + } + }, + { + "model": "auth.permission", + "pk": 26, + "fields": { + "name": "Can change user", + "content_type": 7, + "codename": "change_user" + } + }, + { + "model": "auth.permission", + "pk": 27, + "fields": { + "name": "Can delete user", + "content_type": 7, + "codename": "delete_user" + } + }, + { + "model": "auth.permission", + "pk": 28, + "fields": { + "name": "Can view user", + "content_type": 7, + "codename": "view_user" + } + }, + { + "model": "auth.permission", + "pk": 29, + "fields": { + "name": "Can add content type", + "content_type": 8, + "codename": "add_contenttype" + } + }, + { + "model": "auth.permission", + "pk": 30, + "fields": { + "name": "Can change content type", + "content_type": 8, + "codename": "change_contenttype" + } + }, + { + "model": "auth.permission", + "pk": 31, + "fields": { + "name": "Can delete content type", + "content_type": 8, + "codename": "delete_contenttype" + } + }, + { + "model": "auth.permission", + "pk": 32, + "fields": { + "name": "Can view content type", + "content_type": 8, + "codename": "view_contenttype" + } + }, + { + "model": "auth.permission", + "pk": 33, + "fields": { + "name": "Can add session", + "content_type": 9, + "codename": "add_session" + } + }, + { + "model": "auth.permission", + "pk": 34, + "fields": { + "name": "Can change session", + "content_type": 9, + "codename": "change_session" + } + }, + { + "model": "auth.permission", + "pk": 35, + "fields": { + "name": "Can delete session", + "content_type": 9, + "codename": "delete_session" + } + }, + { + "model": "auth.permission", + "pk": 36, + "fields": { + "name": "Can view session", + "content_type": 9, + "codename": "view_session" + } + }, + { + "model": "auth.permission", + "pk": 37, + "fields": { + "name": "Can add email address", + "content_type": 10, + "codename": "add_emailaddress" + } + }, + { + "model": "auth.permission", + "pk": 38, + "fields": { + "name": "Can change email address", + "content_type": 10, + "codename": "change_emailaddress" + } + }, + { + "model": "auth.permission", + "pk": 39, + "fields": { + "name": "Can delete email address", + "content_type": 10, + "codename": "delete_emailaddress" + } + }, + { + "model": "auth.permission", + "pk": 40, + "fields": { + "name": "Can view email address", + "content_type": 10, + "codename": "view_emailaddress" + } + }, + { + "model": "auth.permission", + "pk": 41, + "fields": { + "name": "Can add email confirmation", + "content_type": 11, + "codename": "add_emailconfirmation" + } + }, + { + "model": "auth.permission", + "pk": 42, + "fields": { + "name": "Can change email confirmation", + "content_type": 11, + "codename": "change_emailconfirmation" + } + }, + { + "model": "auth.permission", + "pk": 43, + "fields": { + "name": "Can delete email confirmation", + "content_type": 11, + "codename": "delete_emailconfirmation" + } + }, + { + "model": "auth.permission", + "pk": 44, + "fields": { + "name": "Can view email confirmation", + "content_type": 11, + "codename": "view_emailconfirmation" + } + }, + { + "model": "auth.permission", + "pk": 45, + "fields": { + "name": "Can add social account", + "content_type": 12, + "codename": "add_socialaccount" + } + }, + { + "model": "auth.permission", + "pk": 46, + "fields": { + "name": "Can change social account", + "content_type": 12, + "codename": "change_socialaccount" + } + }, + { + "model": "auth.permission", + "pk": 47, + "fields": { + "name": "Can delete social account", + "content_type": 12, + "codename": "delete_socialaccount" + } + }, + { + "model": "auth.permission", + "pk": 48, + "fields": { + "name": "Can view social account", + "content_type": 12, + "codename": "view_socialaccount" + } + }, + { + "model": "auth.permission", + "pk": 49, + "fields": { + "name": "Can add social application", + "content_type": 13, + "codename": "add_socialapp" + } + }, + { + "model": "auth.permission", + "pk": 50, + "fields": { + "name": "Can change social application", + "content_type": 13, + "codename": "change_socialapp" + } + }, + { + "model": "auth.permission", + "pk": 51, + "fields": { + "name": "Can delete social application", + "content_type": 13, + "codename": "delete_socialapp" + } + }, + { + "model": "auth.permission", + "pk": 52, + "fields": { + "name": "Can view social application", + "content_type": 13, + "codename": "view_socialapp" + } + }, + { + "model": "auth.permission", + "pk": 53, + "fields": { + "name": "Can add social application token", + "content_type": 14, + "codename": "add_socialtoken" + } + }, + { + "model": "auth.permission", + "pk": 54, + "fields": { + "name": "Can change social application token", + "content_type": 14, + "codename": "change_socialtoken" + } + }, + { + "model": "auth.permission", + "pk": 55, + "fields": { + "name": "Can delete social application token", + "content_type": 14, + "codename": "delete_socialtoken" + } + }, + { + "model": "auth.permission", + "pk": 56, + "fields": { + "name": "Can view social application token", + "content_type": 14, + "codename": "view_socialtoken" + } + }, + { + "model": "auth.permission", + "pk": 57, + "fields": { + "name": "Can add gm", + "content_type": 15, + "codename": "add_gm" + } + }, + { + "model": "auth.permission", + "pk": 58, + "fields": { + "name": "Can change gm", + "content_type": 15, + "codename": "change_gm" + } + }, + { + "model": "auth.permission", + "pk": 59, + "fields": { + "name": "Can delete gm", + "content_type": 15, + "codename": "delete_gm" + } + }, + { + "model": "auth.permission", + "pk": 60, + "fields": { + "name": "Can view gm", + "content_type": 15, + "codename": "view_gm" + } + }, + { + "model": "auth.permission", + "pk": 61, + "fields": { + "name": "Can add player", + "content_type": 16, + "codename": "add_player" + } + }, + { + "model": "auth.permission", + "pk": 62, + "fields": { + "name": "Can change player", + "content_type": 16, + "codename": "change_player" + } + }, + { + "model": "auth.permission", + "pk": 63, + "fields": { + "name": "Can delete player", + "content_type": 16, + "codename": "delete_player" + } + }, + { + "model": "auth.permission", + "pk": 64, + "fields": { + "name": "Can view player", + "content_type": 16, + "codename": "view_player" + } + }, + { + "model": "auth.permission", + "pk": 65, + "fields": { + "name": "Can add campaign player", + "content_type": 17, + "codename": "add_campaignplayer" + } + }, + { + "model": "auth.permission", + "pk": 66, + "fields": { + "name": "Can change campaign player", + "content_type": 17, + "codename": "change_campaignplayer" + } + }, + { + "model": "auth.permission", + "pk": 67, + "fields": { + "name": "Can delete campaign player", + "content_type": 17, + "codename": "delete_campaignplayer" + } + }, + { + "model": "auth.permission", + "pk": 68, + "fields": { + "name": "Can view campaign player", + "content_type": 17, + "codename": "view_campaignplayer" + } + }, + { + "model": "auth.permission", + "pk": 69, + "fields": { + "name": "Can add campaign notes", + "content_type": 18, + "codename": "add_campaignnotes" + } + }, + { + "model": "auth.permission", + "pk": 70, + "fields": { + "name": "Can change campaign notes", + "content_type": 18, + "codename": "change_campaignnotes" + } + }, + { + "model": "auth.permission", + "pk": 71, + "fields": { + "name": "Can delete campaign notes", + "content_type": 18, + "codename": "delete_campaignnotes" + } + }, + { + "model": "auth.permission", + "pk": 72, + "fields": { + "name": "Can view campaign notes", + "content_type": 18, + "codename": "view_campaignnotes" + } + }, + { + "model": "auth.user", + "pk": 5, + "fields": { + "password": "pbkdf2_sha256$720000$wGy4dCx6biTC0yQQoMzzUX$b53hkdZueuhmjEmDUqDJbfDHA+RiyKBEvV78rQO3Uxs=", + "last_login": "2024-09-07T00:49:07.960Z", + "is_superuser": true, + "username": "neill", + "first_name": "Neill", + "last_name": "Cox", + "email": "neill@neill.id.au", + "is_staff": true, + "is_active": true, + "date_joined": "2024-07-31T00:00:00Z", + "groups": [], + "user_permissions": [] + } + }, + { + "model": "contenttypes.contenttype", + "pk": 1, + "fields": { + "app_label": "gurps_character", + "model": "gurpscharacter" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 2, + "fields": { + "app_label": "gurps_character", + "model": "gamesystem" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 3, + "fields": { + "app_label": "gurps_character", + "model": "campaign" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 4, + "fields": { + "app_label": "admin", + "model": "logentry" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 5, + "fields": { + "app_label": "auth", + "model": "permission" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 6, + "fields": { + "app_label": "auth", + "model": "group" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 7, + "fields": { + "app_label": "auth", + "model": "user" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 8, + "fields": { + "app_label": "contenttypes", + "model": "contenttype" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 9, + "fields": { + "app_label": "sessions", + "model": "session" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 10, + "fields": { + "app_label": "account", + "model": "emailaddress" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 11, + "fields": { + "app_label": "account", + "model": "emailconfirmation" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 12, + "fields": { + "app_label": "socialaccount", + "model": "socialaccount" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 13, + "fields": { + "app_label": "socialaccount", + "model": "socialapp" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 14, + "fields": { + "app_label": "socialaccount", + "model": "socialtoken" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 15, + "fields": { + "app_label": "gurps_character", + "model": "gm" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 16, + "fields": { + "app_label": "gurps_character", + "model": "player" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 17, + "fields": { + "app_label": "gurps_character", + "model": "campaignplayer" + } + }, + { + "model": "contenttypes.contenttype", + "pk": 18, + "fields": { + "app_label": "gurps_character", + "model": "campaignnotes" + } + }, + { + "model": "sessions.session", + "pk": "f9yj8itz4o4mv8r9ntm8ap4gnjmhm9eo", + "fields": { + "session_data": ".eJxVTkEOgyAQ_MueDQEVEE9tv9E0Zl0wklJIKp6Mf69aL142k5nZmVkAidIcc4dzHl3MnjD7FLuPy2OyE7TPBf4YWqBkHRSAGVqhS6k0F7VmptZGVgW4D_qwuaLzIdyOy7xlOMP6KuAo6ObJfTu_Z0m4cD3S28VdwBB2mp3D2OE55YndLzMf59clasRp3AuoHLgwJEqyZkDeGIWqIbSlUttsaSszaKc3wDWRqNSgeE1SGNFTo0nA-gM52l4C:1smjdT:ewDmb-tr_92gDdzMkP3KwyNMpn3zJDCdL9WWDm2XwEY", + "expire_date": "2024-09-21T00:49:07.966Z" + } + } +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9da3305 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,14 @@ +[tool.black] +line-length = 72 + +[tool.ruff] +line-length = 72 + +[tool.autopep8] +max_line_length = 72 + +[tool.pycodestyle] +max_line_length = 72 + +[tool.pyright] +typeCheckingMode = "off" diff --git a/requirements.txt b/requirements.txt index 7615561..e01aff2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ -django -tailwind -django_browser_reload -psycopg -django-allauth[socialaccount] -django-bootstrap-icons +Django==5.1.7 +psycopg2-binary==2.9.10 +django-environ-0.12.0 +django-allauth[socialaccount]==65.4.1 +django-bootstrap-icons==0.9.0 +django-browser-reload==1.18.0 + diff --git a/requirements_dev.txt b/requirements_dev.txt new file mode 100644 index 0000000..43d0c8d --- /dev/null +++ b/requirements_dev.txt @@ -0,0 +1 @@ +pdbpp==0.10.3