From: David Kerkeslager Date: Thu, 25 Feb 2021 19:27:53 +0000 (-0500) Subject: Add some models X-Git-Url: https://code.kerkeslager.com/?p=tickle;a=commitdiff_plain;h=98b5ba068c768b45d6c05eb0d21ad1c4388732b0 Add some models --- diff --git a/core/settings.py b/core/settings.py index 3caa931..296d506 100644 --- a/core/settings.py +++ b/core/settings.py @@ -37,6 +37,8 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + + 'tickle', ] MIDDLEWARE = [ diff --git a/tickle/migrations/0001_initial.py b/tickle/migrations/0001_initial.py new file mode 100644 index 0000000..7955d21 --- /dev/null +++ b/tickle/migrations/0001_initial.py @@ -0,0 +1,95 @@ +# Generated by Django 3.1.7 on 2021-02-25 19:26 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Boulder', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=64)), + ('mountainproject', models.URLField(null=True)), + ], + ), + migrations.CreateModel( + name='BoulderDifficulty', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('order', models.PositiveSmallIntegerField()), + ('name', models.CharField(max_length=8)), + ], + ), + migrations.CreateModel( + name='Route', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=64)), + ('protection_style', models.CharField(choices=[('sport', 'Sport'), ('toprope', 'Top Rope'), ('trad', 'Trad')], max_length=8)), + ('mountainproject', models.URLField(null=True)), + ], + ), + migrations.CreateModel( + name='RouteDifficulty', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('order', models.PositiveSmallIntegerField()), + ('name', models.CharField(max_length=8)), + ], + ), + migrations.CreateModel( + name='Todo', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('notes', models.TextField()), + ('protection', models.CharField(choices=[('none', 'None'), ('bolts', 'Bolts'), ('gear', 'Gear'), ('pad', 'Pad'), ('tr', 'Top Rope')], max_length=8)), + ('boulder', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='todos', to='tickle.boulder')), + ('route', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='todos', to='tickle.route')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Pitch', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('difficulty', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='pitches', to='tickle.routedifficulty')), + ('route', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pitches', to='tickle.route')), + ], + ), + migrations.AddField( + model_name='boulder', + name='difficulty', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='boulders', to='tickle.boulderdifficulty'), + ), + migrations.CreateModel( + name='Attempt', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('notes', models.TextField()), + ('result', models.CharField(choices=[('send', 'Sent'), ('fall', 'Fall')], max_length=8)), + ('prior_knowledge', models.BooleanField(default=True)), + ('protection_used', models.CharField(choices=[('none', 'None'), ('bolts', 'Bolts'), ('gear', 'Gear'), ('pad', 'Pad'), ('tr', 'Top Rope')], max_length=8)), + ('boulder', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='attempts', to='tickle.boulder')), + ('route', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='attempts', to='tickle.route')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AddConstraint( + model_name='todo', + constraint=models.CheckConstraint(check=models.Q(models.Q(('boulder__isnull', True), models.Q(_negated=True, route__isnull=True)), models.Q(models.Q(_negated=True, boulder__isnull=True), ('route__isnull', True)), _connector='OR'), name='todo_boulder_xor_route'), + ), + migrations.AddConstraint( + model_name='attempt', + constraint=models.CheckConstraint(check=models.Q(models.Q(('boulder__isnull', True), models.Q(_negated=True, route__isnull=True)), models.Q(models.Q(_negated=True, boulder__isnull=True), ('route__isnull', True)), _connector='OR'), name='attempt_boulder_xor_route'), + ), + ] diff --git a/tickle/models.py b/tickle/models.py index 71a8362..268d7b0 100644 --- a/tickle/models.py +++ b/tickle/models.py @@ -1,3 +1,108 @@ +from django.contrib.auth.models import User +from django.core.exceptions import ValidationError from django.db import models +from django.db.models import Q -# Create your models here. +class QQ: + def __xor__(self, other): + return (self & (~other)) | ((~self) & other) + +Q.__bases__ += (QQ, ) + +class Boulder(models.Model): + name = models.CharField(max_length=64) + difficulty = models.ForeignKey( + 'BoulderDifficulty', + null=True, + on_delete=models.PROTECT, + related_name='boulders', + ) + mountainproject = models.URLField(null=True) + +class BoulderDifficulty(models.Model): + order = models.PositiveSmallIntegerField() + name = models.CharField(max_length=8) + +class Pitch(models.Model): + route = models.ForeignKey( + 'Route', + on_delete=models.CASCADE, + related_name='pitches', + ) + difficulty = models.ForeignKey( + 'RouteDifficulty', + on_delete=models.PROTECT, + related_name='pitches', + ) + +PROTECTION_STYLE_CHOICES = ( + ('sport', 'Sport'), + ('toprope', 'Top Rope'), + ('trad', 'Trad'), +) + +class Route(models.Model): + name = models.CharField(max_length=64) + protection_style = models.CharField(max_length=8, choices=PROTECTION_STYLE_CHOICES) + mountainproject = models.URLField(null=True) + + # TODO Write test for this + @property + def difficulty(self): + return self.pitches.order_by('-difficulty__order').first().difficulty + +class RouteDifficulty(models.Model): + order = models.PositiveSmallIntegerField() + name = models.CharField(max_length=8) + +ATTEMPT_RESULT_CHOICES = ( + ('send', 'Sent'), + ('fall', 'Fall'), +) + +PROTECTION_CHOICES = ( + ('none', 'None'), + ('bolts', 'Bolts'), + ('gear', 'Gear'), + ('pad', 'Pad'), + ('tr', 'Top Rope'), +) + +class Attempt(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + notes = models.TextField() + boulder = models.ForeignKey('Boulder', null=True, on_delete=models.PROTECT, related_name='attempts') + route = models.ForeignKey('Route', null=True, on_delete=models.PROTECT, related_name='attempts') + result = models.CharField(max_length=8, choices=ATTEMPT_RESULT_CHOICES) + prior_knowledge = models.BooleanField(default=True) + protection_used = models.CharField(max_length=8, choices=PROTECTION_CHOICES) + + class Meta: + constraints = ( + models.CheckConstraint( + check=(Q(boulder__isnull=True) ^ Q(route__isnull=True)), + name='attempt_boulder_xor_route', + ), + ) + +STYLE_CHOICES = ( + ('onsight', 'On Sight'), + ('flash', 'Flash'), + ('complete', 'Complete'), + ('project', 'project'), +) + +class Todo(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + notes = models.TextField() + protection = models.CharField(max_length=8, choices=PROTECTION_CHOICES) + boulder = models.ForeignKey('Boulder', null=True, on_delete=models.PROTECT, related_name='todos') + route = models.ForeignKey('Route', null=True, on_delete=models.PROTECT, related_name='todos') + + class Meta: + constraints = ( + models.CheckConstraint( + check=(Q(boulder__isnull=True) ^ Q(route__isnull=True)), + name='todo_boulder_xor_route', + ), + )