--- /dev/null
+# 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'),
+ ),
+ ]
+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',
+ ),
+ )