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 BOULDER_DIFFICULTY_CHOICES = (
32 class Area(models.Model):
33 parent = models.ForeignKey(
37 on_delete=models.CASCADE,
39 name = models.CharField(max_length=64)
40 notes = models.TextField(blank=True)
43 if self.parent is None:
46 return '{} > {}'.format(self.parent, self.name)
48 class Boulder(models.Model):
49 area = models.ForeignKey('Area', on_delete=models.PROTECT)
50 name = models.CharField(max_length=64)
51 difficulty = models.CharField(
52 choices=BOULDER_DIFFICULTY_CHOICES,
55 mountainproject = models.URLField(blank=True)
56 notes = models.TextField(blank=True)
59 return '{} ({})'.format(self.name, self.difficulty)
61 PITCH_DIFFICULTY_CHOICES = (
103 class Pitch(models.Model):
104 order = models.PositiveSmallIntegerField()
105 route = models.ForeignKey(
107 on_delete=models.CASCADE,
108 related_name='pitches',
110 difficulty = models.CharField(
111 choices=PITCH_DIFFICULTY_CHOICES,
114 name = models.CharField(blank=True, max_length=32)
115 notes = models.TextField(blank=True)
118 ordering = ('order',)
121 return 'P{} ({})'.format(self.order, self.difficulty)
123 PROTECTION_STYLE_CHOICES = (
125 ('toprope', 'Top Rope'),
129 class Route(models.Model):
130 area = models.ForeignKey('Area', on_delete=models.PROTECT)
131 name = models.CharField(max_length=64)
132 protection_style = models.CharField(max_length=8, choices=PROTECTION_STYLE_CHOICES)
133 mountainproject = models.URLField(blank=True)
134 notes = models.TextField(blank=True)
136 # TODO Write test for this
138 def difficulty(self):
139 return self.pitches.order_by('-difficulty').first().difficulty
142 return '{} ({})'.format(self.name, self.difficulty)
144 ATTEMPT_RESULT_CHOICES = (
149 PROTECTION_CHOICES = (
157 class Attempt(models.Model):
158 user = models.ForeignKey(User, on_delete=models.CASCADE)
159 date = models.DateField()
160 notes = models.TextField(blank=True)
161 boulder = models.ForeignKey('Boulder', null=True, on_delete=models.PROTECT, related_name='attempts')
162 route = models.ForeignKey('Route', null=True, on_delete=models.PROTECT, related_name='attempts')
163 result = models.CharField(max_length=8, choices=ATTEMPT_RESULT_CHOICES)
164 prior_knowledge = models.BooleanField(default=True)
165 protection_used = models.CharField(max_length=8, choices=PROTECTION_CHOICES)
169 models.CheckConstraint(
170 check=(Q(boulder__isnull=True) ^ Q(route__isnull=True)),
171 name='attempt_boulder_xor_route',
178 ('onsight', 'On Sight'),
180 ('project', 'Project'),
184 class Todo(models.Model):
185 user = models.ForeignKey(User, on_delete=models.CASCADE)
186 notes = models.TextField(blank=True)
187 protection = models.CharField(max_length=8, choices=PROTECTION_CHOICES)
188 boulder = models.ForeignKey('Boulder', null=True, on_delete=models.PROTECT, related_name='todos')
189 route = models.ForeignKey('Route', null=True, on_delete=models.PROTECT, related_name='todos')
190 style = models.CharField(max_length=8, choices=STYLE_CHOICES)
194 models.CheckConstraint(
195 check=(Q(boulder__isnull=True) ^ Q(route__isnull=True)),
196 name='todo_boulder_xor_route',
200 ordering = ('route__name',)
210 return '{} {}'.format(self.style, climb)