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

gcivil-nyu-org / team4-wed-fall25 / 18

22 Oct 2025 08:01PM UTC coverage: 48.812%. Remained the same
18

api

travis-pro

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

Syncing main with develop branch

15 of 77 new or added lines in 5 files covered. (19.48%)

66 existing lines in 4 files now uncovered.

226 of 463 relevant lines covered (48.81%)

0.49 hits per line

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

14.29
/note2webapp/views.py
1
from django.contrib.auth import login, logout
1✔
2
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
1✔
3
from django.contrib.auth.decorators import login_required
1✔
4
from django.contrib.auth.models import Group
1✔
5
from django.shortcuts import render, redirect, get_object_or_404
1✔
6
from django.contrib import messages
1✔
7
from django.http import JsonResponse
1✔
8
from django.utils import timezone
1✔
9
from .forms import UploadForm, VersionForm
1✔
10
from .models import ModelUpload, ModelVersion
1✔
11
from .utils import validate_model
1✔
12
import os
1✔
13

14

15
# -------------------
16
# SIGNUP
17
# -------------------
18
def signup_view(request):
1✔
19
    if request.method == "POST":
×
20
        form = UserCreationForm(request.POST)
×
21
        if form.is_valid():
×
22
            user = form.save()
×
23

24
            # By default, add every new user to "ModelUploader"
25
            group, created = Group.objects.get_or_create(name="ModelUploader")
×
26
            user.groups.add(group)
×
27

28
            login(request, user)
×
29
            return redirect("dashboard")
×
30
    else:
31
        form = UserCreationForm()
×
32
    return render(request, "note2webapp/signup.html", {"form": form})
×
33

34

35
# -------------------
36
# LOGIN
37
# -------------------
38
def login_view(request):
1✔
39
    if request.method == "POST":
×
40
        form = AuthenticationForm(request, data=request.POST)
×
41
        if form.is_valid():
×
42
            user = form.get_user()
×
43
            login(request, user)
×
44
            return redirect("dashboard")
×
45
    else:
46
        form = AuthenticationForm()
×
47
    return render(request, "note2webapp/login.html", {"form": form})
×
48

49

50
# -------------------
51
# LOGOUT
52
# -------------------
53
def logout_view(request):
1✔
54
    logout(request)
×
55
    return redirect("login")
×
56

57

58
# -------------------
59
# DASHBOARD (Role-based)
60
# -------------------
61
@login_required
1✔
62
def dashboard(request):
1✔
63
    """Redirect based on user role"""
64
    role = getattr(request.user.profile, "role", "uploader")  # fallback uploader
×
65
    if role == "uploader":
×
NEW
66
        return model_uploader_dashboard(request)  # goes to home.html
×
67
    elif role == "reviewer":
×
68
        return reviewer_dashboard(request)
×
69
    else:
70
        return render(request, "note2webapp/other_dashboard.html")
×
71

72

73
# -------------------
74
# MODEL UPLOADER DASHBOARD
75
# -------------------
76
@login_required
1✔
77
def model_uploader_dashboard(request):
1✔
78
    page = request.GET.get("page", "list")
×
79
    pk = request.GET.get("pk")
×
80

81
    # 👇 only show this uploader's models
82
    uploads = ModelUpload.objects.filter(user=request.user).order_by("-created_at")
×
83

84
    # Add active_versions_count to EVERY upload object
85
    for upload in uploads:
×
86
        upload.active_versions_count = upload.versions.filter(is_deleted=False).count()
×
87

88
    # ALWAYS add uploads to context first
89
    context = {"uploads": uploads, "page": page}
×
90

91
    # Create new upload
92
    if page == "create":
×
93
        if request.method == "POST":
×
94
            form = UploadForm(request.POST)
×
95
            if form.is_valid():
×
NEW
96
                model_name = form.cleaned_data["name"]
×
97

98
                # Check if model with same name already exists for this user
NEW
99
                if ModelUpload.objects.filter(
×
100
                    user=request.user, name=model_name
101
                ).exists():
NEW
102
                    messages.error(
×
103
                        request,
104
                        f"A model with the name '{model_name}' already exists. Please choose a different name.",
105
                    )
106
                    context["form"] = form
×
107
                    return render(request, "note2webapp/home.html", context)
×
108

UNCOV
109
                upload = form.save(commit=False)
×
UNCOV
110
                upload.user = request.user
×
111
                upload.save()
×
112
                messages.success(request, f"Model '{model_name}' created successfully!")
×
UNCOV
113
                return redirect(f"/dashboard/?page=detail&pk={upload.pk}")
×
114
        else:
115
            form = UploadForm()
×
116
        context["form"] = form
×
117

118
    # Upload details + versions
UNCOV
119
    elif page == "detail" and pk:
×
NEW
120
        upload = get_object_or_404(ModelUpload, pk=pk, user=request.user)
×
121
        versions = upload.versions.all().order_by("-created_at")
×
UNCOV
122
        context["upload"] = upload
×
UNCOV
123
        context["versions"] = versions
×
124

125
    # Add version
126
    elif page == "add_version" and pk:
×
127
        upload = get_object_or_404(ModelUpload, pk=pk, user=request.user)
×
128
        if request.method == "POST":
×
UNCOV
129
            form = VersionForm(request.POST, request.FILES)
×
UNCOV
130
            if form.is_valid():
×
131
                version = form.save(commit=False)
×
132
                version.upload = upload
×
133
                version.save()
×
134
                validate_model(version)
×
NEW
135
                messages.success(
×
136
                    request, f"Version '{version.tag}' uploaded successfully!"
137
                )
138
                return redirect(f"/dashboard/?page=detail&pk={upload.pk}")
×
139
        else:
140
            form = VersionForm()
×
141
        context["form"] = form
×
142
        context["upload"] = upload
×
143

UNCOV
144
    return render(request, "note2webapp/home.html", context)
×
145

146

147
# -------------------
148
# REVIEWER DASHBOARD (multi-mode like uploader)
149
# -------------------
150
@login_required
1✔
151
def reviewer_dashboard(request):
1✔
152
    """Unified reviewer dashboard: list, detail, feedback"""
UNCOV
153
    page = request.GET.get("page", "list")
×
UNCOV
154
    pk = request.GET.get("pk")
×
155

UNCOV
156
    context = {"page": page}
×
157

158
    # ---List all uploaded model versions---
UNCOV
159
    if page == "list":
×
160
        versions = ModelVersion.objects.all().order_by("-created_at")
×
161
        context["versions"] = versions
×
162

163
    # ---View details of a specific model version---
UNCOV
164
    elif page == "detail" and pk:
×
UNCOV
165
        version = get_object_or_404(ModelVersion, pk=pk)
×
166
        context["version"] = version
×
167
        # In future, we'll show validation logs, files, predictions, etc.
168

169
    # ---Add feedback (integrated, not separate view)---
UNCOV
170
    elif page == "add_feedback" and pk:
×
171
        version = get_object_or_404(ModelVersion, pk=pk)
×
172
        if request.method == "POST":
×
173
            comment = request.POST.get("comment", "")
×
UNCOV
174
            print(f"📝 Feedback for version {version.id}: {comment}")
×
175
            # Later, store in DB via Feedback model
UNCOV
176
            return redirect(f"/dashboard/?page=detail&pk={version.pk}")
×
177
        context["version"] = version
×
178

179
    return render(request, "note2webapp/reviewer.html", context)
×
180

181

182
# -------------------
183
# VERSION MANAGEMENT
184
# -------------------
185
@login_required
1✔
186
def model_versions(request, model_id):
1✔
187
    """View all versions of a model including deleted ones"""
UNCOV
188
    model_upload = get_object_or_404(ModelUpload, id=model_id)
×
189

190
    # Check permission - only owner or staff
UNCOV
191
    if request.user != model_upload.user and not request.user.is_staff:
×
UNCOV
192
        messages.error(request, "You don't have permission to view these versions.")
×
NEW
193
        return redirect("dashboard")
×
194

195
    # Get all versions including deleted
NEW
196
    versions = ModelVersion.objects.filter(upload=model_upload).order_by("-created_at")
×
197

198
    # Calculate counts
199
    total_count = versions.count()
×
200
    active_count = versions.filter(is_active=True, is_deleted=False).count()
×
UNCOV
201
    available_count = versions.filter(is_deleted=False).count()
×
UNCOV
202
    deleted_count = versions.filter(is_deleted=True).count()
×
203

UNCOV
204
    context = {
×
205
        "model_upload": model_upload,
206
        "versions": versions,
207
        "total_count": total_count,
208
        "active_count": active_count,
209
        "available_count": available_count,
210
        "deleted_count": deleted_count,
211
    }
212

NEW
213
    return render(request, "note2webapp/model_versions.html", context)
×
214

215

216
@login_required
1✔
217
def soft_delete_version(request, version_id):
1✔
218
    """Soft delete a model version"""
UNCOV
219
    version = get_object_or_404(ModelVersion, id=version_id)
×
220

221
    # Check permission - only uploader (owner) can delete
UNCOV
222
    if request.user != version.upload.user and not request.user.is_staff:
×
NEW
223
        if request.headers.get("X-Requested-With") == "XMLHttpRequest":
×
NEW
224
            return JsonResponse(
×
225
                {"success": False, "error": "Permission denied"}, status=403
226
            )
227
        messages.error(request, "You don't have permission to delete this version.")
×
NEW
228
        return redirect("dashboard")
×
229

230
    # Check if this is the active version
231
    if version.is_active:
×
232
        # Check if there are other non-deleted versions
233
        other_versions = ModelVersion.objects.filter(
×
234
            upload=version.upload, is_deleted=False
235
        ).exclude(id=version_id)
236

UNCOV
237
        if other_versions.exists():
×
NEW
238
            if request.headers.get("X-Requested-With") == "XMLHttpRequest":
×
NEW
239
                return JsonResponse(
×
240
                    {
241
                        "success": False,
242
                        "error": "Cannot delete active version. Please activate another version first.",
243
                    },
244
                    status=400,
245
                )
NEW
246
            messages.error(
×
247
                request,
248
                "Cannot delete active version. Please activate another version first.",
249
            )
NEW
250
            return redirect("dashboard")
×
251

NEW
252
    if request.method == "POST":
×
253
        # Soft delete
UNCOV
254
        version.is_deleted = True
×
UNCOV
255
        version.deleted_at = timezone.now()
×
UNCOV
256
        version.is_active = False
×
UNCOV
257
        version.save()
×
258

259
        # Delete physical files from media folder
NEW
260
        files_to_delete = [
×
261
            version.model_file,
262
            version.predict_file,
263
            version.schema_file,
264
        ]
265

UNCOV
266
        for file_field in files_to_delete:
×
UNCOV
267
            if file_field:
×
268
                try:
×
UNCOV
269
                    if os.path.isfile(file_field.path):
×
270
                        os.remove(file_field.path)
×
UNCOV
271
                except Exception as e:
×
272
                    print(f"Error deleting file: {e}")
×
273

NEW
274
        if request.headers.get("X-Requested-With") == "XMLHttpRequest":
×
NEW
275
            return JsonResponse(
×
276
                {
277
                    "success": True,
278
                    "message": f"Version (Tag: {version.tag}) deleted successfully",
279
                }
280
            )
281

NEW
282
        messages.success(
×
283
            request, f"Version with tag '{version.tag}' has been deleted successfully."
284
        )
NEW
285
        return redirect(f"/model-versions/{version.upload.id}/")
×
286

NEW
287
    return redirect("dashboard")
×
288

289

290
@login_required
1✔
291
def activate_version(request, version_id):
1✔
292
    """Activate a specific version and deactivate others"""
293
    version = get_object_or_404(ModelVersion, id=version_id)
×
294

295
    # Check permission - only uploader (owner) can activate
296
    if request.user != version.upload.user and not request.user.is_staff:
×
NEW
297
        if request.headers.get("X-Requested-With") == "XMLHttpRequest":
×
NEW
298
            return JsonResponse(
×
299
                {"success": False, "error": "Permission denied"}, status=403
300
            )
UNCOV
301
        messages.error(request, "You don't have permission to activate this version.")
×
NEW
302
        return redirect("dashboard")
×
303

304
    # Check if version is deleted
UNCOV
305
    if version.is_deleted:
×
NEW
306
        if request.headers.get("X-Requested-With") == "XMLHttpRequest":
×
NEW
307
            return JsonResponse(
×
308
                {"success": False, "error": "Cannot activate deleted version"},
309
                status=400,
310
            )
311
        messages.error(request, "Cannot activate a deleted version.")
×
NEW
312
        return redirect("dashboard")
×
313

NEW
314
    if request.method == "POST":
×
315
        # Deactivate all other versions of this model
NEW
316
        ModelVersion.objects.filter(upload=version.upload).update(is_active=False)
×
317

318
        # Activate this version
UNCOV
319
        version.is_active = True
×
UNCOV
320
        version.save()
×
321

NEW
322
        if request.headers.get("X-Requested-With") == "XMLHttpRequest":
×
NEW
323
            return JsonResponse(
×
324
                {
325
                    "success": True,
326
                    "message": f'Version with tag "{version.tag}" is now active',
327
                    "active_version_id": version.id,
328
                }
329
            )
330

331
        messages.success(request, f"Version with tag '{version.tag}' is now active.")
×
NEW
332
        return redirect(f"/model-versions/{version.upload.id}/")
×
333

NEW
334
    return redirect("dashboard")
×
335

336

337
@login_required
1✔
338
def delete_model(request, model_id):
1✔
339
    """Permanently delete a model if it has no non-deleted versions"""
UNCOV
340
    model_upload = get_object_or_404(ModelUpload, id=model_id)
×
341

342
    # Check permission - only owner or staff
343
    if request.user != model_upload.user and not request.user.is_staff:
×
NEW
344
        if request.headers.get("X-Requested-With") == "XMLHttpRequest":
×
NEW
345
            return JsonResponse(
×
346
                {"success": False, "error": "Permission denied"}, status=403
347
            )
348
        messages.error(request, "You don't have permission to delete this model.")
×
NEW
349
        return redirect("dashboard")
×
350

351
    # Check if model has any NON-DELETED versions
NEW
352
    active_version_count = ModelVersion.objects.filter(
×
353
        upload=model_upload, is_deleted=False
354
    ).count()
355

UNCOV
356
    if active_version_count > 0:
×
NEW
357
        if request.headers.get("X-Requested-With") == "XMLHttpRequest":
×
NEW
358
            return JsonResponse(
×
359
                {
360
                    "success": False,
361
                    "error": "Cannot delete model with active versions. Please delete all versions first.",
362
                },
363
                status=400,
364
            )
NEW
365
        messages.error(
×
366
            request,
367
            "Cannot delete model with active versions. Please delete all versions first.",
368
        )
NEW
369
        return redirect("dashboard")
×
370

NEW
371
    if request.method == "POST":
×
UNCOV
372
        model_name = model_upload.name
×
UNCOV
373
        model_upload.delete()
×
374

NEW
375
        if request.headers.get("X-Requested-With") == "XMLHttpRequest":
×
NEW
376
            return JsonResponse(
×
377
                {
378
                    "success": True,
379
                    "message": f'Model "{model_name}" has been permanently deleted.',
380
                }
381
            )
382

383
        messages.success(request, f'Model "{model_name}" has been permanently deleted.')
×
NEW
384
        return redirect("dashboard")
×
385

NEW
386
    return redirect("dashboard")
×
387

388

389
@login_required
1✔
390
def deprecate_version(request, version_id):
1✔
391
    """Deprecate (deactivate) a version"""
UNCOV
392
    version = get_object_or_404(ModelVersion, id=version_id)
×
393

394
    # Check permission - only uploader (owner) can deprecate
395
    if request.user != version.upload.user and not request.user.is_staff:
×
NEW
396
        if request.headers.get("X-Requested-With") == "XMLHttpRequest":
×
NEW
397
            return JsonResponse(
×
398
                {"success": False, "error": "Permission denied"}, status=403
399
            )
UNCOV
400
        messages.error(request, "You don't have permission to deprecate this version.")
×
NEW
401
        return redirect("dashboard")
×
402

403
    # Check if version is deleted
UNCOV
404
    if version.is_deleted:
×
NEW
405
        if request.headers.get("X-Requested-With") == "XMLHttpRequest":
×
NEW
406
            return JsonResponse(
×
407
                {"success": False, "error": "Cannot deprecate deleted version"},
408
                status=400,
409
            )
UNCOV
410
        messages.error(request, "Cannot deprecate a deleted version.")
×
NEW
411
        return redirect("dashboard")
×
412

NEW
413
    if request.method == "POST":
×
414
        # Deactivate this version
415
        version.is_active = False
×
416
        version.save()
×
417

NEW
418
        if request.headers.get("X-Requested-With") == "XMLHttpRequest":
×
NEW
419
            return JsonResponse(
×
420
                {
421
                    "success": True,
422
                    "message": f'Version with tag "{version.tag}" has been deprecated (deactivated)',
423
                    "version_id": version.id,
424
                }
425
            )
426

NEW
427
        messages.success(
×
428
            request, f"Version with tag '{version.tag}' has been deprecated."
429
        )
NEW
430
        return redirect(f"/model-versions/{version.upload.id}/")
×
431

NEW
432
    return redirect("dashboard")
×
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