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"])],
|
||||
)
|
||||
predicted_image_url = models.CharField(max_length=500)
|
||||
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
|
||||
|
||||
class PredictedImage(models.Model):
|
||||
original_image = models.OneToOneField(UploadImage, on_delete=models.CASCADE)
|
||||
image = models.ImageField(upload_to=f"plankton/%Y/%m/%d/{original_image.name}")
|
||||
prediction_data = models.JSONField()
|
||||
date_predicted = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
@property
|
||||
def get_owner(self):
|
||||
return self.original_image.owner
|
||||
|
||||
@property
|
||||
def get_original_image(self):
|
||||
@ -34,3 +28,9 @@ class PredictedImage(models.Model):
|
||||
for pred in results:
|
||||
pred["confidence"] = round(pred["confidence"] * 100, 2)
|
||||
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(
|
||||
"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)
|
||||
|
@ -16,6 +16,6 @@ def predict_image(image):
|
||||
name=f"{image.image.name}_predicted",
|
||||
imgsz=[640, 640],
|
||||
)
|
||||
for r in results:
|
||||
x = r.tojson()
|
||||
return x
|
||||
for result in results:
|
||||
metrics = result.tojson()
|
||||
return metrics
|
||||
|
@ -1,15 +1,15 @@
|
||||
from typing import Any
|
||||
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 .forms import DetectForm
|
||||
from django.views.generic import ListView, DetailView
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.decorators import method_decorator
|
||||
from .models import UploadImage, PredictedImage
|
||||
from .models import PredictionBatch, UploadImage, PredictedImage
|
||||
from .utils import predict_image
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
import json
|
||||
|
||||
|
||||
class DetectView(View):
|
||||
@ -26,11 +26,11 @@ class DetectView(View):
|
||||
files = request.FILES.getlist("image")
|
||||
predictions = []
|
||||
if form.is_valid():
|
||||
pred_batch = PredictionBatch.objects.create(owner=request.user)
|
||||
for f in files:
|
||||
image = UploadImage(
|
||||
image=f,
|
||||
)
|
||||
image.owner = request.user
|
||||
image.save()
|
||||
prediciton_results = predict_image(image)
|
||||
image.predicted_image_url = (
|
||||
@ -43,7 +43,7 @@ class DetectView(View):
|
||||
predicted_image = PredictedImage.objects.create(
|
||||
original_image=image,
|
||||
image=image.predicted_image_url,
|
||||
prediction_data={"data": "no results"},
|
||||
prediction_data={"data": "no predicitions"},
|
||||
)
|
||||
else:
|
||||
predicted_image = PredictedImage.objects.create(
|
||||
@ -52,12 +52,14 @@ class DetectView(View):
|
||||
prediction_data=results_metrics,
|
||||
)
|
||||
predictions.append(predicted_image)
|
||||
pred_batch.images.add(*predictions)
|
||||
pred_batch.save()
|
||||
return render(
|
||||
request,
|
||||
"results.html",
|
||||
{
|
||||
"img_saved": True,
|
||||
"img_url": predictions,
|
||||
"img": pred_batch,
|
||||
},
|
||||
)
|
||||
else:
|
||||
@ -65,18 +67,28 @@ class DetectView(View):
|
||||
|
||||
|
||||
class ListHistory(LoginRequiredMixin, ListView):
|
||||
model = PredictedImage
|
||||
queryset = PredictedImage.objects.all()
|
||||
model = PredictionBatch
|
||||
queryset = PredictionBatch.objects.all()
|
||||
template_name = "history.html"
|
||||
paginate_by = 3
|
||||
|
||||
def get_queryset(self) -> QuerySet[Any]:
|
||||
queryset = PredictedImage.objects.filter(
|
||||
original_image__owner=self.request.user
|
||||
).order_by("-date_predicted")
|
||||
queryset = PredictionBatch.objects.filter(owner=self.request.user).order_by(
|
||||
"-date_predicted"
|
||||
)
|
||||
return queryset
|
||||
|
||||
|
||||
class DetectionDetails(LoginRequiredMixin, DetailView):
|
||||
model = PredictedImage
|
||||
template_name = "detection_detail.html"
|
||||
model = PredictionBatch
|
||||
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;
|
||||
}
|
||||
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;
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ form{
|
||||
border: 2px grey solid;
|
||||
padding: 0px 0px 40px 0px;
|
||||
margin-bottom: 5px;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
||||
@ -43,10 +43,9 @@ form{
|
||||
.upload_field {
|
||||
margin: auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-direction: column;
|
||||
justify-content: right;
|
||||
width:645px;
|
||||
height: 465px;
|
||||
}
|
||||
|
||||
|
||||
@ -84,4 +83,15 @@ form{
|
||||
font-size: large;
|
||||
text-decoration:solid;
|
||||
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%}
|
||||
<div class="history_list">
|
||||
<h2>History</h2>
|
||||
{% for detection in page_obj %}
|
||||
{% for detection in page_obj %}
|
||||
<div class="history_details">
|
||||
<a href="{%url 'detection-details' pk=detection.id %}">
|
||||
<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">
|
||||
<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>
|
||||
</a>
|
||||
|
@ -1,19 +1,27 @@
|
||||
{%extends 'base.html'%}
|
||||
{%load static%}
|
||||
{%block extracss%}
|
||||
<link rel="stylesheet" href="{% static 'DetectionApp/css/detection-detail.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'Community/css/post_details.css' %}">
|
||||
{%endblock extracss%}
|
||||
{%block content%}
|
||||
<div class="page-container">
|
||||
<div class="post">
|
||||
<p class="title">{{object.title}}</p>
|
||||
<p class="content">{{object.content}}</p>
|
||||
<p class="author">{{object.author}}</p>
|
||||
<div class="title-content">
|
||||
<p id="title">{{object.title}}</p>
|
||||
<p id="content">{{object.content}}</p>
|
||||
</div>
|
||||
<div class="author">
|
||||
<p id="author">{{object.author}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p id="comment-label">Comments:</p>
|
||||
<div class="comments">
|
||||
{%for comments in object.comment_set.all%}
|
||||
<p>{{comments.author}}</p>
|
||||
<p>{{comments.content}}</p>
|
||||
<p>{{comments.post}}</p>
|
||||
<div class="single-comment">
|
||||
<p id="comment-content">{{comments.content}}</p>
|
||||
<p id="comment-author">{{comments.author}}</p>
|
||||
</div>
|
||||
{%endfor%}
|
||||
</div>
|
||||
</div>
|
||||
{%endblock content%}
|
@ -12,7 +12,7 @@
|
||||
<button onclick="location.href='{% url 'login' %}'" id='submit' method="GET" type='button'>Login</button>
|
||||
{% else %}
|
||||
<p id="description">Results:</p>
|
||||
{%for img in img_url%}
|
||||
{%for img in img.images.all %}
|
||||
<p class="image_number">Image {{forloop.counter}}</p>
|
||||
{%for detection in img.get_prediction_data%}
|
||||
<div class="results">
|
||||
@ -25,10 +25,11 @@
|
||||
</div>
|
||||
<div class="upload_field">
|
||||
<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;">
|
||||
{%endfor%}
|
||||
</label>
|
||||
<button onclick="location.href='{%url 'download_predicitons' pk=img.id%}'" id="download_json" method="GET" type="button">Export predictions</button>
|
||||
</div>
|
||||
</form>
|
||||
<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>
|
||||
</form>
|
||||
{%endblock content%}
|
Loading…
Reference in New Issue
Block a user