Add button for exporting results, fix history

This commit is contained in:
Piotr Szkudlarek 2024-01-04 21:33:13 +01:00
parent 270bb92c4c
commit c6ba3dd6b0
16 changed files with 229 additions and 54 deletions

View File

@ -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,
),
),
],
),
]

View File

@ -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",
),
]

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -1,7 +0,0 @@
.history_list{
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100%;
}

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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%}

View File

@ -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>

View File

@ -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%}

View File

@ -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>

View File

@ -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%}