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

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

02 Apr 2025 11:02PM UTC coverage: 96.534% (-0.2%) from 96.692%
343

cron

travis-pro

web-flow
Merge pull request #191 from gcivil-nyu-org/develop

Merge Develop to main

133 of 137 new or added lines in 6 files covered. (97.08%)

4 existing lines in 2 files now uncovered.

752 of 779 relevant lines covered (96.53%)

0.97 hits per line

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

88.43
/parks/views.py
1
from django.shortcuts import render, get_object_or_404, redirect
1✔
2
from django.http import HttpResponse  # noqa: F401  # Ignore "imported but unused"
1✔
3
from django.db.models import OuterRef, Subquery, CharField
1✔
4
from django.db.models.functions import Cast
1✔
5
from .models import DogRunNew, Review, ParkImage, ReviewReport, ImageReport
1✔
6
from django.forms.models import model_to_dict
1✔
7

8
import folium
1✔
9
from folium.plugins import MarkerCluster
1✔
10

11
from .utilities import folium_cluster_styling
1✔
12

13
from django.contrib.auth import login
1✔
14
from .forms import RegisterForm
1✔
15
import json
1✔
16
from django.db.models import Q  # Import Q for complex queries
1✔
17

18
from django.contrib.auth.decorators import login_required
1✔
19
from django.db.models import Avg
1✔
20
from django.http import HttpResponseForbidden
1✔
21

22

23
def register_view(request):
1✔
24
    if request.method == "POST":
1✔
25
        form = RegisterForm(request.POST)
1✔
26
        if form.is_valid():
1✔
27
            user = form.save()  # Save user
1✔
28
            login(request, user)  # Log in the user immediately
1✔
29
            request.session.save()  # Ensure session is updated
1✔
30
            return redirect("home")  # Redirect to homepage
1✔
31
    else:
32
        form = RegisterForm()
1✔
33

34
    return render(request, "parks/register.html", {"form": form})
1✔
35

36

37
def park_list(request):
1✔
38
    query = request.GET.get("query", "")
1✔
39
    parks = DogRunNew.objects.all()  # Fetch all dog runs from the database
1✔
40

41
    if query:
1✔
42
        parks = parks.filter(
×
43
            Q(name__icontains=query)
44
            | Q(google_name__icontains=query)
45
            | Q(zip_code__icontains=query)
46
        )
47

48
    return render(request, "parks/park_list.html", {"parks": parks, "query": query})
1✔
49

50

51
def home_view(request):
1✔
52
    return render(request, "parks/home.html")
1✔
53

54

55
def map(request):
1✔
56

57
    NYC_LAT_AND_LONG = (40.730610, -73.935242)
1✔
58
    # Create map centered on NYC
59
    m = folium.Map(location=NYC_LAT_AND_LONG, zoom_start=11)
1✔
60

61
    icon_create_function = folium_cluster_styling("rgb(0, 128, 0)")
1✔
62

63
    marker_cluster = MarkerCluster(icon_create_function=icon_create_function).add_to(m)
1✔
64

65
    # Fetch all dog runs from the database
66
    parks = DogRunNew.objects.all()
1✔
67

68
    # Mark every park on the map
69
    for park in parks:
1✔
70
        park_name = park.name
×
71

72
        folium.Marker(
×
73
            location=(park.latitude, park.longitude),
74
            icon=folium.Icon(icon="dog", prefix="fa", color="green"),
75
            popup=folium.Popup(park_name, max_width=200),
76
        ).add_to(marker_cluster)
77

78
    # represent map as html
79
    context = {"map": m._repr_html_()}
1✔
80
    return render(request, "parks/map.html", context)
1✔
81

82

83
def park_and_map(request):
1✔
84
    # Get filter values from GET request
85
    query = request.GET.get("query", "").strip()
1✔
86
    filter_value = request.GET.get("filter", "").strip()
1✔
87
    accessible_value = request.GET.get("accessible", "").strip()
1✔
88
    borough_value = request.GET.get("borough", "").strip().upper()
1✔
89

90
    thumbnail = ParkImage.objects.filter(park_id=OuterRef("pk")).values("image")[:1]
1✔
91

92
    # Fetch all dog runs from the database
93
    parks = (
1✔
94
        DogRunNew.objects.all()
95
        .order_by("id")
96
        .prefetch_related("images")
97
        .annotate(thumbnail_url=Cast(Subquery(thumbnail), output_field=CharField()))
98
    )
99

100
    # Search by ZIP, name, or Google name
101
    if query:
1✔
102
        parks = parks.filter(
×
103
            Q(name__icontains=query)
104
            | Q(google_name__icontains=query)
105
            | Q(zip_code__icontains=query)
106
        )
107

108
    # Filter by park type (e.g., "Off-Leash")
109
    if filter_value:
1✔
110
        parks = parks.filter(dogruns_type__iexact=filter_value)
×
111

112
    # Filter by accessibility only if explicitly set to "True" or "False"
113
    if accessible_value == "True":
1✔
114
        parks = parks.filter(accessible=True)
×
115
    elif accessible_value == "False":
1✔
116
        parks = parks.filter(accessible=False)
×
117

118
    if borough_value:
1✔
119
        parks = parks.filter(borough=borough_value)
1✔
120
    # Convert parks to JSON (for JS use)
121
    parks_json = json.dumps(list(parks.values()))
1✔
122

123
    # Render the template
124
    return render(
1✔
125
        request,
126
        "parks/combined_view.html",
127
        {
128
            "parks": parks,
129
            "parks_json": parks_json,
130
            "query": query,
131
            "selected_type": filter_value,
132
            "selected_accessible": accessible_value,
133
            "selected_borough": borough_value,
134
        },
135
    )
136

137

138
def park_detail(request, id):
1✔
139
    park = get_object_or_404(DogRunNew, id=id)
1✔
140
    images = ParkImage.objects.filter(park=park)
1✔
141
    reviews = park.reviews.all()
1✔
142
    average_rating = reviews.aggregate(Avg("rating"))["rating__avg"]
1✔
143

144
    if request.user.is_authenticated and request.method == "POST":
1✔
145
        form_type = request.POST.get("form_type")
1✔
146

147
        if form_type == "upload_image" and request.FILES.getlist("images"):
1✔
UNCOV
148
            for image in request.FILES.getlist("images"):
×
NEW
149
                ParkImage.objects.create(park=park, image=image, user=request.user)
×
NEW
150
            return redirect("park_detail", id=park.id)
×
151

152
        elif form_type == "submit_review":
1✔
153
            review_text = request.POST.get("text", "").strip()
1✔
154
            rating_value = request.POST.get("rating", "").strip()
1✔
155

156
            if not rating_value.isdigit():
1✔
157
                return render(
×
158
                    request,
159
                    "parks/park_detail.html",
160
                    {
161
                        "park": park,
162
                        "images": images,
163
                        "reviews": reviews,
164
                        "error_message": "Please select a valid rating!",
165
                        "average_rating": average_rating,
166
                    },
167
                )
168

169
            rating = int(rating_value)
1✔
170
            if rating < 1 or rating > 5:
1✔
UNCOV
171
                return render(
×
172
                    request,
173
                    "parks/park_detail.html",
174
                    {
175
                        "park": park,
176
                        "images": images,
177
                        "reviews": reviews,
178
                        "error_message": "Rating must be between 1 and 5 stars!",
179
                        "average_rating": average_rating,
180
                    },
181
                )
182

183
            Review.objects.create(
1✔
184
                park=park, text=review_text, rating=rating, user=request.user
185
            )
186
            return redirect("park_detail", id=park.id)
1✔
187
        # report reviews
188
        elif form_type == "report_review":
1✔
189
            if request.user.is_authenticated:
1✔
190
                review_id = request.POST.get("review_id")
1✔
191
                reason = request.POST.get("reason", "").strip()
1✔
192
            if review_id and reason:
1✔
193
                review = get_object_or_404(Review, id=review_id)
1✔
194
                ReviewReport.objects.create(
1✔
195
                    review=review, reported_by=request.user, reason=reason
196
                )
197
                return redirect("park_detail", id=park.id)
1✔
198

199
    park_json = json.dumps(model_to_dict(park))
1✔
200

201
    return render(
1✔
202
        request,
203
        "parks/park_detail.html",
204
        {
205
            "park": park,
206
            "images": images,
207
            "reviews": reviews,
208
            "park_json": park_json,
209
            "average_rating": average_rating,
210
        },
211
    )
212

213

214
@login_required
1✔
215
def delete_review(request, review_id):
1✔
216
    review = get_object_or_404(Review, id=review_id)
1✔
217
    if request.user == review.user:
1✔
218
        review.delete()
1✔
219
        return redirect("park_detail", id=review.park.id)
1✔
220
    else:
NEW
221
        return HttpResponseForbidden("You are not allowed to delete this review.")
×
222

223

224
@login_required
1✔
225
def delete_image(request, image_id):
1✔
226
    image = get_object_or_404(ParkImage, id=image_id)
1✔
227
    if image.user == request.user:
1✔
228
        park_id = image.park.id
1✔
229
        image.delete()
1✔
230
        return redirect("park_detail", id=park_id)
1✔
NEW
231
    return HttpResponseForbidden("You are not allowed to delete this image.")
×
232

233

234
def contact_view(request):
1✔
235
    return render(request, "parks/contact.html")
1✔
236

237

238
@login_required
1✔
239
def report_image(request, image_id):
1✔
240
    image = get_object_or_404(ParkImage, id=image_id)
1✔
241
    if request.method == "POST":
1✔
242
        reason = request.POST.get("reason", "").strip()
1✔
243
        if reason:
1✔
244
            ImageReport.objects.create(user=request.user, image=image, reason=reason)
1✔
245
            return redirect("park_detail", id=image.park.id)
1✔
246
    return redirect("park_detail", id=image.park.id)
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