New HIT status page
This commit is contained in:
parent
d3bf3d47ea
commit
b2626244a4
6 changed files with 155 additions and 104 deletions
|
@ -56,10 +56,11 @@ class HIT(Base):
|
|||
return os.path.join('/frames', f"{self.id:06d}.jpg")
|
||||
|
||||
def getSvgImageUrl(self):
|
||||
return f"scans/{self.id:06d}.svg"
|
||||
return f"/scans/{self.id:06d}.svg"
|
||||
|
||||
def getSvgImagePath(self):
|
||||
return os.path.join('www', self.getSvgImageUrl())
|
||||
# os.path.join on svgImageUrl leads to invalid absolute url
|
||||
return os.path.join(f'www/scans/{self.id:06d}.svg')
|
||||
|
||||
def getLastAssignment(self):
|
||||
if not len(self.assignments):
|
||||
|
|
|
@ -238,6 +238,8 @@ class CentralManagement():
|
|||
continue
|
||||
|
||||
self.currentHit.scanned_at = datetime.datetime.utcnow()
|
||||
self.store.saveHIT(self.currentHit)
|
||||
|
||||
time_diff = datetime.datetime.now() - self.lastHitTime
|
||||
to_wait = 10 - time_diff.total_seconds()
|
||||
# self.statusPageQueue.add(dict(hit_id=self.currentHit.id, state='scan'))
|
||||
|
|
|
@ -165,6 +165,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
|||
def on_close(self):
|
||||
self.__class__.rmConnection(self)
|
||||
logger.info(f"Client disconnected: {self.request.remote_ip}")
|
||||
# TODO: abandon assignment??
|
||||
|
||||
def submit_strokes(self):
|
||||
if len(self.strokes) < 1:
|
||||
|
|
|
@ -5,10 +5,69 @@
|
|||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<script src='dateformat.js'></script>
|
||||
<script src='reconnecting-websocket.min.js'></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="wrapper">
|
||||
<div class='hit' v-for="(hit, hitId) in hits"
|
||||
:class="[{'hugvey--on': hit.status != 'off'},'hugvey--' + hit.status]"
|
||||
>
|
||||
|
||||
<div class='transition'>
|
||||
<p v-if="hit.hit_id">Created HIT at {{hit.created_at}}</p>
|
||||
<p v-else>Creating HIT</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class='state' v-if="hit.hit_id">
|
||||
<h2>Human intelligence task</h2>
|
||||
<p>{{hit.hit_id}}</p>
|
||||
<p>Description</p>
|
||||
</div>
|
||||
|
||||
<div class='transition' v-if="hit.hit_id">
|
||||
<p v-if="!hit.assignment">Wait for human</p>
|
||||
<template v-else>
|
||||
<p v-if="hit.assignment.returned_at || hit.assignment.abandoned_at">Asignment abandoned</p>
|
||||
<p v-else>Assignment accepted</p>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
|
||||
<template v-if="hit.assignment">
|
||||
|
||||
<div class='state' v-if="hit.assignment"
|
||||
:class="[{'assignment-hidden': hit.assignment.returned_at || hit.assignment.abandoned_at}]">
|
||||
<h2>Worker {{hit.assignment.worker_id}}</h2>
|
||||
<p>{{hit.assignment.turk_browser}} / {{hit.assignment.turk_ip}} / {{hit.assignment.turk_country}} / {{hit.assignment.turk_os}}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='transition' v-if="hit.assignment.submit_page_at">
|
||||
<p v-if="!hit.assignment.confirmed_at">Assignment submitted</p>
|
||||
<p v-else>Confirmed submission</p> <!-- Validated submitted code through SQSmessage -->
|
||||
</div>
|
||||
|
||||
<div class='state' v-if="hit.assignment.submit_page_at">
|
||||
result: (add duration)
|
||||
<img :src="hit.svg_image">
|
||||
</div>
|
||||
|
||||
<div class='transition' v-if="hit.assignment.submit_page_at">
|
||||
<p v-if="!hit.scanned_at">Scanning</p>
|
||||
<p v-else>Scanned at {{hit.scanned_at}}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='state' v-if="hit.scanned_at">
|
||||
scan:
|
||||
<img :src="hit.scan_image">
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<!-- <div class="phase" id="waiting_for_human">
|
||||
<span class="narrative_phase_text">waiting for human worker to accept task</span>
|
||||
|
@ -17,10 +76,10 @@
|
|||
<div class="phase" id="human_accepted_task">
|
||||
<span class="narrative_phase_text">task accepted by human worker</span>
|
||||
</div> -->
|
||||
|
||||
<!--
|
||||
<div class="phase" id="worker_specs">
|
||||
<span class="grid-item spec_name" id="hit_id_descriptor">human intelligent task id</span>
|
||||
<span class="grid-item spec_value" id="hit_id"> </span>
|
||||
<span class="grid-item spec_value" id="hit_id">{{hit.id}}</span>
|
||||
|
||||
<span class="grid-item spec_name" id="worker_id_descriptor">human worker id</span>
|
||||
<span class="grid-item spec_value" id="worker_id"> </span>
|
||||
|
@ -43,10 +102,9 @@
|
|||
|
||||
<span class="grid-item spec_name" id="elapsed_time_descriptor">time elapsed</span>
|
||||
<span class="grid-item spec_value" id="elapsed_time"> </span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>-->
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,38 +1,22 @@
|
|||
|
||||
// DOM STUFF ///////////////////////////////////////////////////////////////////
|
||||
|
||||
let divs = {},
|
||||
spec_names = [
|
||||
'worker_id',
|
||||
'ip',
|
||||
'location',
|
||||
'browser',
|
||||
'os',
|
||||
'state',
|
||||
'fee',
|
||||
'hit_created',
|
||||
'hit_opened',
|
||||
'hit_submitted',
|
||||
'elapsed_time',
|
||||
'hit_id'
|
||||
]
|
||||
var app = new Vue({
|
||||
el: '#wrapper',
|
||||
data: {
|
||||
message: 'Hello Vue!',
|
||||
hits: {
|
||||
|
||||
divs.linkDOM = function(name){
|
||||
divs[name] = document.getElementById(`${name}`)
|
||||
}
|
||||
|
||||
spec_names.forEach(function(name){
|
||||
divs.linkDOM(name)
|
||||
}
|
||||
},
|
||||
// watch: {
|
||||
// hits: {
|
||||
// deep: true
|
||||
// }
|
||||
// }
|
||||
})
|
||||
|
||||
|
||||
|
||||
let request_time = timeStamp(),
|
||||
hit_started = false,
|
||||
elapsed_time,
|
||||
hit_finished = false
|
||||
|
||||
|
||||
// SOCKET STUFF ////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
@ -43,76 +27,29 @@ ws.addEventListener('open', () => {
|
|||
// ws.send('hi server')
|
||||
})
|
||||
|
||||
|
||||
ws.addEventListener('message', (event) => {
|
||||
console.log('message: ' + event.data)
|
||||
|
||||
let data = JSON.parse(event.data)
|
||||
if(data.property === 'hit_opened') {
|
||||
if(data.value != null){
|
||||
hit_started = true
|
||||
hit_finished = false
|
||||
request_time = new Date()
|
||||
divs[data.property].innerHTML = `${request_time.format('dd mmm HH:MM:ss')}`
|
||||
}else{
|
||||
divs[data.property].innerHTML = `—`
|
||||
hit_started = false
|
||||
let hits = JSON.parse(event.data)
|
||||
let a = {};
|
||||
for(let hitid in app.hits) {
|
||||
a[hitid] = app.hits[hitid];
|
||||
}
|
||||
for(let hit of hits){
|
||||
a[hit.id] = hit;
|
||||
}
|
||||
else if(data.property === 'hit_submitted'){
|
||||
hit_finished = true;
|
||||
}
|
||||
else if(divs[data.property]){
|
||||
data.value === null ? divs[data.property].innerHTML = `—` : divs[data.property].innerHTML = `${data.value}`
|
||||
}
|
||||
app.hits = a;
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ANIMATION STUFF /////////////////////////////////////////////////////////////
|
||||
|
||||
let frames,
|
||||
frames_per_sec = 10,
|
||||
current_frame = 0
|
||||
|
||||
|
||||
|
||||
function makeAnimation(){
|
||||
let now,
|
||||
delta = 0,
|
||||
last = timeStamp(),
|
||||
step = 1/frames_per_sec
|
||||
function frame() {
|
||||
now = timeStamp()
|
||||
delta += Math.min(1, (now - last) / 1000)
|
||||
while(delta > step){
|
||||
delta -= step
|
||||
update(step)
|
||||
}
|
||||
last = now
|
||||
requestAnimationFrame(frame)
|
||||
}
|
||||
requestAnimationFrame(frame)
|
||||
}
|
||||
|
||||
function update(step){
|
||||
|
||||
if(!hit_finished) elapsed_time = `${new Date((Date.now() - request_time)).format('MM"m "ss"s"')}`
|
||||
if(hit_started){
|
||||
divs['elapsed_time'].innerHTML = elapsed_time
|
||||
}else{
|
||||
divs['elapsed_time'].innerHTML = `—`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
makeAnimation()
|
||||
|
||||
|
||||
function timeStamp(){return window.performance && window.performance.now ? window.performance.now() : new Date().getTime()}
|
||||
//
|
||||
//function update(step){
|
||||
//
|
||||
// if(!hit_finished) elapsed_time = `${new Date((Date.now() - request_time)).format('MM"m "ss"s"')}`
|
||||
// if(hit_started){
|
||||
// divs['elapsed_time'].innerHTML = elapsed_time
|
||||
// }else{
|
||||
// divs['elapsed_time'].innerHTML = `—`
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -47,9 +47,9 @@ html, body{
|
|||
|
||||
position: absolute;
|
||||
left: var(--pos-x);
|
||||
top: var(--pos-y);
|
||||
bottom: calc(100vh - (var(--pos-y) + var(--height)));
|
||||
width: var(--width);
|
||||
height: var(--height);
|
||||
height: auto;
|
||||
|
||||
background: var(--alt-color);
|
||||
box-sizing: border-box;
|
||||
|
@ -57,6 +57,57 @@ html, body{
|
|||
}
|
||||
|
||||
|
||||
|
||||
#wrapper .hit{
|
||||
display:block;
|
||||
}
|
||||
|
||||
|
||||
.transition{
|
||||
overflow:hidden;
|
||||
position:relative;
|
||||
padding-left: calc(50%);
|
||||
font-size: 12px;
|
||||
height: 40px;
|
||||
animation-duration: 3s;
|
||||
animation-name: slidein;
|
||||
}
|
||||
.transition::before{
|
||||
content:'⇩'; /*'⇓';*/
|
||||
display:block;
|
||||
position:absolute;
|
||||
font-family:monospace;
|
||||
font-size: 100px;
|
||||
top:0;
|
||||
left:calc(50% - 30px);
|
||||
line-height:0;
|
||||
}
|
||||
|
||||
.state{
|
||||
overflow:hidden;
|
||||
border:solid 1px black;
|
||||
animation-duration: 3s;
|
||||
animation-name: slidein;
|
||||
transition: max-height 1s;
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.state.assignment-hidden {
|
||||
/*On abandon etc.*/
|
||||
max-height: 0px;
|
||||
}
|
||||
|
||||
@keyframes slidein {
|
||||
from {
|
||||
max-height:0;
|
||||
}
|
||||
|
||||
to {
|
||||
max-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#worker_specs{
|
||||
display:grid;
|
||||
grid-template-columns: 1fr ;
|
||||
|
@ -97,3 +148,4 @@ html, body{
|
|||
position: relative;
|
||||
top: 5px;
|
||||
}
|
||||
*/
|
Loading…
Reference in a new issue