268d7b0c2f53e85e614f9de56bee027ca559fcc2
[tickle] / tickle / models.py
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
5
6 class QQ:
7     def __xor__(self, other):
8         return (self & (~other)) | ((~self) & other)
9
10 Q.__bases__ += (QQ, )
11
12 class Boulder(models.Model):
13     name = models.CharField(max_length=64)
14     difficulty = models.ForeignKey(
15         'BoulderDifficulty',
16         null=True,
17         on_delete=models.PROTECT,
18         related_name='boulders',
19     )
20     mountainproject = models.URLField(null=True)
21
22 class BoulderDifficulty(models.Model):
23     order = models.PositiveSmallIntegerField()
24     name = models.CharField(max_length=8)
25
26 class Pitch(models.Model):
27     route = models.ForeignKey(
28         'Route',
29         on_delete=models.CASCADE,
30         related_name='pitches',
31     )
32     difficulty = models.ForeignKey(
33         'RouteDifficulty',
34         on_delete=models.PROTECT,
35         related_name='pitches',
36     )
37
38 PROTECTION_STYLE_CHOICES = (
39     ('sport', 'Sport'),
40     ('toprope', 'Top Rope'),
41     ('trad', 'Trad'),
42 )
43
44 class Route(models.Model):
45     name = models.CharField(max_length=64)
46     protection_style = models.CharField(max_length=8, choices=PROTECTION_STYLE_CHOICES)
47     mountainproject = models.URLField(null=True)
48
49     # TODO Write test for this
50     @property
51     def difficulty(self):
52         return self.pitches.order_by('-difficulty__order').first().difficulty
53
54 class RouteDifficulty(models.Model):
55     order = models.PositiveSmallIntegerField()
56     name = models.CharField(max_length=8)
57
58 ATTEMPT_RESULT_CHOICES = (
59     ('send', 'Sent'),
60     ('fall', 'Fall'),
61 )
62
63 PROTECTION_CHOICES = (
64     ('none', 'None'),
65     ('bolts', 'Bolts'),
66     ('gear', 'Gear'),
67     ('pad', 'Pad'),
68     ('tr', 'Top Rope'),
69 )
70
71 class Attempt(models.Model):
72     user = models.ForeignKey(User, on_delete=models.CASCADE)
73     notes = models.TextField()
74     boulder = models.ForeignKey('Boulder', null=True, on_delete=models.PROTECT, related_name='attempts')
75     route = models.ForeignKey('Route', null=True, on_delete=models.PROTECT, related_name='attempts')
76     result = models.CharField(max_length=8, choices=ATTEMPT_RESULT_CHOICES)
77     prior_knowledge = models.BooleanField(default=True)
78     protection_used = models.CharField(max_length=8, choices=PROTECTION_CHOICES)
79
80     class Meta:
81         constraints = (
82             models.CheckConstraint(
83                 check=(Q(boulder__isnull=True) ^ Q(route__isnull=True)),
84                 name='attempt_boulder_xor_route',
85             ),
86         )
87
88 STYLE_CHOICES = (
89     ('onsight', 'On Sight'),
90     ('flash', 'Flash'),
91     ('complete', 'Complete'),
92     ('project', 'project'),
93 )
94
95 class Todo(models.Model):
96     user = models.ForeignKey(User, on_delete=models.CASCADE)
97     notes = models.TextField()
98     protection = models.CharField(max_length=8, choices=PROTECTION_CHOICES)
99     boulder = models.ForeignKey('Boulder', null=True, on_delete=models.PROTECT, related_name='todos')
100     route = models.ForeignKey('Route', null=True, on_delete=models.PROTECT, related_name='todos')
101
102     class Meta:
103         constraints = (
104             models.CheckConstraint(
105                 check=(Q(boulder__isnull=True) ^ Q(route__isnull=True)),
106                 name='todo_boulder_xor_route',
107             ),
108         )