Allow multiple file upload
This commit is contained in:
parent
e146f16f56
commit
a75c94d5c6
@ -12,3 +12,4 @@ class ListPosts(ListView):
|
||||
|
||||
class PostDetails(DetailView):
|
||||
model = Post
|
||||
template_name = "post_details.html"
|
||||
|
@ -1,11 +1,33 @@
|
||||
from django import forms
|
||||
from pyparsing import removeQuotes
|
||||
from .models import UploadImage
|
||||
from django.forms import ClearableFileInput, HiddenInput
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class DetectForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = UploadImage
|
||||
fields = [
|
||||
"image",
|
||||
]
|
||||
labels = {"image": ""}
|
||||
class MultipleFileInput(forms.ClearableFileInput):
|
||||
allow_multiple_selected = True
|
||||
|
||||
|
||||
class MultipleFileField(forms.FileField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
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,27 +22,43 @@ class DetectView(View):
|
||||
@method_decorator(login_required)
|
||||
def post(self, request, *args, **kwargs):
|
||||
form = self.form_class(request.POST, request.FILES)
|
||||
files = request.FILES.getlist("image")
|
||||
predictions = []
|
||||
if form.is_valid():
|
||||
image = form.save(commit=False)
|
||||
image.owner = request.user
|
||||
image.save()
|
||||
prediciton_results = predict_image(image)
|
||||
image.predicted_image_url = (
|
||||
f"{image.image.name}_predicted/{image.image.name.split('/')[-1]}"
|
||||
)
|
||||
image.save()
|
||||
PredictedImage.objects.create(
|
||||
original_image=image,
|
||||
image=image.predicted_image_url,
|
||||
confidence=prediciton_results[0]["confidence"],
|
||||
class_name=prediciton_results[0]["name"],
|
||||
)
|
||||
for f in files:
|
||||
image = UploadImage(
|
||||
image=f,
|
||||
)
|
||||
image.owner = request.user
|
||||
image.save()
|
||||
prediciton_results = predict_image(image)
|
||||
image.predicted_image_url = (
|
||||
f"{image.image.name}_predicted/{image.image.name.split('/')[-1]}"
|
||||
)
|
||||
image.save()
|
||||
try:
|
||||
results_metrics = prediciton_results[0]
|
||||
except IndexError as e:
|
||||
PredictedImage.objects.create(
|
||||
original_image=image,
|
||||
image=image.predicted_image_url,
|
||||
confidence=0.0,
|
||||
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(
|
||||
request,
|
||||
"upload.html",
|
||||
"results.html",
|
||||
{
|
||||
"img_saved": True,
|
||||
"img_url": image.predicted_image_url,
|
||||
"img_url": predictions,
|
||||
},
|
||||
)
|
||||
else:
|
||||
@ -53,7 +69,7 @@ class ListHistory(LoginRequiredMixin, ListView):
|
||||
model = PredictedImage
|
||||
queryset = PredictedImage.objects.all()
|
||||
template_name = "history.html"
|
||||
paginate_by = 4
|
||||
paginate_by = 3
|
||||
|
||||
def get_queryset(self) -> QuerySet[Any]:
|
||||
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{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 5%;
|
||||
justify-content:flex-end;
|
||||
align-items: center;
|
||||
background-color:lightgray;
|
||||
margin-bottom:5px;
|
||||
height: 45px;
|
||||
border: grey solid;
|
||||
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 {
|
||||
margin: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: right;
|
||||
flex-basis: auto;
|
||||
}
|
||||
@ -48,6 +49,7 @@ form{
|
||||
|
||||
.upload_button{
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
justify-content: 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">
|
||||
<p>Footer</p>
|
||||
</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>
|
||||
</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 %}
|
||||
<p id="description">Please login to submit image</p>
|
||||
<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>
|
||||
<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%}
|
||||
</div>
|
||||
<div class="upload_field">
|
||||
<label for="id_image", class="upload_button">
|
||||
<div class="upload_field">
|
||||
<label for="id_image", class="upload_button" id="image-preview-container">
|
||||
{{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;"/>
|
||||
{%else%}
|
||||
<img src="{%get_media_prefix%}{{img_url}}" alt="upload_image" style="width:640px; height:460px;">
|
||||
{%endif%}
|
||||
<img id="image-preview" src="{% static 'DetectionApp/images/upload_img.png' %}" alt="Image Preview" style="width:640px; height:460px;"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<script>
|
||||
document.getElementById('id_image').addEventListener('change', function() {
|
||||
var input = this;
|
||||
document.getElementById('id_image').addEventListener('change', function () {
|
||||
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();
|
||||
|
||||
reader.onload = function(e) {
|
||||
document.getElementById('image-preview').src = e.target.result;
|
||||
document.getElementById('image-preview').style.display = 'block';
|
||||
}
|
||||
reader.readAsDataURL(input.files[0]);
|
||||
|
||||
reader.readAsDataURL(input.files[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</form>
|
||||
{%endblock content%}
|
Loading…
Reference in New Issue
Block a user