1 from django.contrib.auth.models import User
2 from django.core.exceptions import ValidationError
3 from django.db import models
4 from django.db.models import Q
7 def __xor__(self, other):
8 return (self & (~other)) | ((~self) & other)
12 class Boulder(models.Model):
13 name = models.CharField(max_length=64)
14 difficulty = models.ForeignKey(
17 on_delete=models.PROTECT,
18 related_name='boulders',
20 mountainproject = models.URLField(blank=True)
23 return '{} ({})'.format(self.name, self.difficulty)
25 class BoulderDifficulty(models.Model):
26 order = models.PositiveSmallIntegerField()
27 name = models.CharField(max_length=8)
35 class Pitch(models.Model):
36 order = models.PositiveSmallIntegerField()
37 route = models.ForeignKey(
39 on_delete=models.CASCADE,
40 related_name='pitches',
42 difficulty = models.ForeignKey(
44 on_delete=models.PROTECT,
45 related_name='pitches',
52 return 'P{} ({})'.format(self.order, self.difficulty)
54 PROTECTION_STYLE_CHOICES = (
56 ('toprope', 'Top Rope'),
60 class Route(models.Model):
61 name = models.CharField(max_length=64)
62 protection_style = models.CharField(max_length=8, choices=PROTECTION_STYLE_CHOICES)
63 mountainproject = models.URLField(blank=True)
65 # TODO Write test for this
68 return self.pitches.order_by('-difficulty__order').first().difficulty
71 return '{} ({})'.format(self.name, self.difficulty)
73 class RouteDifficulty(models.Model):
74 order = models.PositiveSmallIntegerField()
75 name = models.CharField(max_length=8)
80 ATTEMPT_RESULT_CHOICES = (
85 PROTECTION_CHOICES = (
93 class Attempt(models.Model):
94 user = models.ForeignKey(User, on_delete=models.CASCADE)
95 date = models.DateField()
96 notes = models.TextField()
97 boulder = models.ForeignKey('Boulder', null=True, on_delete=models.PROTECT, related_name='attempts')
98 route = models.ForeignKey('Route', null=True, on_delete=models.PROTECT, related_name='attempts')
99 result = models.CharField(max_length=8, choices=ATTEMPT_RESULT_CHOICES)
100 prior_knowledge = models.BooleanField(default=True)
101 protection_used = models.CharField(max_length=8, choices=PROTECTION_CHOICES)
105 models.CheckConstraint(
106 check=(Q(boulder__isnull=True) ^ Q(route__isnull=True)),
107 name='attempt_boulder_xor_route',
114 ('onsight', 'On Sight'),
116 ('project', 'Project'),
120 class Todo(models.Model):
121 user = models.ForeignKey(User, on_delete=models.CASCADE)
122 notes = models.TextField()
123 protection = models.CharField(max_length=8, choices=PROTECTION_CHOICES)
124 boulder = models.ForeignKey('Boulder', null=True, on_delete=models.PROTECT, related_name='todos')
125 route = models.ForeignKey('Route', null=True, on_delete=models.PROTECT, related_name='todos')
126 style = models.CharField(max_length=8, choices=STYLE_CHOICES)
130 models.CheckConstraint(
131 check=(Q(boulder__isnull=True) ^ Q(route__isnull=True)),
132 name='todo_boulder_xor_route',
136 ordering = ('route__name',)
146 return '{} {}'.format(self.style, climb)