67befd7c7f3923016c43a9d93019cd11f5a42cf4
[climbing.kerkeslager.com] / src / climbing / models.py
1 from django.contrib.auth.models import User
2 from django.db import models
3
4 from core import utils
5
6 class Area(models.Model):
7     name = models.CharField(max_length=64)
8     notes = models.TextField(blank=True, null=True)
9
10     def __str__(self):
11         return self.name
12
13     @property
14     def sub_areas(self):
15         return utils.merge(
16             self.crags.order_by('name'),
17             self.clusters.order_by('name'),
18             'name',
19         )
20
21 class Crag(models.Model):
22     area = models.ForeignKey(
23         Area,
24         on_delete=models.CASCADE,
25         related_name='crags',
26     )
27     name = models.CharField(max_length=64)
28     notes = models.TextField(blank=True, null=True)
29
30     def __str__(self):
31         return self.name
32
33 class Route(models.Model):
34     area = models.ForeignKey(
35         Crag,
36         on_delete=models.CASCADE,
37         related_name='routes',
38     )
39     name = models.CharField(max_length=64)
40     mountain_project = models.URLField(blank=True, null=True)
41     notes = models.TextField(blank=True, null=True)
42
43     def __str__(self):
44         pitch_count = self.pitches.count()
45
46         if pitch_count == 0:
47             return self.name
48
49         if pitch_count == 1:
50             return '{} {}'.format(self.name, self.difficulty_display)
51
52         return '{} {} ({} pitches)'.format(
53             self.name,
54             self.difficulty_display,
55             pitch_count,
56         )
57
58     @property
59     def difficulty(self):
60         diff = -1
61
62         for pitch in self.pitches.all():
63             diff = max(diff, pitch.difficulty)
64
65         return diff
66
67     @property
68     def difficulty_display(self):
69         return RouteDifficulty(self.difficulty).label
70
71 class RouteDifficulty(models.IntegerChoices):
72     YDS_5_0 = 0, '5.0'
73     YDS_5_1 = 1, '5.1'
74     YDS_5_2 = 2, '5.2'
75     YDS_5_3 = 3, '5.3'
76     YDS_5_4 = 4, '5.4'
77     YDS_5_5 = 5, '5.5'
78     YDS_5_6 = 6, '5.6'
79     YDS_5_6p = 7, '5.6+'
80     YDS_5_7 = 8, '5.7'
81     YDS_5_7p = 9, '5.7+'
82     YDS_5_8 = 10, '5.8'
83     YDS_5_8p = 11, '5.8+'
84     YDS_5_9m = 12, '5.9-'
85     YDS_5_9 = 13, '5.9'
86     YDS_5_9p = 14, '5.9+'
87     YDS_5_10a = 15, '5.10a'
88     YDS_5_10b = 16, '5.10b'
89     YDS_5_10c = 17, '5.10c'
90     YDS_5_10d = 18, '5.10d'
91     YDS_5_11a = 19, '5.11a'
92     YDS_5_11b = 20, '5.11b'
93     YDS_5_11c = 21, '5.11c'
94     YDS_5_11d = 22, '5.11d'
95     YDS_5_12a = 23, '5.12a'
96     YDS_5_12b = 24, '5.12b'
97     YDS_5_12c = 25, '5.12c'
98     YDS_5_12d = 26, '5.12d'
99     YDS_5_13a = 27, '5.13a'
100     YDS_5_13b = 28, '5.13b'
101     YDS_5_13c = 29, '5.13c'
102     YDS_5_13d = 30, '5.13d'
103     YDS_5_14a = 31, '5.14a'
104     YDS_5_14b = 32, '5.14b'
105     YDS_5_14c = 33, '5.14c'
106     YDS_5_14d = 34, '5.14d'
107     YDS_5_15a = 35, '5.15a'
108     YDS_5_15b = 36, '5.15b'
109     YDS_5_15c = 37, '5.15c'
110     YDS_5_15d = 38, '5.15d'
111
112 SAFETY_CHOICES = (
113     ('G', 'G'),
114     ('PG', 'PG'),
115     ('PG13', 'PG13'),
116     ('R', 'R'),
117     ('X', 'X'),
118 )
119
120 class Pitch(models.Model):
121     route = models.ForeignKey(
122         Route,
123         on_delete=models.CASCADE,
124         related_name='pitches',
125     )
126     name = models.CharField(max_length=64, blank=True, null=True)
127     difficulty = models.PositiveIntegerField(choices=RouteDifficulty.choices)
128     safety = models.CharField(max_length=4, choices=SAFETY_CHOICES)
129     notes = models.TextField(blank=True, null=True)
130
131     class Meta:
132         verbose_name_plural = 'pitches'
133
134     def __str__(self):
135         if self.name:
136             return '{} ({})'.format(self.name, self.difficulty)
137         return 'Pitch ({})'.format(self.difficulty)
138
139 class Cluster(models.Model):
140     area = models.ForeignKey(
141         Area,
142         on_delete=models.CASCADE,
143         related_name='clusters',
144     )
145     name = models.CharField(max_length=64)
146     notes = models.TextField(blank=True, null=True)
147
148     def __str__(self):
149         return self.name
150
151 class Boulder(models.Model):
152     cluster = models.ForeignKey(
153         Cluster,
154         on_delete=models.CASCADE,
155         related_name='boulders',
156     )
157     name = models.CharField(max_length=64)
158     notes = models.TextField(blank=True, null=True)
159
160     def __str__(self):
161         return self.name
162
163 BOULDER_DIFFICULTY_CHOICES = (
164     ('V0', 'V0'),
165     ('V1', 'V1'),
166     ('V2', 'V2'),
167     ('V3', 'V3'),
168     ('V4', 'V4'),
169     ('V5', 'V5'),
170     ('V6', 'V6'),
171     ('V7', 'V7'),
172     ('V8', 'V8'),
173     ('V9', 'V9'),
174     ('V10', 'V10'),
175     ('V11', 'V11'),
176     ('V12', 'V12'),
177     ('V13', 'V13'),
178     ('V14', 'V14'),
179     ('V15', 'V15'),
180     ('V16', 'V16'),
181     ('V17', 'V17'),
182 )
183
184 class Problem(models.Model):
185     boulder = models.ForeignKey(
186         Boulder,
187         on_delete=models.CASCADE,
188         related_name='problems',
189     )
190     name = models.CharField(max_length=64)
191     difficulty = models.CharField(max_length=3, choices=BOULDER_DIFFICULTY_CHOICES)
192     safety = models.CharField(max_length=4, choices=SAFETY_CHOICES)
193     mountain_project = models.URLField(blank=True, null=True)
194     notes = models.TextField(blank=True, null=True)
195
196     def __str__(self):
197         return '{} ({})'.format(self.name, self.difficulty)
198
199 class RouteTodo(models.Model):
200     user = models.ForeignKey(User, on_delete=models.CASCADE)
201     route = models.ForeignKey(Route, on_delete=models.CASCADE)
202
203 class ProblemTodo(models.Model):
204     user = models.ForeignKey(User, on_delete=models.CASCADE)
205     problem = models.ForeignKey(Problem, on_delete=models.CASCADE)
206
207 class RouteTick(models.Model):
208     user = models.ForeignKey(User, on_delete=models.CASCADE)
209     route = models.ForeignKey(Route, on_delete=models.CASCADE)
210     timestamp = models.DateTimeField()
211     notes = models.TextField(blank=True, null=True)
212
213 class ProblemTick(models.Model):
214     user = models.ForeignKey(User, on_delete=models.CASCADE)
215     problem = models.ForeignKey(Problem, on_delete=models.CASCADE)
216     timestamp = models.DateTimeField()
217     notes = models.TextField(blank=True, null=True)