• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gcivil-nyu-org / team4-wed-spring25 / 722

05 May 2025 06:05PM UTC coverage: 89.964% (+0.02%) from 89.942%
722

push

travis-pro

web-flow
Merge pull request #333 from gcivil-nyu-org/marvin_dev

Marvin dev

1981 of 2202 relevant lines covered (89.96%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.33
/parks/models.py
1
from django.db import models
1✔
2
from cloudinary.models import CloudinaryField
1✔
3
from django.contrib.auth.models import User
1✔
4
from django.utils import timezone
1✔
5
from django.utils.text import slugify
1✔
6
from django.urls import reverse
1✔
7

8

9
class DogRun(models.Model):
1✔
10
    id = models.CharField(max_length=255)
1✔
11
    prop_id = models.CharField(max_length=255, primary_key=True)
1✔
12
    name = models.CharField(max_length=255)
1✔
13
    address = models.CharField(max_length=255)
1✔
14
    dogruns_type = models.CharField(max_length=100)
1✔
15
    accessible = models.CharField(max_length=50)
1✔
16
    notes = models.TextField(max_length=255)
1✔
17
    image = models.ImageField(upload_to="dogruns/", null=True, blank=True)
1✔
18

19
    class Meta:
1✔
20
        db_table = "dog_runs"
1✔
21

22
    def __str__(self):
23
        return self.name
24

25

26
class DogRunNew(models.Model):
1✔
27
    id = models.CharField(max_length=255, primary_key=True)
1✔
28
    prop_id = models.CharField(max_length=255)
1✔
29
    name = models.CharField(max_length=255)
1✔
30
    address = models.CharField(max_length=255)
1✔
31
    dogruns_type = models.CharField(max_length=100)
1✔
32
    accessible = models.CharField(max_length=50)
1✔
33
    notes = models.TextField(max_length=255)
1✔
34
    google_name = models.TextField(null=True, blank=True)
1✔
35
    borough = models.TextField(null=True, blank=True)
1✔
36
    zip_code = models.TextField(null=True, blank=True)
1✔
37
    formatted_address = models.TextField(null=True, blank=True)
1✔
38
    latitude = models.FloatField(null=True, blank=True)
1✔
39
    longitude = models.FloatField(null=True, blank=True)
1✔
40
    additional = models.JSONField(null=True, blank=True)  # PostgreSQL JSONB
1✔
41
    display_name = models.TextField(null=False, blank=False)
1✔
42
    slug = models.SlugField(null=False, blank=False)
1✔
43

44
    class Meta:
1✔
45
        db_table = "dog_runs_new"
1✔
46

47
    def __str__(self):
48
        return self.name
49

50
    def save(self, *args, **kwargs):
1✔
51
        combined = f"{self.display_name}-{self.prop_id}"
1✔
52
        self.slug = slugify(combined)
1✔
53
        super().save(*args, **kwargs)
1✔
54

55
    # function to easily get url of park_detail
56
    def detail_page_url(self):
1✔
57
        return reverse("park_detail", kwargs={"slug": self.slug, "id": self.id})
1✔
58

59

60
class Review(models.Model):
1✔
61
    park = models.ForeignKey(
1✔
62
        DogRunNew, on_delete=models.CASCADE, related_name="reviews"
63
    )
64
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="reviews")
1✔
65
    text = models.TextField()
1✔
66
    rating = models.IntegerField()
1✔
67
    created_at = models.DateTimeField(auto_now_add=True)
1✔
68

69
    # Soft Delete fields
70
    is_removed = models.BooleanField(default=False)
1✔
71
    removed_at = models.DateTimeField(null=True, blank=True)
1✔
72
    removed_by = models.ForeignKey(
1✔
73
        User,
74
        null=True,
75
        blank=True,
76
        on_delete=models.SET_NULL,
77
        related_name="removed_reviews",
78
    )
79
    is_deleted = models.BooleanField(default=False)
1✔
80

81
    def has_active_replies(self):
1✔
82
        return self.replies.filter(is_deleted=False).exists()
×
83

84
    class Meta:
1✔
85
        db_table = "park_reviews"
1✔
86
        ordering = ["-created_at"]
1✔
87

88
    def __str__(self):
89
        return f"Review for {self.park.name} ({self.rating} stars)"
90

91

92
class ParkImage(models.Model):
1✔
93
    park = models.ForeignKey(
1✔
94
        DogRunNew,
95
        on_delete=models.CASCADE,
96
        related_name="images",
97
        to_field="id",
98
        db_column="park_id",
99
    )
100
    user = models.ForeignKey(
1✔
101
        User, on_delete=models.CASCADE, related_name="images", null=True, blank=True
102
    )
103
    review = models.ForeignKey(
1✔
104
        "Review", on_delete=models.CASCADE, null=True, blank=True, related_name="images"
105
    )
106
    image = CloudinaryField("image")
1✔
107

108
    # Soft deletion fields:
109
    is_removed = models.BooleanField(default=False)
1✔
110
    removed_at = models.DateTimeField(null=True, blank=True)
1✔
111
    removed_by = models.ForeignKey(
1✔
112
        User,
113
        on_delete=models.SET_NULL,
114
        null=True,
115
        blank=True,
116
        related_name="removed_images",
117
        help_text="Admin who removed the image",
118
    )
119

120
    def __str__(self):
121
        return f"Image for {self.park.name}"
122

123

124
class ReviewReport(models.Model):
1✔
125
    review = models.ForeignKey(
1✔
126
        "Review", on_delete=models.CASCADE, related_name="reports"
127
    )
128
    reported_by = models.ForeignKey(User, on_delete=models.CASCADE)
1✔
129
    reason = models.TextField()
1✔
130
    reported_at = models.DateTimeField(default=timezone.now)
1✔
131

132
    def __str__(self):
133
        return f"Reported by {self.reported_by.username} on {self.review.id}"
134

135

136
class ImageReport(models.Model):
1✔
137
    user = models.ForeignKey(
1✔
138
        User, on_delete=models.CASCADE, related_name="image_reports"
139
    )
140
    image = models.ForeignKey(
1✔
141
        ParkImage, on_delete=models.CASCADE, related_name="reports"
142
    )
143
    reason = models.TextField()
1✔
144
    created_at = models.DateTimeField(auto_now_add=True)
1✔
145

146
    def __str__(self):
147
        return f"Report by {self.user.username} on Image {self.image.id}"
148

149

150
class Reply(models.Model):
1✔
151
    review = models.ForeignKey(Review, on_delete=models.CASCADE, related_name="replies")
1✔
152
    user = models.ForeignKey(User, on_delete=models.CASCADE)
1✔
153
    parent_reply = models.ForeignKey(
1✔
154
        "self", on_delete=models.CASCADE, null=True, blank=True, related_name="children"
155
    )
156
    text = models.TextField()
1✔
157
    created_at = models.DateTimeField(auto_now_add=True)
1✔
158

159
    is_deleted = models.BooleanField(default=False)
1✔
160

161
    # Soft Delete fields
162
    is_removed = models.BooleanField(default=False)
1✔
163
    removed_at = models.DateTimeField(null=True, blank=True)
1✔
164
    removed_by = models.ForeignKey(
1✔
165
        User,
166
        null=True,
167
        blank=True,
168
        on_delete=models.SET_NULL,
169
        related_name="removed_replies",
170
    )
171

172
    is_removed_permanently = models.BooleanField(default=False)
1✔
173

174
    def has_active_children(self):
1✔
175
        return self.children.filter(is_deleted=False).exists()
×
176

177
    class Meta:
1✔
178
        ordering = ["created_at"]
1✔
179

180
    def __str__(self):
181
        return f"Reply by {self.user.username} on review {self.review.id}"
182

183

184
class ReplyReport(models.Model):
1✔
185
    reply = models.ForeignKey(Reply, on_delete=models.CASCADE, related_name="reports")
1✔
186
    user = models.ForeignKey(User, on_delete=models.CASCADE)
1✔
187
    reason = models.TextField()
1✔
188
    created_at = models.DateTimeField(auto_now_add=True)
1✔
189

190

191
class ParkPresence(models.Model):
1✔
192
    STATUS_CHOICES = [
1✔
193
        ("Current", "Current"),
194
        ("On their way", "On their way"),
195
    ]
196

197
    user = models.ForeignKey(
1✔
198
        User, on_delete=models.CASCADE, related_name="park_presences"
199
    )
200
    park = models.ForeignKey(
1✔
201
        DogRunNew, on_delete=models.CASCADE, related_name="presences"
202
    )
203
    status = models.CharField(max_length=20, choices=STATUS_CHOICES)
1✔
204

205
    # This is now the proper datetime for scheduled arrival
206
    time = models.DateTimeField(null=True, blank=True)
1✔
207

208
    checked_in_at = models.DateTimeField(auto_now_add=True)
1✔
209

210
    class Meta:
1✔
211
        db_table = "park_presence"
1✔
212
        unique_together = ("user", "park")
1✔
213
        ordering = ["-checked_in_at"]
1✔
214

215
    def __str__(self):
216
        return f"{self.user.username} - {self.park.display_name} ({self.status})"
217

218

219
class Message(models.Model):
1✔
220
    sender = models.ForeignKey(
1✔
221
        User, on_delete=models.CASCADE, related_name="sent_messages"
222
    )
223
    recipient = models.ForeignKey(
1✔
224
        User, on_delete=models.CASCADE, related_name="received_messages"
225
    )
226
    content = models.TextField()
1✔
227
    timestamp = models.DateTimeField(auto_now_add=True)
1✔
228

229
    class Meta:
1✔
230
        ordering = ["timestamp"]
1✔
231

232
    def __str__(self):
233
        return f"From {self.sender} to {self.recipient} at {self.timestamp}"
234

235

236
class ParkInfoReport(models.Model):
1✔
237
    park = models.ForeignKey(
1✔
238
        DogRunNew, on_delete=models.CASCADE, related_name="info_reports"
239
    )
240
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
1✔
241
    new_dogruns_type = models.CharField(
1✔
242
        max_length=20, choices=[("Off-Leash", "Off-Leash"), ("Run", "Run")]
243
    )
244
    new_accessible = models.BooleanField()
1✔
245
    submitted_at = models.DateTimeField(auto_now_add=True)
1✔
246
    is_handled = models.BooleanField(default=False)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc