Added smartphone as scanner option
This commit is contained in:
parent
9adcee728a
commit
0b285b068c
|
@ -32,3 +32,7 @@ venv.bak/
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
.history
|
.history
|
||||||
|
node_modules/
|
||||||
|
deploy/
|
||||||
|
media/
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -54,5 +54,9 @@
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<h1>Use your smartphone</h1>
|
||||||
|
<p>Use your <a class="navbar-brand" href="{% url 'booker:smartphone_scanner' %}">smartphone</a> to scan.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,234 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block additional_css %}
|
||||||
|
<link rel="preload" as="style" onload="this.rel='stylesheet';this.onload=null" href="https://unpkg.com/normalize.css@8.0.0/normalize.css">
|
||||||
|
<link rel="preload" as="style" onload="this.rel='stylesheet';this.onload=null" href="https://unpkg.com/milligram@1.3.0/dist/milligram.min.css">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block title %}Smartphone Scanner{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container" >
|
||||||
|
|
||||||
|
<div class="row-mb8" id="video-button-content">
|
||||||
|
<a class="button" id="startButton">Start</a>
|
||||||
|
<a class="button-outline" id="resetButton">Reset</a>
|
||||||
|
</div>
|
||||||
|
<div class="row" id="video-content">
|
||||||
|
<div class="col-6">
|
||||||
|
<video id="video" width="450" height="320" style="border: 1px solid gray"></video>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-2" id="sourceSelectPanel" style="display:none">
|
||||||
|
<label for="sourceSelect">Change video source:</label>
|
||||||
|
<select id="sourceSelect" style="max-width:400px">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-2">
|
||||||
|
<label>Result:</label>
|
||||||
|
<pre><code id="result"></code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="https://unpkg.com/@zxing/library@latest"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function beep() {
|
||||||
|
var audio = new Audio("{% static 'sound/beep.mp3' %}");
|
||||||
|
audio.volume = 0.1
|
||||||
|
console.log("Audio play {% static 'sound/beep.mp3' %}")
|
||||||
|
audio.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
let selectedDeviceId;
|
||||||
|
let lastScanResult = '';
|
||||||
|
// const codeReader = new ZXing.BrowserBarcodeReader()
|
||||||
|
const codeReader = new ZXing.BrowserMultiFormatReader()
|
||||||
|
console.log('ZXing code reader initialized')
|
||||||
|
codeReader.getVideoInputDevices()
|
||||||
|
.then((videoInputDevices) => {
|
||||||
|
const sourceSelect = document.getElementById('sourceSelect')
|
||||||
|
selectedDeviceId = videoInputDevices[0].deviceId
|
||||||
|
if (videoInputDevices.length > 1) {
|
||||||
|
videoInputDevices.forEach((element) => {
|
||||||
|
const sourceOption = document.createElement('option')
|
||||||
|
sourceOption.text = element.label
|
||||||
|
sourceOption.value = element.deviceId
|
||||||
|
sourceSelect.appendChild(sourceOption)
|
||||||
|
})
|
||||||
|
|
||||||
|
sourceSelect.onchange = () => {
|
||||||
|
selectedDeviceId = sourceSelect.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceSelectPanel = document.getElementById('sourceSelectPanel')
|
||||||
|
sourceSelectPanel.style.display = 'block'
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('startButton').addEventListener('click', () => {
|
||||||
|
beep()
|
||||||
|
codeReader.decodeFromInputVideoDeviceContinuously(selectedDeviceId, 'video', (result) => {
|
||||||
|
if (result && lastScanResult !== result.text) {
|
||||||
|
lastScanResult = result.text
|
||||||
|
console.log('Got scan (cont.): ' + result)
|
||||||
|
document.getElementById('result').textContent = result.text
|
||||||
|
beep()
|
||||||
|
useScannedText(result.text)
|
||||||
|
} else if (result) {
|
||||||
|
console.log('... scan again')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
codeReader.decodeOnceFromVideoDevice(selectedDeviceId, 'video').then((result) => {
|
||||||
|
if (lastScanResult !== result.text) {
|
||||||
|
lastScanResult = result.text
|
||||||
|
console.log('Got scan: ' + result.text)
|
||||||
|
document.getElementById('result').textContent = result.text
|
||||||
|
useScannedText(result.text)
|
||||||
|
} else {
|
||||||
|
console.log('... scan again')
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
document.getElementById('result').textContent = err
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
console.log(`Started continous decode from camera with id ${selectedDeviceId}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
document.getElementById('resetButton').addEventListener('click', () => {
|
||||||
|
document.getElementById('result').textContent = '';
|
||||||
|
codeReader.reset();
|
||||||
|
console.log('Reset.')
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="input-group">
|
||||||
|
<input class="form-control" type="text" maxlength="40" id="container_id" placeholder="Container ID">
|
||||||
|
<input class="form-control" type="text" maxlength="40" id="asset_id" placeholder="Asset ID">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="input-group">
|
||||||
|
<input class="btn btn-primary" id="book-in-button" type="button" value="+Book">
|
||||||
|
<input class="btn btn-primary" id="book-out-button" type="button" value="-Book">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="input-group">
|
||||||
|
<textarea class="form-control" readonly="readonly" rows="4" id="container_details"></textarea>
|
||||||
|
<textarea class="form-control" readonly="readonly" rows="4" id="asset_details"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="input-group">
|
||||||
|
<textarea class="form-control mb-2" id="chat-log" cols="100" rows="10"></textarea>
|
||||||
|
<input class="btn btn-secondary" id="clear-chat-area" type="button" value="Clear">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
var containerIsValid = false;
|
||||||
|
var assetIsValid = false;
|
||||||
|
|
||||||
|
async function getContainerInfo(containerId) {
|
||||||
|
document.querySelector('#container_id').value = containerId;
|
||||||
|
url = '/api/containers?named_id=' + containerId;
|
||||||
|
document.querySelector('#container_details').value = 'Trying ' + url;
|
||||||
|
const response = await fetch(url);
|
||||||
|
const container = await response.json();
|
||||||
|
if (container.count >= 1) {
|
||||||
|
document.querySelector('#container_details').value = container.results[0].description;
|
||||||
|
containerIsValid = true;
|
||||||
|
} else {
|
||||||
|
document.querySelector('#container_details').value = document.querySelector('#container_details').value + "\nERROR: No result\n";
|
||||||
|
console.log(response);
|
||||||
|
containerIsValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAssetInfo(assetId) {
|
||||||
|
document.querySelector('#asset_id').value = assetId;
|
||||||
|
url = '/api/assets?named_id=' + assetId;
|
||||||
|
document.querySelector('#asset_details').value = 'Trying ' + url;
|
||||||
|
const response = await fetch(url);
|
||||||
|
const asset = await response.json();
|
||||||
|
if (asset.count >= 1) {
|
||||||
|
document.querySelector('#asset_details').value = asset.results[0].description;
|
||||||
|
assetIsValid = true;
|
||||||
|
} else {
|
||||||
|
document.querySelector('#asset_details').value = document.querySelector('#asset_details').value + "\nERROR: No result\n";
|
||||||
|
console.log(response);
|
||||||
|
assetIsValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function useScannedText(msg) {
|
||||||
|
// const data = JSON.parse(e.data);
|
||||||
|
// msg = String(data.message);
|
||||||
|
document.querySelector('#chat-log').value += (msg + '\n');
|
||||||
|
if (msg.startsWith('C-')) {
|
||||||
|
getContainerInfo(data.message);
|
||||||
|
// document.querySelector('#container_id').value = msg;
|
||||||
|
} else if (msg.startsWith('A-')) {
|
||||||
|
getAssetInfo(data.message);
|
||||||
|
// document.querySelector('#asset_id').value = msg;
|
||||||
|
} else {
|
||||||
|
assetId = 'A-' + msg;
|
||||||
|
document.querySelector('#asset_id').value = assetId;
|
||||||
|
getAssetInfo(assetId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.querySelector('#clear-chat-area').onclick = function(e) {
|
||||||
|
document.querySelector('#chat-log').value = "cleared\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector('#book-in-button').onclick = function(e) {
|
||||||
|
if (!containerIsValid) {
|
||||||
|
msg = 'Cannot book-in as container is invalid';
|
||||||
|
console.log(msg);
|
||||||
|
document.querySelector('#chat-log').value += ('ERROR: ' + msg + '\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!assetIsValid) {
|
||||||
|
msg = 'Cannot book-in as asset is invalid';
|
||||||
|
console.log(msg);
|
||||||
|
document.querySelector('#chat-log').value += ('ERROR: ' + msg + '\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector('#book-out-button').onclick = function(e) {
|
||||||
|
if (!containerIsValid) {
|
||||||
|
msg = 'Cannot book-out as container is invalid';
|
||||||
|
console.log(msg);
|
||||||
|
document.querySelector('#chat-log').value += ('ERROR: ' + msg + '\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!assetIsValid) {
|
||||||
|
msg = 'Cannot book-out as asset is invalid';
|
||||||
|
console.log(msg);
|
||||||
|
document.querySelector('#chat-log').value += ('ERROR: ' + msg + '\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -5,5 +5,6 @@ app_name = 'booker'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.IndexView.as_view(), name='index'),
|
path('', views.IndexView.as_view(), name='index'),
|
||||||
|
path('smartphone_scanner/', views.SmartphoneScannerView.as_view(), name='smartphone_scanner'),
|
||||||
path('<str:scanner_id>/', views.scanner, name='scanner')
|
path('<str:scanner_id>/', views.scanner, name='scanner')
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView, TemplateView
|
||||||
from .models import Scanner
|
from .models import Scanner
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,3 +12,6 @@ def scanner(request, scanner_id):
|
||||||
return render(request, 'booker/scanner.html', {
|
return render(request, 'booker/scanner.html', {
|
||||||
'scanner_id': scanner_id
|
'scanner_id': scanner_id
|
||||||
})
|
})
|
||||||
|
|
||||||
|
class SmartphoneScannerView(TemplateView):
|
||||||
|
template_name = 'booker/smartphone_scanner.html'
|
||||||
|
|
|
@ -138,6 +138,12 @@ STATIC_URL = '/static/'
|
||||||
STATICFILES_DIRS = [
|
STATICFILES_DIRS = [
|
||||||
BASE_DIR / "static"
|
BASE_DIR / "static"
|
||||||
]
|
]
|
||||||
|
STATIC_ROOT = os.path.join(BASE_DIR, "deploy/static/")
|
||||||
|
STATICFILES_FINDERS = [
|
||||||
|
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||||
|
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||||
|
'npm.finders.NpmFinder'
|
||||||
|
]
|
||||||
|
|
||||||
# Default primary key field type
|
# Default primary key field type
|
||||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
|
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
|
||||||
|
@ -177,5 +183,8 @@ REST_FRAMEWORK = {
|
||||||
],
|
],
|
||||||
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
|
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
|
||||||
'PAGE_SIZE': 10,
|
'PAGE_SIZE': 10,
|
||||||
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
|
'DEFAULT_RENDERER_CLASSES': [
|
||||||
|
'rest_framework.renderers.JSONRenderer',
|
||||||
|
'rest_framework.renderers.BrowsableAPIRenderer',
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
<!-- This file stores project-specific CSS -->
|
<!-- This file stores project-specific CSS -->
|
||||||
<link href="{% static 'css/project.css' %}" rel="stylesheet">
|
<link href="{% static 'css/project.css' %}" rel="stylesheet">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
{% block additional_css %}
|
||||||
|
{% endblock %}
|
||||||
<!-- Le javascript
|
<!-- Le javascript
|
||||||
================================================== -->
|
================================================== -->
|
||||||
{# Placed at the top of the document so pages load faster with defer #}
|
{# Placed at the top of the document so pages load faster with defer #}
|
||||||
|
@ -31,6 +33,8 @@
|
||||||
<script defer src="{% static 'js/project.js' %}"></script>
|
<script defer src="{% static 'js/project.js' %}"></script>
|
||||||
|
|
||||||
{% endblock javascript %}
|
{% endblock javascript %}
|
||||||
|
{% block additional_javascript %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue