Allow multiple file upload
This commit is contained in:
parent
e146f16f56
commit
a75c94d5c6
@ -12,3 +12,4 @@ class ListPosts(ListView):
|
|||||||
|
|
||||||
class PostDetails(DetailView):
|
class PostDetails(DetailView):
|
||||||
model = Post
|
model = Post
|
||||||
|
template_name = "post_details.html"
|
||||||
|
@ -1,11 +1,33 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
from pyparsing import removeQuotes
|
||||||
from .models import UploadImage
|
from .models import UploadImage
|
||||||
|
from django.forms import ClearableFileInput, HiddenInput
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
|
||||||
class DetectForm(forms.ModelForm):
|
class MultipleFileInput(forms.ClearableFileInput):
|
||||||
class Meta:
|
allow_multiple_selected = True
|
||||||
model = UploadImage
|
|
||||||
fields = [
|
|
||||||
"image",
|
class MultipleFileField(forms.FileField):
|
||||||
]
|
def __init__(self, *args, **kwargs):
|
||||||
labels = {"image": ""}
|
kwargs.setdefault("widget", MultipleFileInput())
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def clean(self, data, initial=None):
|
||||||
|
single_file_clean = super().clean
|
||||||
|
if isinstance(data, (list, tuple)):
|
||||||
|
result = [single_file_clean(d, initial) for d in data]
|
||||||
|
else:
|
||||||
|
result = single_file_clean(data, initial)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class DetectForm(forms.Form):
|
||||||
|
image = MultipleFileField()
|
||||||
|
predicted_image_url = forms.CharField(
|
||||||
|
max_length=500, widget=HiddenInput(), required=False
|
||||||
|
)
|
||||||
|
owner = forms.ModelChoiceField(
|
||||||
|
queryset=User.objects.all(), widget=HiddenInput(), required=False
|
||||||
|
)
|
||||||
|
@ -22,8 +22,13 @@ class DetectView(View):
|
|||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
form = self.form_class(request.POST, request.FILES)
|
form = self.form_class(request.POST, request.FILES)
|
||||||
|
files = request.FILES.getlist("image")
|
||||||
|
predictions = []
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
image = form.save(commit=False)
|
for f in files:
|
||||||
|
image = UploadImage(
|
||||||
|
image=f,
|
||||||
|
)
|
||||||
image.owner = request.user
|
image.owner = request.user
|
||||||
image.save()
|
image.save()
|
||||||
prediciton_results = predict_image(image)
|
prediciton_results = predict_image(image)
|
||||||
@ -31,18 +36,29 @@ class DetectView(View):
|
|||||||
f"{image.image.name}_predicted/{image.image.name.split('/')[-1]}"
|
f"{image.image.name}_predicted/{image.image.name.split('/')[-1]}"
|
||||||
)
|
)
|
||||||
image.save()
|
image.save()
|
||||||
|
try:
|
||||||
|
results_metrics = prediciton_results[0]
|
||||||
|
except IndexError as e:
|
||||||
PredictedImage.objects.create(
|
PredictedImage.objects.create(
|
||||||
original_image=image,
|
original_image=image,
|
||||||
image=image.predicted_image_url,
|
image=image.predicted_image_url,
|
||||||
confidence=prediciton_results[0]["confidence"],
|
confidence=0.0,
|
||||||
class_name=prediciton_results[0]["name"],
|
class_name="no predictions",
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
PredictedImage.objects.create(
|
||||||
|
original_image=image,
|
||||||
|
image=image.predicted_image_url,
|
||||||
|
confidence=results_metrics["confidence"],
|
||||||
|
class_name=results_metrics["name"],
|
||||||
|
)
|
||||||
|
predictions.append(image)
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"upload.html",
|
"results.html",
|
||||||
{
|
{
|
||||||
"img_saved": True,
|
"img_saved": True,
|
||||||
"img_url": image.predicted_image_url,
|
"img_url": predictions,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@ -53,7 +69,7 @@ class ListHistory(LoginRequiredMixin, ListView):
|
|||||||
model = PredictedImage
|
model = PredictedImage
|
||||||
queryset = PredictedImage.objects.all()
|
queryset = PredictedImage.objects.all()
|
||||||
template_name = "history.html"
|
template_name = "history.html"
|
||||||
paginate_by = 4
|
paginate_by = 3
|
||||||
|
|
||||||
def get_queryset(self) -> QuerySet[Any]:
|
def get_queryset(self) -> QuerySet[Any]:
|
||||||
queryset = PredictedImage.objects.filter(
|
queryset = PredictedImage.objects.filter(
|
||||||
|
Binary file not shown.
3
PlanktonDetector/static/Community/css/post_details.css
Normal file
3
PlanktonDetector/static/Community/css/post_details.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.class{
|
||||||
|
display:flex;
|
||||||
|
}
|
@ -16,13 +16,14 @@ body, html{
|
|||||||
.top-menu{
|
.top-menu{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
height: 5%;
|
|
||||||
justify-content:flex-end;
|
justify-content:flex-end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color:lightgray;
|
background-color:lightgray;
|
||||||
margin-bottom:5px;
|
margin-bottom:5px;
|
||||||
|
height: 45px;
|
||||||
border: grey solid;
|
border: grey solid;
|
||||||
border-width: 0px 2px 2px 2px;
|
border-width: 0px 2px 2px 2px;
|
||||||
|
transition: top 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
64
PlanktonDetector/static/DetectionApp/css/results.css
Normal file
64
PlanktonDetector/static/DetectionApp/css/results.css
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
form{
|
||||||
|
height:100%;
|
||||||
|
width:100%;
|
||||||
|
display:flex;
|
||||||
|
flex-direction:row;
|
||||||
|
margin:auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.side_menu{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items:center;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 25%;
|
||||||
|
background-color: lightgray;
|
||||||
|
border: grey solid;
|
||||||
|
border-width: 2px;
|
||||||
|
margin-bottom: 59px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#description{
|
||||||
|
font-size: 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#submit{
|
||||||
|
margin-bottom: 40px;
|
||||||
|
background-color: chartreuse;
|
||||||
|
margin-top: auto;
|
||||||
|
height: 50px;
|
||||||
|
width: 80%;
|
||||||
|
border-radius: 25px;
|
||||||
|
border: 2px white;
|
||||||
|
box-shadow: 0 0 0 4px white;
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload_field {
|
||||||
|
margin: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: right;
|
||||||
|
width:645px;
|
||||||
|
height: 465px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.upload_field label{
|
||||||
|
display:flex;
|
||||||
|
flex-direction: row;
|
||||||
|
overflow-x: scroll;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.upload_field label img{
|
||||||
|
margin-right:5px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -41,6 +41,7 @@ form{
|
|||||||
.upload_field {
|
.upload_field {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
flex-basis: auto;
|
flex-basis: auto;
|
||||||
}
|
}
|
||||||
@ -48,6 +49,7 @@ form{
|
|||||||
|
|
||||||
.upload_button{
|
.upload_button{
|
||||||
display:flex;
|
display:flex;
|
||||||
|
flex-direction: row;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
@ -57,6 +59,8 @@ form{
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.upload_field label img{
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,5 +27,17 @@
|
|||||||
<div class="footer">
|
<div class="footer">
|
||||||
<p>Footer</p>
|
<p>Footer</p>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
var prevScrollpos = window.pageYOffset;
|
||||||
|
window.onscroll = function() {
|
||||||
|
var currentScrollPos = window.pageYOffset;
|
||||||
|
if (prevScrollpos > currentScrollPos) {
|
||||||
|
document.getElementsByClassName("top-menu").style.top = "0";
|
||||||
|
} else {
|
||||||
|
document.getElementsByClassName("top-menu").style.top = "-50px";
|
||||||
|
}
|
||||||
|
prevScrollpos = currentScrollPos;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
19
PlanktonDetector/templates/post_details.html
Normal file
19
PlanktonDetector/templates/post_details.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{%extends 'base.html'%}
|
||||||
|
{%load static%}
|
||||||
|
{%block extracss%}
|
||||||
|
<link rel="stylesheet" href="{% static 'DetectionApp/css/detection-detail.css' %}">
|
||||||
|
{%endblock extracss%}
|
||||||
|
{%block content%}
|
||||||
|
<div class="post">
|
||||||
|
<p class="title">{{object.title}}</p>
|
||||||
|
<p class="content">{{object.content}}</p>
|
||||||
|
<p class="author">{{object.author}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="comments">
|
||||||
|
{%for comments in object.comment_set.all%}
|
||||||
|
<p>{{comments.author}}</p>
|
||||||
|
<p>{{comments.content}}</p>
|
||||||
|
<p>{{comments.post}}</p>
|
||||||
|
{%endfor%}
|
||||||
|
</div>
|
||||||
|
{%endblock content%}
|
26
PlanktonDetector/templates/results.html
Normal file
26
PlanktonDetector/templates/results.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{%load static%}
|
||||||
|
{%block extracss%}
|
||||||
|
<link rel="stylesheet" href="{%static 'DetectionApp/css/results.css' %}">
|
||||||
|
{%endblock extracss%}
|
||||||
|
{%block content%}
|
||||||
|
<form method="POST" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="side_menu">
|
||||||
|
{% if not user.is_authenticated %}
|
||||||
|
<p id="description">Please login to see results</p>
|
||||||
|
<button onclick="location.href='{% url 'login' %}'" id='submit' method="GET" type='button'>Login</button>
|
||||||
|
{% else %}
|
||||||
|
<p id="description">Results:</p>
|
||||||
|
<button onclick="location.href='{% url 'detect' %}'" id='submit' method="GET" type='button'>Submit again</button>
|
||||||
|
{%endif%}
|
||||||
|
</div>
|
||||||
|
<div class="upload_field">
|
||||||
|
<label>
|
||||||
|
{%for img in img_url%}
|
||||||
|
<img src="{%get_media_prefix%}{{img.predicted_image_url}}" alt="upload_image" style="width:640px; height:460px;">
|
||||||
|
{%endfor%}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{%endblock content%}
|
@ -10,40 +10,62 @@
|
|||||||
{% if not user.is_authenticated %}
|
{% if not user.is_authenticated %}
|
||||||
<p id="description">Please login to submit image</p>
|
<p id="description">Please login to submit image</p>
|
||||||
<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>
|
||||||
{% elif img_saved is None %}
|
{% else %}
|
||||||
<p id="description">Choose image for analysis</p>
|
<p id="description">Choose image for analysis</p>
|
||||||
<input type="submit" id="submit" value="Submit">
|
<input type="submit" id="submit" value="Submit">
|
||||||
{% else %}
|
|
||||||
<p id="description">Photo saved</p>
|
|
||||||
<button onclick="location.href='{% url 'detect' %}'" id='submit' method="GET" type='button'>Submit again</button>
|
|
||||||
{%endif%}
|
{%endif%}
|
||||||
</div>
|
</div>
|
||||||
<div class="upload_field">
|
<div class="upload_field">
|
||||||
<label for="id_image", class="upload_button">
|
<label for="id_image", class="upload_button" id="image-preview-container">
|
||||||
{{form.as_p}}
|
{{form.as_p}}
|
||||||
{% if not img_url %}
|
|
||||||
<img id="image-preview" src="{% static 'DetectionApp/images/upload_img.png' %}" alt="Image Preview" style="width:640px; height:460px;"/>
|
<img id="image-preview" src="{% static 'DetectionApp/images/upload_img.png' %}" alt="Image Preview" style="width:640px; height:460px;"/>
|
||||||
{%else%}
|
|
||||||
<img src="{%get_media_prefix%}{{img_url}}" alt="upload_image" style="width:640px; height:460px;">
|
|
||||||
{%endif%}
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<script>
|
<script>
|
||||||
document.getElementById('id_image').addEventListener('change', function() {
|
document.getElementById('id_image').addEventListener('change', function () {
|
||||||
var input = this;
|
var input = this;
|
||||||
|
var previewContainer = document.getElementById('image-preview-container');
|
||||||
|
|
||||||
if (input.files && input.files[0]) {
|
|
||||||
|
if (input.files && input.files.length > 1) {
|
||||||
|
document.getElementById('image-preview').style.display='none'
|
||||||
|
for (var i = 0; i < input.files.length; i++) {
|
||||||
|
(function (index) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = function (e) {
|
||||||
|
// Create a new image element for each preview
|
||||||
|
var img = document.createElement('img');
|
||||||
|
img.src = e.target.result;
|
||||||
|
img.id = 'image-preview'
|
||||||
|
img.style.display = 'block';
|
||||||
|
img.style.width='640px'
|
||||||
|
img.style.height='460px'
|
||||||
|
img.style.marginRight="5px"
|
||||||
|
|
||||||
|
// Append the image element to the container
|
||||||
|
previewContainer.style.overflowY='hidden'
|
||||||
|
previewContainer.style.overflowX='scroll'
|
||||||
|
previewContainer.style.width='640px'
|
||||||
|
previewContainer.style.height='460px'
|
||||||
|
previewContainer.appendChild(img);
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsDataURL(input.files[index]);
|
||||||
|
})(i);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = function(e) {
|
reader.onload = function(e) {
|
||||||
document.getElementById('image-preview').src = e.target.result;
|
document.getElementById('image-preview').src = e.target.result;
|
||||||
document.getElementById('image-preview').style.display = 'block';
|
document.getElementById('image-preview').style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.readAsDataURL(input.files[0]);
|
reader.readAsDataURL(input.files[0]);
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</form>
|
</form>
|
||||||
{%endblock content%}
|
{%endblock content%}
|
Loading…
Reference in New Issue
Block a user