Add button for exporting results, fix history
This commit is contained in:
parent
270bb92c4c
commit
c6ba3dd6b0
@ -0,0 +1,46 @@
|
|||||||
|
# Generated by Django 4.2.7 on 2024-01-04 19:37
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
("DetectionApp", "0011_remove_predictedimage_class_name_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="predictedimage",
|
||||||
|
name="date_predicted",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="uploadimage",
|
||||||
|
name="owner",
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="PredicitonBatch",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("date_predicted", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("images", models.ManyToManyField(to="DetectionApp.predictedimage")),
|
||||||
|
(
|
||||||
|
"owner",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.7 on 2024-01-04 19:49
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
("DetectionApp", "0012_remove_predictedimage_date_predicted_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameModel(
|
||||||
|
old_name="PredicitonBatch",
|
||||||
|
new_name="PredictionBatch",
|
||||||
|
),
|
||||||
|
]
|
@ -12,18 +12,12 @@ class UploadImage(models.Model):
|
|||||||
validators=[FileExtensionValidator(allowed_extensions=["jpg", "png"])],
|
validators=[FileExtensionValidator(allowed_extensions=["jpg", "png"])],
|
||||||
)
|
)
|
||||||
predicted_image_url = models.CharField(max_length=500)
|
predicted_image_url = models.CharField(max_length=500)
|
||||||
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
|
||||||
|
|
||||||
|
|
||||||
class PredictedImage(models.Model):
|
class PredictedImage(models.Model):
|
||||||
original_image = models.OneToOneField(UploadImage, on_delete=models.CASCADE)
|
original_image = models.OneToOneField(UploadImage, on_delete=models.CASCADE)
|
||||||
image = models.ImageField(upload_to=f"plankton/%Y/%m/%d/{original_image.name}")
|
image = models.ImageField(upload_to=f"plankton/%Y/%m/%d/{original_image.name}")
|
||||||
prediction_data = models.JSONField()
|
prediction_data = models.JSONField()
|
||||||
date_predicted = models.DateTimeField(auto_now_add=True)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def get_owner(self):
|
|
||||||
return self.original_image.owner
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def get_original_image(self):
|
def get_original_image(self):
|
||||||
@ -34,3 +28,9 @@ class PredictedImage(models.Model):
|
|||||||
for pred in results:
|
for pred in results:
|
||||||
pred["confidence"] = round(pred["confidence"] * 100, 2)
|
pred["confidence"] = round(pred["confidence"] * 100, 2)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
class PredictionBatch(models.Model):
|
||||||
|
images = models.ManyToManyField(PredictedImage)
|
||||||
|
date_predicted = models.DateTimeField(auto_now_add=True)
|
||||||
|
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
@ -10,4 +10,5 @@ urlpatterns = [
|
|||||||
path(
|
path(
|
||||||
"detection/<int:pk>", views.DetectionDetails.as_view(), name="detection-details"
|
"detection/<int:pk>", views.DetectionDetails.as_view(), name="detection-details"
|
||||||
),
|
),
|
||||||
|
path("export_pred/<int:pk>", views.download_pred_res, name="download_predicitons"),
|
||||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
@ -16,6 +16,6 @@ def predict_image(image):
|
|||||||
name=f"{image.image.name}_predicted",
|
name=f"{image.image.name}_predicted",
|
||||||
imgsz=[640, 640],
|
imgsz=[640, 640],
|
||||||
)
|
)
|
||||||
for r in results:
|
for result in results:
|
||||||
x = r.tojson()
|
metrics = result.tojson()
|
||||||
return x
|
return metrics
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
from django.shortcuts import render, redirect
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import render
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from .forms import DetectForm
|
from .forms import DetectForm
|
||||||
from django.views.generic import ListView, DetailView
|
from django.views.generic import ListView, DetailView
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from .models import UploadImage, PredictedImage
|
from .models import PredictionBatch, UploadImage, PredictedImage
|
||||||
from .utils import predict_image
|
from .utils import predict_image
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class DetectView(View):
|
class DetectView(View):
|
||||||
@ -26,11 +26,11 @@ class DetectView(View):
|
|||||||
files = request.FILES.getlist("image")
|
files = request.FILES.getlist("image")
|
||||||
predictions = []
|
predictions = []
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
pred_batch = PredictionBatch.objects.create(owner=request.user)
|
||||||
for f in files:
|
for f in files:
|
||||||
image = UploadImage(
|
image = UploadImage(
|
||||||
image=f,
|
image=f,
|
||||||
)
|
)
|
||||||
image.owner = request.user
|
|
||||||
image.save()
|
image.save()
|
||||||
prediciton_results = predict_image(image)
|
prediciton_results = predict_image(image)
|
||||||
image.predicted_image_url = (
|
image.predicted_image_url = (
|
||||||
@ -43,7 +43,7 @@ class DetectView(View):
|
|||||||
predicted_image = PredictedImage.objects.create(
|
predicted_image = PredictedImage.objects.create(
|
||||||
original_image=image,
|
original_image=image,
|
||||||
image=image.predicted_image_url,
|
image=image.predicted_image_url,
|
||||||
prediction_data={"data": "no results"},
|
prediction_data={"data": "no predicitions"},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
predicted_image = PredictedImage.objects.create(
|
predicted_image = PredictedImage.objects.create(
|
||||||
@ -52,12 +52,14 @@ class DetectView(View):
|
|||||||
prediction_data=results_metrics,
|
prediction_data=results_metrics,
|
||||||
)
|
)
|
||||||
predictions.append(predicted_image)
|
predictions.append(predicted_image)
|
||||||
|
pred_batch.images.add(*predictions)
|
||||||
|
pred_batch.save()
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"results.html",
|
"results.html",
|
||||||
{
|
{
|
||||||
"img_saved": True,
|
"img_saved": True,
|
||||||
"img_url": predictions,
|
"img": pred_batch,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@ -65,18 +67,28 @@ class DetectView(View):
|
|||||||
|
|
||||||
|
|
||||||
class ListHistory(LoginRequiredMixin, ListView):
|
class ListHistory(LoginRequiredMixin, ListView):
|
||||||
model = PredictedImage
|
model = PredictionBatch
|
||||||
queryset = PredictedImage.objects.all()
|
queryset = PredictionBatch.objects.all()
|
||||||
template_name = "history.html"
|
template_name = "history.html"
|
||||||
paginate_by = 3
|
paginate_by = 3
|
||||||
|
|
||||||
def get_queryset(self) -> QuerySet[Any]:
|
def get_queryset(self) -> QuerySet[Any]:
|
||||||
queryset = PredictedImage.objects.filter(
|
queryset = PredictionBatch.objects.filter(owner=self.request.user).order_by(
|
||||||
original_image__owner=self.request.user
|
"-date_predicted"
|
||||||
).order_by("-date_predicted")
|
)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class DetectionDetails(LoginRequiredMixin, DetailView):
|
class DetectionDetails(LoginRequiredMixin, DetailView):
|
||||||
model = PredictedImage
|
model = PredictionBatch
|
||||||
template_name = "detection_detail.html"
|
template_name = "results.html"
|
||||||
|
context_object_name = "img"
|
||||||
|
|
||||||
|
|
||||||
|
def download_pred_res(request, pk):
|
||||||
|
pred_batch = PredictionBatch.objects.get(pk=pk)
|
||||||
|
response = HttpResponse(content_type="text/plain")
|
||||||
|
response["Content-Disposition"] = "attachment; filename=predictions.txt"
|
||||||
|
for img in pred_batch.images.all():
|
||||||
|
response.write(img.prediction_data)
|
||||||
|
return response
|
||||||
|
Binary file not shown.
@ -1,3 +1,85 @@
|
|||||||
.class{
|
body, html{
|
||||||
|
height: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-container{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post{
|
||||||
display:flex;
|
display:flex;
|
||||||
}
|
margin-top:10px;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content:center;
|
||||||
|
align-items: flex-start;
|
||||||
|
border: 3px solid lightgray;
|
||||||
|
width: 80%;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-content{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#title{
|
||||||
|
font-size: 40px;
|
||||||
|
color: black;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content{
|
||||||
|
font-size: 20px;
|
||||||
|
color:black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author{
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#author{
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comments{
|
||||||
|
display: flex;
|
||||||
|
flex-direction:column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 80%;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#comment-label{
|
||||||
|
font-size: 30px;
|
||||||
|
align-self: flex-start;
|
||||||
|
margin-left: 10%;
|
||||||
|
color:black;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-comment{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 90%;
|
||||||
|
border-bottom: 3px solid lightgray;
|
||||||
|
margin-top:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#comment-author{
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
.history_list{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
@ -24,7 +24,7 @@ ul{
|
|||||||
display:flex;
|
display:flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: flex-start;
|
align-items: center;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ form{
|
|||||||
border: 2px grey solid;
|
border: 2px grey solid;
|
||||||
padding: 0px 0px 40px 0px;
|
padding: 0px 0px 40px 0px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -43,10 +43,9 @@ form{
|
|||||||
.upload_field {
|
.upload_field {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
width:645px;
|
width:645px;
|
||||||
height: 465px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -84,4 +83,15 @@ form{
|
|||||||
font-size: large;
|
font-size: large;
|
||||||
text-decoration:solid;
|
text-decoration:solid;
|
||||||
color: black;
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
#download_json{
|
||||||
|
background-color: chartreuse;
|
||||||
|
margin: 30px auto auto auto;
|
||||||
|
height: 50px;
|
||||||
|
width: 60%;
|
||||||
|
border-radius: 25px;
|
||||||
|
border: 2px white;
|
||||||
|
box-shadow: 0 0 0 4px white;
|
||||||
|
font-size: 25px;
|
||||||
}
|
}
|
@ -1,7 +0,0 @@
|
|||||||
{%extends 'base.html'%}
|
|
||||||
{%load static%}
|
|
||||||
{%block extracss%}
|
|
||||||
<link rel="stylesheet" href="{% static 'DetectionApp/css/detection-detail.css' %}">
|
|
||||||
{%endblock extracss%}
|
|
||||||
{%block content%}
|
|
||||||
{%endblock content%}
|
|
@ -6,14 +6,15 @@
|
|||||||
{%block content%}
|
{%block content%}
|
||||||
<div class="history_list">
|
<div class="history_list">
|
||||||
<h2>History</h2>
|
<h2>History</h2>
|
||||||
{% for detection in page_obj %}
|
{% for detection in page_obj %}
|
||||||
<div class="history_details">
|
<div class="history_details">
|
||||||
<a href="{%url 'detection-details' pk=detection.id %}">
|
<a href="{%url 'detection-details' pk=detection.id %}">
|
||||||
<div class="history_details">
|
<div class="history_details">
|
||||||
<img src="{{detection.image.url}}" alt="upload_image" style="width:100px; height:100px;">
|
<img src="{{detection.images.first.image.url}}" alt="upload_image" style="width:125px; height:125px;">
|
||||||
<div class="date_author">
|
<div class="date_author">
|
||||||
<p class="date">{{detection.date_predicted.date}}</p>
|
<p class="date">{{detection.date_predicted.date}}</p>
|
||||||
<p class="author">{{detection.get_owner}}</p>
|
<p class="author">{{detection.owner}}</p>
|
||||||
|
<p class="num_images">Images uploaded: {{detection.images.all.count}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,19 +1,27 @@
|
|||||||
{%extends 'base.html'%}
|
{%extends 'base.html'%}
|
||||||
{%load static%}
|
{%load static%}
|
||||||
{%block extracss%}
|
{%block extracss%}
|
||||||
<link rel="stylesheet" href="{% static 'DetectionApp/css/detection-detail.css' %}">
|
<link rel="stylesheet" href="{% static 'Community/css/post_details.css' %}">
|
||||||
{%endblock extracss%}
|
{%endblock extracss%}
|
||||||
{%block content%}
|
{%block content%}
|
||||||
|
<div class="page-container">
|
||||||
<div class="post">
|
<div class="post">
|
||||||
<p class="title">{{object.title}}</p>
|
<div class="title-content">
|
||||||
<p class="content">{{object.content}}</p>
|
<p id="title">{{object.title}}</p>
|
||||||
<p class="author">{{object.author}}</p>
|
<p id="content">{{object.content}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="author">
|
||||||
|
<p id="author">{{object.author}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p id="comment-label">Comments:</p>
|
||||||
<div class="comments">
|
<div class="comments">
|
||||||
{%for comments in object.comment_set.all%}
|
{%for comments in object.comment_set.all%}
|
||||||
<p>{{comments.author}}</p>
|
<div class="single-comment">
|
||||||
<p>{{comments.content}}</p>
|
<p id="comment-content">{{comments.content}}</p>
|
||||||
<p>{{comments.post}}</p>
|
<p id="comment-author">{{comments.author}}</p>
|
||||||
|
</div>
|
||||||
{%endfor%}
|
{%endfor%}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{%endblock content%}
|
{%endblock content%}
|
@ -12,7 +12,7 @@
|
|||||||
<button onclick="location.href='{% url 'login' %}'" id='submit' method="GET" type='button'>Login</button>
|
<button onclick="location.href='{% url 'login' %}'" id='submit' method="GET" type='button'>Login</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p id="description">Results:</p>
|
<p id="description">Results:</p>
|
||||||
{%for img in img_url%}
|
{%for img in img.images.all %}
|
||||||
<p class="image_number">Image {{forloop.counter}}</p>
|
<p class="image_number">Image {{forloop.counter}}</p>
|
||||||
{%for detection in img.get_prediction_data%}
|
{%for detection in img.get_prediction_data%}
|
||||||
<div class="results">
|
<div class="results">
|
||||||
@ -25,10 +25,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="upload_field">
|
<div class="upload_field">
|
||||||
<label>
|
<label>
|
||||||
{%for img in img_url%}
|
{%for img in img.images.all %}
|
||||||
<img src="{{img.image.url}}" alt="upload_image" style="width:640px; height:460px;">
|
<img src="{{img.image.url}}" alt="upload_image" style="width:640px; height:460px;">
|
||||||
{%endfor%}
|
{%endfor%}
|
||||||
</label>
|
</label>
|
||||||
|
<button onclick="location.href='{%url 'download_predicitons' pk=img.id%}'" id="download_json" method="GET" type="button">Export predictions</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<script>
|
<script>
|
||||||
|
@ -67,6 +67,16 @@ document.getElementById('id_image').addEventListener('change', function () {
|
|||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var user = `{{user.is_authenticated}}`
|
||||||
|
var upload = document.getElementById("id_image")
|
||||||
|
if (user.toLowerCase()=="false"){
|
||||||
|
console.log('disabled');
|
||||||
|
upload.disabled=true;
|
||||||
|
}else if (user.toLowerCase()=="true"){
|
||||||
|
console.log("enabled");
|
||||||
|
upload.disabled=false;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</form>
|
</form>
|
||||||
{%endblock content%}
|
{%endblock content%}
|
Loading…
Reference in New Issue
Block a user