The company has workers performing various activities during a day. Each activity has start_time
and finish_time
. It is usual that workers forget to beep the end of activity (finish_time
) and that's because there is a stored procedure read_unended time_from time_to
which reads records between time_from
and time_to
which has not finish_time
(is NULL
).
For example
id name day start_time finish_time place activity
38 Thomas 2021-12-03 2021-12-03 08:51:38.000 NULL p1 a1
28 Charles 2021-12-02 2021-12-02 12:29:03.000 NULL p2 a2
49 John 2021-12-06 2021-12-06 11:59:48.000 NULL p3 a3
68 Jessie 2021-12-08 2021-12-08 10:55:12.000 NULL p4 a4
82 Susanne 2021-12-10 2021-12-10 12:38:03.000 NULL p5 a5
There is a form in (forms.py
)
class FromToForm(Form):
start_date = DateField(widget=AdminDateWidget())
start_time = TimeField(widget=AdminTimeWidget())
end_date = DateField(widget=AdminDateWidget())
end_time = TimeField(widget=AdminTimeWidget())
There is a view in (views.py
) which displays such a table.
def ending(req):
from_to_form = FromToForm()
result = []
context = {
'form': from_to_form,
'result': result
}
if req.method == "POST":
from_to_form = FromToForm(req.POST)
if from_to_form.is_valid():
start = datetime.combine(from_to_form.cleaned_data['start_date'], from_to_form.cleaned_data['start_time']).isoformat()
end = datetime.combine(from_to_form.cleaned_data['end_date'], from_to_form.cleaned_data['end_time']).isoformat()
with connections["mssql_database"].cursor() as cursor:
cursor.execute("EXEC read_unended @dt_od='%s', @dt_do='%s'" % (start, end))
result = cursor.fetchall()
context['result'] = result
return render(req, 'ending.html', context)
else:
return render(req, 'ending.html', context)
else:
return render(req, 'ending.html', context)
and an associated template in templates.py
.
<form action='.' method='POST'>{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<input type='submit' value='Read unended' />
</form>
{% if result %}
<table >
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>day</th>
<th>start_time</th>
<th>finish_time</th>
<th>place</th>
<th>activity</th>
</tr>
</thead>
<tbody>
{%for i in result %}
<tr>
<td>{{i.0}}</td>
<td>{{i.1}}</td>
<td>{{i.2}}</td>
<td>{{i.3}}</td>
<td>CELL TO INSERT END TIME*</td>
<td>{{i.5}}</td>
<td>{{i.6}}</td>
<td>BUTTON TO FINISH THIS ACTIVITY**<td/>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
Every activity is ended
{% endif %}
** and * is not implemented yet.
I'd like to implement the following functionality. In every row of the dynamically generated table, there should be a button ** to finish this activity (this row) with the time * the user of the application inserts. In that moment the page should refresh and that row should not be displayed anymore, because this activity has already finish_time
assigned. How can I implement such a view and template? Do I need to add a dynamically generated fields to the existing form? What do you suggest?
CodePudding user response:
You have to create a function to post your data and another to get the table body
template.html
<table >
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>day</th>
<th>start_time</th>
<th>finish_time</th>
<th>place</th>
<th>activity</th>
</tr>
</thead>
<tbody id="tbodyId">
{% for i in result %}
<tr>
<td id="tdId_{{ i.0 }}0">{{i.0}}</td>
<td id="tdId_{{ i.0 }}1">{{i.1}}</td>
<td id="tdId_{{ i.0 }}2">{{i.2}}</td>
<td id="tdId_{{ i.0 }}3">{{i.3}}</td>
<td><input type="date" id="tdId_{{i.0}}4"/></td>
<td id="tdId_{{ i.0 }}5">{{i.5}}</td>
<td id="tdId_{{ i.0 }}6">{{i.6}}</td>
<td><button value="{{i.0}}" type="button" onclick="setEndActivity(this.value)">End</button><td/>
</tr>
{% endfor %}
</tbody>
</table>
<script>
function loadResults(activityId) {
let xhttpRequest = new XMLHttpRequest()
const start_date = document.getElementById(`tdId_${activityId}2`).value
const start_time = document.getElementById(`tdId_${activityId}3`).value
const end_date = document.getElementById(`tdId_${activityId}2`).value
const end_time = document.getElementById(`tdId_${activityId}4`).value
xhttpRequest.onreadystatechange = function (data) {
if (this.readyState === 4 && this.status === 200) {
document.getElementById("tbodyId").innerHTML = this.response
}
}
xhttpRequest.open("GET", `./ajax/load-results?start_date=${start_date}&end_date=${end_date}&start_time=${start_time}&end_time=${end_time}`, true)
xhttpRequest.send()
}
function setEndActivity(activityId) {
const dataToBackEnd = new FormData()
dataToBackEnd.append("activity", activityId)
dataToBackEnd.append("start_date", document.getElementById(`tdId_${activityId}2`).value)
dataToBackEnd.append("start_time", document.getElementById(`tdId_${activityId}3`).value)
dataToBackEnd.append("end_date", document.getElementById(`tdId_${activityId}2`).value)
dataToBackEnd.append("end_time", document.getElementById(`tdId_${activityId}4`).value)
const request = new Request('./ajax/end-activity/',
{
method: 'POST',
headers: { 'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value },
body: dataToBackEnd
})
fetch(request, {
method: 'POST',
mode: 'same-origin'
}).then(
function(response) {
if (response.status === 200) {
loadResults(activityId)
} else {
alert("Error")
}
}
)
}
</script>
In your views you will add 2 views, or just 1 if you modify the "ending" view to receive the post and return a http response
views.py
def ajax_ending(req):
if req.method == "POST":
from_to_form = FromToForm(req.POST)
if from_to_form.is_valid():
# save in database
return HttpResponse(status=200)
else:
return HttpResponse(status=400)
else:
return HttpResponse(status=405)
def ajax_load_results(request):
if request.method == "GET":
if request.user:
# get the activities
start_date = request.GET['start_date']
start_time = request.GET['start_time']
start_date_time = f'{start_date} {start_time}'
end_date = request.GET['end_date']
end_time = request.GET['end_time']
end_date_time = f'{end_date} {end_time}'
# this datetime.strptime depends of how you write your date and time
start = datetime.strptime(start_date_time, "%d-%m-%Y %H:%M:%S").isoformat()
end = datetime.strptime(end_date_time, "%d-%m-%Y %H:%M:%S").isoformat()
# i don't understand why you are executing queries like this, so i assume they work
with connections["mssql_database"].cursor() as cursor:
cursor.execute("EXEC read_unended @dt_od='%s', @dt_do='%s'" % (start, end))
result = cursor.fetchall()
context['result'] = result
return render(request, 'ajax-ending.html', {"context": context})
else:
return HttpResponse(status=403)
else:
return HttpResponse(status=405)
And you will have another template.html just for tbody of table
ajax-ending.html
{% for i in result %}
<tr>
<td id="tdId_{{ i.0 }}0">{{i.0}}</td>
<td id="tdId_{{ i.0 }}1">{{i.1}}</td>
<td id="tdId_{{ i.0 }}2">{{i.2}}</td>
<td id="tdId_{{ i.0 }}3">{{i.3}}</td>
<td id="tdId_{{ i.0 }}4">CELL TO INSERT END TIME*</td>
<td id="tdId_{{ i.0 }}5">{{i.5}}</td>
<td id="tdId_{{ i.0 }}6">{{i.6}}</td>
<td><button value="{{i.0}}" type="button" onclick="setEndActivity(this.value)">End</button><td/>
</tr>
{% endfor %}
ulrs.py
from django.urls import path
from . import views
app_name = 'your_app_name'
urlpatterns = [
'ending/',
views.ending,
name='ending'),
path(
'ending/ajax/end-activity/',
views.ajax_ending,
name='ajax_ending'),
path(
'ending/ajax/load-results/',
views.ajax_load_results,
name='ajax_load_results'),
]
(Not tested, if throws a error let me know)