How to handle three functions with celery in django? - python

I'm having a three function in my django site views but I can only run one. I have never used celery, can you help me to transform this into the celery tasks?
As you can see, I want to save document which is uploaded by user, and then I want to do some pandas stuff with that file, and after that I want to show pandas stuff in html page.
This is forms.py
class DocumentForm(forms.Form):
docfile = forms.FileField(label='Select a file')
This is views.py
def save_exls(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile=request.FILES['docfile'])
newdoc.save()
return redirect('html_exls')
else:
form = DocumentForm()
documents = Document.objects.all()
context = {'documents': documents, 'form': form,}
return render(request, 'list.html', context)
def pandas_exls(request):
if request.method == "POST":
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
output = io.BytesIO()
newdoc = request.FILES['docfile']
dfs = pd.read_excel(newdoc, sheet_name=None, index_col=[0])
writer = pd.ExcelWriter(output)
for name, df in dfs.items():
#pandas stuff
done.to_excel(writer, sheet_name=name)
output.seek(0)
response = HttpResponse(
output, content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename=%s' % filename
return response
else:
form = DocumentForm()
return render(request, 'list.html', {'form': form})
def html_exls(request):
if request.method == "POST":
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
output = io.BytesIO()
newdoc = request.FILES['docfile']
dfs = pd.read_excel(newdoc, sheet_name=None, index_col=[0])
writer = pd.ExcelWriter(output)
for name, df in dfs.items():
#pandas stuff for html
done.to_excel(writer, sheet_name=name)
html = done.to_html()
print(html)
output.seek(0)
response = HttpResponse(
output, content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename=%s' % filename
return response
else:
form = DocumentForm()
return render(request, 'list.html', {'form': form})
This is html file, list.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Django site</title>
</head>
<body>
<!-- Upload form. Note enctype attribute! -->
<form action="{% url "pandas_exls" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
<p>
{{ form.docfile.errors }}
{{ form.docfile }}
</p>
<p><input type="submit" value="Upload"/></p>
</form>
<br/>
{{html|safe}}
</body>
</html>

I use task_api for this, this is a django project that keeps track of the functions and provides some javascript to poll the backend for a change and updates when the functions are done running.
It does not yet seem to support file parameters, but you could save the file first, and then in javascript call the next method to operate on that file once saved, and display progress.
I would write 3 endpoints, 1 to upload the file, using the form, then 1 ajax endpoint you call in javascript to process the file, this will return when it is done, at which point you can redirect to the 3rd endpoint which will download the file.
e.g.
views.py
from task_api.tasks import Task
from task_api.params import IntParameter, StringParameter
class Pandas(Task):
name = 'pandas'
inputs = {
'documentid': IntParameter(),
}
outputs = {'output': StringParameter()}
def run(self, documentid):
self.progress = 0
output = io.BytesIO()
newdoc = Document.objects.get(id=documentid)
dfs = pd.read_excel(newdoc.docfile, sheet_name=None, index_col=[0])
writer = pd.ExcelWriter(output)
for name, df in dfs.items():
#pandas stuff
self.progress += 1 # update progress so we can show something to user in javascript
done.to_excel(writer, sheet_name=name)
output.seek(0)
Document.done = True
Document.output = output
Document.save()
return documentid
# configure /download in your urls.py to point to this view with documentid parameter
def download_exls(request, documentid):
response = HttpResponse(
Document.objects.get(id=documentid).output, content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename=%s' % filename
return response
def save_exls(request):
if request.method == 'POST' and request.is_ajax():
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile=request.FILES['docfile'])
newdoc.save()
return HttpResponse(json.dumps({'documentid': newdoc.id}), content_type="application/json")
else:
form = DocumentForm()
documents = Document.objects.all()
context = {'documents': documents, 'form': form,}
return render(request, 'list.html', context)
and in your list.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Django site</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="{% static 'django-task-api.min.js' %}"></script>
<script type="text/javascript">
function runtask(task, documentid, progressdivname) {
TaskAPI.run(task, documentid, function(json) {
if (json.target === null || json.progress === null) {
return false
}
document.getElementById(progressdivname).innerHTML = 'Progress: ' + json.progress + ' / ' + json.target
})
.then(function(json) {
# panda's is done, download doc
window.location = '/download/' + json.outputs.documentid;
})
return false
}
$(document).ready(function (e) {
$("#form").on('submit',(function(e) {
e.preventDefault();
$.ajax({
url: "/upload",
type: "POST",
data: new FormData(this),
contentType: false,
cache: false,
processData:false,
beforeSend : function()
{
//$("#preview").fadeOut();
$("#err").fadeOut();
},
success: function(documentid)
{
// process uploaded file.
runtask('pandas', {'documentid': documentid}, 'progress')
}
,
error: function(e)
{
$("#err").html(e).fadeIn();
}
});
}));
});
</script>
</head>
<body>
<!-- Upload form. Note enctype attribute! -->
<form action="{% url "pandas_exls" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
<p>
{{ form.docfile.errors }}
{{ form.docfile }}
</p>
<p><input type="submit" value="Upload"/></p>
</form>
<br/>
<div id="progress"></div>
</body>
</html>
Be advised, this will allow anyone to download any file, just based on the id, you will probably want to add some authentication checks if this is not intended, you might also want to do some garbage collection and cleanup because this will keep all documents in your database.

Related

Field value is not displayed for editing in the form Django?

views.py
dicfacultets = DicFacultets.objects.all()
disfacultetsedit = DicFacultets.objects.get(id=id)
form = FacultetsForm(request.POST, instance=disfacultetsedit)
if request.method == 'GET':
return render(request, 'tao/dicfacultetsedit.html', {'dicfacultets': dicfacultets,
'form': FacultetsForm(),
})
else:
try:
if form.is_valid():
disfacultetsedit = form.save(commit=False)
title = request.POST.get('title')
disfacultetsedit.title = title
disfacultetsedit.save()
return redirect('dicfacultets')
except TypeError:
return render(request, 'tao/dicfacultets.html', {'dicfacultets': dicfacultets,
'error': 'error',
'form': form,
})
return render(request, 'tao/dicfacultets.html', {'dicfacultets': dicfacultets, })
html
{% for fac in dicfacultets %}
...
# *call modal window for editing*
<a href="facultetsedit/{{ fac.id }}" class="on-default edit-row" data-toggle="modal"
data-target="#facultetsedit{{fac.id}}"><i class="fa fa-pencil"></i></a>
# next call form method="POST" action="{% url 'facultetsedit' id=fac.id %}"> {{ form }} form in modal window
when a modal window is called, the field is empty, but editing and saving work

how to us the prefix?

I try to upload two forms with one submit button.
A user can select a pdf file and a excel file. And then uploading both files. And then the contents of both are returned.
So I try to upload both files with one submit button.
But the two selected file options are not visible for uploading the files.
So I have the template like this:
{% extends 'base.html' %} {% load static %} {% block content %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Create a Profile</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="{% static 'main/css/custom-style.css' %}" />
<link rel="stylesheet" type="text/css" href="{% static 'main/css/bootstrap.css' %}" />
</head>
<body>
<div class="container center">
<span class="form-inline" role="form">
<div class="inline-div">
<form class="form-inline" action="/controlepunt140" method="POST" enctype="multipart/form-data">
<div class="d-grid gap-3">
<div class="form-group">
{% csrf_token %}
{{ form.0.as_p }}
<button type="submit" name="form_pdf" class="btn btn-warning">Upload!</button>
</div>
<div class="form-outline">
<div class="form-group">
<textarea class="inline-txtarea form-control" id="content" cols="70" rows="25">
{{content}}</textarea>
</div>
</div>
</div>
<div class="d-grid gap-3">
<div class="form-group">
{{ form.1.as_p }}
</div>
<div class="form-outline">
<div class="form-group">
<textarea class="inline-txtarea form-control" id="content" cols="70" rows="25">
{{conten_excel}}</textarea>
</div>
</div>
</div>
</form>
</div>
</span>
</div>
</body>
</html>
{% endblock content %}
and the views.py:
class ReadingFile(View):
def get(self, *args, **kwargs):
return render(self.request, "main/controle_punt140.html", {
"form1": UploadFileForm(),
"form2": ExcelForm()
})
def post(self, *args, **kwargs):
filter_text = FilterText()
types_of_encoding = ["utf8", "cp1252"]
form1 = UploadFileForm(
self.request.POST, self.request.FILES, prefix="form1")
form2 = ExcelForm(self.request.FILES,
self.request.FILES, prefix="form2")
content = ''
content_excel = ''
if form1.is_valid() and form2.is_valid() and self.request.POST:
uploadfile = UploadFile(image=self.request.FILES["upload_file"])
excel_file = self.request.FILES["upload_file"]
uploadfile.save()
for encoding_type in types_of_encoding:
with open(os.path.join(settings.MEDIA_ROOT, f"{uploadfile.image}"), 'r', encoding=encoding_type) as f:
if uploadfile.image.path.endswith('.pdf'):
content = filter_text.show_extracted_data_from_file(
uploadfile.image.path)
else:
content = f.read()
if uploadfile.image.path.endswith('xlsx'):
wb = openpyxl.load_workbook(excel_file)
worksheet = wb['Sheet1']
print(worksheet)
excel_data = list()
for row in worksheet.iter_rows():
row_data = list()
for cell in row:
row_data.append(str(cell.value))
excel_data.append(row_data)
print(excel_data)
content_excel = excel_data
else:
content_excel = f.read()
return render(self.request, "main/controle_punt140.html", {
'form1': ExcelForm(),
'form2': UploadFileForm(),
"content": [content, content_excel]
})
# I've adjusted the indent here to what I think it should be.
return render(self.request, "main/controle_punt140.html", {
"form1": form1,
"form2": form2,
})
and forms.py:
class UploadFileForm(forms.Form):
upload_file = forms.FileField(required=False)
class ExcelForm(forms.Form):
upload_file = forms.FileField(required=False)
urls.py:
urlpatterns = [
path('', views.starting_page, name='starting_page'),
path('controlepunt140', views.ReadingFile.as_view(), name='controlepunt140'),
]
The variable name used in the template is the key of the dictionary, not the value. The value is what is inserted into the template when django renders the page.
You have {{form1.as__p}} in your template, but you send "form": [form1, form2] as your context, so the variable in the template should be {{ form.0.as_p }} and {{ form.1.as_p }}. I haven't tested this, but if it doesn't work, you could just send the two forms separately like:
from django.shortcuts import redirect
class ReadingFile(View):
def get(self, *args, **kwargs):
return render(self.request, "main/controle_punt140.html", {
"form1": UploadFileForm(),
"form2": ExcelForm()
})
def post(self, *args, **kwargs):
filter_text = FilterText()
types_of_encoding = ["utf8", "cp1252"]
form1 = UploadFileForm(self.request.POST, self.request.FILES, prefix="form1")
form2 = ExcelForm(self.request.FILES, self.request.FILES, prefix="form2")
content = ''
content_excel = ''
if form1.is_valid() and form2.is_valid() and self.request.POST:
uploadfile = UploadFile(image=self.request.FILES["upload_file"])
excel_file = self.request.FILES["upload_file"]
uploadfile.save()
for encoding_type in types_of_encoding:
with open(os.path.join(settings.MEDIA_ROOT, f"{uploadfile.image}"), 'r', encoding=encoding_type) as f:
if uploadfile.image.path.endswith('.pdf'):
content = filter_text.show_extracted_data_from_file(
uploadfile.image.path)
else:
content = f.read()
if uploadfile.image.path.endswith('xlsx'):
#Uploading excel form:
#this is just logic.
pass
else:
content_excel = f.read()
# You probably should do a redirect after the form is
# submitted, rather than render the page.
return redirect('main:controlepunt140')
# return render(self.request, "main/controle_punt140.html", {
'form1': ExcelForm(),
'form2': UploadFileForm(),
"content": [content, content_excel]
})
# I've adjusted the indent here to what I think it should be.
return render(self.request, "main/controle_punt140.html", {
"form1": form1,
"form2": form2,
})
You probable should also change to a redirect after the form is submitted and saved successfully. Check out Post/Redirect/Get and/or rendering content after a succesful post request.
Edit
Changed template to use {{ form.0.as_p }} as indicated by #nigel239
You can redirect to the same page where the form was submitted, so if the user hits the refresh button on their browser for some reason, you will not get an alert box asking the user to resend the form.

upload a file and get celery progress bar using ajax in django

I want to upload a file, process it in a Celery task, and show a progress bar using AJAX. but I did not get the solution. Can you help me with this task?
Views.py
# Create your views here.
def index(request):
if request.method == 'POST':
myfile =request.FILES['file']
fs = FileSystemStorage()
filename = fs.save(myfile.name , myfile)
uploaded_file_url = fs.url(filename)
data = process_file.delay(myfile.name)
task_id=data.task_id
return render(request , 'index.html', context={'task_id':task_id})
else:
return render(request , 'index.html')
task.py
def task_status(request, task_id):
if 'task' in request.GET:
task_id = request.GET['task_id']
else:
return HttpResponse('no job id given')
task = AsyncResult(task_id)
data = {
'state': task.state,
'result': task.result,
}
return HttpResponse(json.dumps(data), content_type='application/json')
# return render(request, 'progress.html', data)
Task.py
#shared_task(bind=True)
def process_file(self, file_name):
print('Uploading image...')
progress_recorder = ProgressRecorder(self)
result = 0
for i in range(5):
time.sleep(1)
result += i
instance = Tooltype(image=file_name)
instance.save()
#fs.delete(file_name)
print('Uploaded!')
progress_recorder.set_progress(i + 1, 5)
return result
index.html
<body>
<h1>select your file to process it!</h1>
<div class='progress-wrapper'>
<div id='progress-bar' class='progress-bar' style="background-color: #68a9ef; width: 0%;"> </div>
</div>
<div id="progress-bar-message">Waiting for progress to start...</div>
<form id="process-raw-data" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" id="file" name="file" >
<button type="submit" class="btn btn-success">Submit</button>
</form>
{% if task_id %}
<script>
var progressUrl = "{% url 'celery_progress:task_status' task_id %}";
function customResult(resultElement, result) {
$( resultElement ).append(
$('<p>').text('Sum of all seconds is ' + result)
);
}
$(function () {
CeleryProgressBar.initProgressBar(progressUrl, {
onResult: customResult,
})
});
</script>
{% endif %}
<script src="{% static 'celery_progress/celery_progress.js' %}"></script>
<script src="{% static 'assets/js/main.js' %}"></script>
</body>
when i upload a file and click on submit button it shows the progress bar but the file not shown in page and the task is successed and image is not uploading and show on page.
[2022-04-20 15:37:52,815: INFO/MainProcess] Task toolapp.tasks.process_file[b219aba3-d170-49e2-925c-3ad1c0a7c54b] succeeded in 5.327999999979511s: 10
thanks and got stuck on this task please help me.

How can I send this variable from django view to html using ajax on key up?

I am making and app that send data in realtime to the user in the html and I want to update the paragraph tag every time the users releases a key.
My HTML:
<form method="POST">
{% csrf_token %}
<p id="amount_word" class="amount_word" style="text-align:center">{{ amount_words }}</p>
</form>
My javascript ('texteditor' is a textarea that I have):
$("#texteditor").keyup(function(event){
data = {'csrfmiddlewaretoken':$('input[name=csrfmiddlewaretoken]').val()};
$.ajax({
type:'POST',
url:'/write/',
datatype: 'JSON',
data: data,
success: function(data) {
console.log(data) // check out how data is structured
$('.amount_word').contents()[0].textContent = data.amount_words
}
})
})
My python view:
def write_view(request, *args, **kwargs):
if request.is_ajax() and request.method == "POST":
def send_text():
texteditor = request.POST['TextEntered']
amount_words = "Amount of words: " + texteditor
print(amount_words)
texteditor = request.POST.get('TextEntered')
if texteditor == 'NoneType':
print("NoneType here")
else:
send_text()
return JsonResponse({'amount_words': amount_words})
return render(request, "write.html")
else:
return render(request, "write.html")
The template is write.html, and the URL is /write
try this it is working for me
<form method="POST">
{% csrf_token %}
<p id="amount_word" class="amount_word" style="text-align:center">amount:{{ amount_words }}</p>
<input type="text" id="texteditor" >
</form>
Jquery
$("#texteditor").keyup(function(event){
data = {'csrfmiddlewaretoken':$('input[name=csrfmiddlewaretoken]').val(),
'TextEntered': $('#texteditor').val()};
console.log("keyup = ", data);
$.ajax({
type:'POST',
url:'/write/',
datatype: 'JSON',
data: data,
success: function(data) {
console.log(data) // check out how data is structured
$('.amount_word').contents()[0].textContent = data.amount_words
}
});
});
in views
import json
from django.http import HttpResponse
def write_view(request, *args, **kwargs):
if request.is_ajax() and request.method == "POST":
texteditor = request.POST['TextEntered']
amount_words = "Amount of words: " + texteditor
print(amount_words)
texteditor = request.POST.get('TextEntered')
return HttpResponse(json.dumps({'amount_words': amount_words}), content_type="application/json")
else:
return render(request, "write.html")

Django with ajax render

I am pretty new to Django and I try to create my first application. However I faced a problem now.
I have a form (to add Alert - my mdoel) with Django validation and a div with list of all alerts. If I fill all fields correctly the alert is added and my div with alerts is refreshed using ajax. The problem is with form validation.
Without ajax I was sending bound form with errorLists for every field. I used Django form API. It was working ok but I can't refresh the errorlist if adding a form couldn't succeed.
I am getting either no render of form or render without context - all fields are blank. Of course they shouldn't be blank if I made a mistake in one field and It was working OK without ajax.
Bunch of code, First this is the view method I call:
def add_alert(request):
report = __get_or_create_empty_Report(request)
if request.method == 'POST':
form = AlertForm(request.POST)
if form.is_valid():
alert_project = ""
alert_name = ""
alert_ticket = ""
alert_date = ""
alert_type = ""
alert_comment = ""
if form.cleaned_data.has_key('alert_project'):
alert_project = form.cleaned_data['alert_project']
if form.cleaned_data.has_key('alert_name'):
alert_name = form.cleaned_data['alert_name']
if form.cleaned_data.has_key('alert_ticket'):
alert_ticket = form.cleaned_data['alert_ticket']
if form.cleaned_data.has_key('alert_date'):
alert_date = form.cleaned_data['alert_date']
if form.cleaned_data.has_key('alert_type'):
alert_type = form.cleaned_data['alert_type']
if form.cleaned_data.has_key('alert_comment'):
alert_comment = form.cleaned_data['alert_comment']
alert = Alert()
alert.alt_prj_id = get_object_or_404(Project, prj_id=alert_project)
alert.alt_name = alert_name
alert.alt_ticket = alert_ticket
alert.alt_date = alert_date
alert.alt_type = alert_type
comment = Comment()
comment.com_value = alert_comment
comment.save()
alert.alt_com_id = comment
alert.alt_rep_id = report
alert.save()
alerts = Alert.objects.filter(alt_rep_id=report.rep_id)
return render(request, 'main/theform.html', {'form': form, 'alerts': alerts, 'error_message': "Alert has been added"})
else:
form = AlertForm()
alerts = Alert.objects.filter(alt_rep_id=report.rep_id)
return render(request, 'main/theform.html', {'form': form, 'alerts': alerts})
alerts = Alert.objects.filter(alt_rep_id=report.rep_id)
return render(request, 'main/theform.html', {'form': form, 'alerts': alerts, 'error_message': "Alert has NOT been added"})
Here's the form template:
{% extends 'main/base.html' %}
{% block content %}
<div id="alerts"> </div>
{% for alert in alerts %}
<p> {{ alert.alt_name }} </p>
{% endfor %}
<form class="col-md-12" action="{% url 'main:add_alert' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{{ error_message }}
{% endblock %}
and simple script:
$(document).ready(function() {
//AJAX util
$('.add-alert-form').submit(function (e) {
e.preventDefault();
$.ajax({
type: "POST",
url: $(this).attr("action"),
data: $(this).serialize(),
success: function (data) {
$("#alerts").load(" #alerts");
}
});
});
}
I think I am doing it completly wrong but I have no idea what's the right way.
Take a look at https://docs.djangoproject.com/en/1.7/topics/forms/modelforms/
so your views would look like that:
if form.is_valid():
obj = form.save()
data = serializers.serialize('json', [obj,])
else:
data = json.dumps(dict([(k, [unicode(e) for e in v]) for k,v in form.errors.items()]))
return HttpResponse(data, mimetype='application/json')

Categories

Resources