Backend
This commit is contained in:
parent
cd2f4d3d1d
commit
f87c959f22
6 changed files with 215 additions and 1 deletions
|
@ -1,6 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy import Column, Integer, String, DateTime
|
from sqlalchemy import Column, Integer, String, DateTime, Float
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from sqlalchemy.sql.schema import ForeignKey, Sequence
|
from sqlalchemy.sql.schema import ForeignKey, Sequence
|
||||||
from sqlalchemy.engine import create_engine
|
from sqlalchemy.engine import create_engine
|
||||||
|
@ -55,6 +55,7 @@ class HIT(Base):
|
||||||
turk_screen_width = Column(Integer, default = None)
|
turk_screen_width = Column(Integer, default = None)
|
||||||
turk_screen_height = Column(Integer, default = None)
|
turk_screen_height = Column(Integer, default = None)
|
||||||
scanned_at = Column(DateTime, default=None)
|
scanned_at = Column(DateTime, default=None)
|
||||||
|
fee = Column(Float(precision=2), default=None)
|
||||||
|
|
||||||
|
|
||||||
def getImagePath(self):
|
def getImagePath(self):
|
||||||
|
@ -149,6 +150,11 @@ class Store:
|
||||||
return int(2.5*60)
|
return int(2.5*60)
|
||||||
return int(sum(durations) / len(durations))
|
return int(sum(durations) / len(durations))
|
||||||
|
|
||||||
|
def getHITs(self, n = 100):
|
||||||
|
return self.session.query(HIT).\
|
||||||
|
filter(HIT.submit_hit_at != None).\
|
||||||
|
order_by(HIT.submit_hit_at.desc()).limit(n)
|
||||||
|
|
||||||
# def rmSource(self, id: int):
|
# def rmSource(self, id: int):
|
||||||
# with self.getSession() as session:
|
# with self.getSession() as session:
|
||||||
# source = session.query(Source).get(id)
|
# source = session.query(Source).get(id)
|
||||||
|
|
|
@ -226,6 +226,7 @@ class CentralManagement():
|
||||||
estimatedHitDuration = self.store.getAvgDurationOfPreviousNHits(5)
|
estimatedHitDuration = self.store.getAvgDurationOfPreviousNHits(5)
|
||||||
|
|
||||||
fee = (self.config['hour_rate_aim']/3600.) * estimatedHitDuration
|
fee = (self.config['hour_rate_aim']/3600.) * estimatedHitDuration
|
||||||
|
self.currentHit.fee = fee
|
||||||
self.logger.debug(f"Based on average duration of {estimatedHitDuration} fee should be {fee}/hit to get hourly rate of {self.config['hour_rate_aim']}")
|
self.logger.debug(f"Based on average duration of {estimatedHitDuration} fee should be {fee}/hit to get hourly rate of {self.config['hour_rate_aim']}")
|
||||||
new_hit = self.mturk.create_hit(
|
new_hit = self.mturk.create_hit(
|
||||||
Title = 'Trace the drawn line',
|
Title = 'Trace the drawn line',
|
||||||
|
|
|
@ -274,6 +274,40 @@ class DrawPageHandler(tornado.web.RequestHandler):
|
||||||
.replace("{LEFT_PADDING}", str(self.top_padding))
|
.replace("{LEFT_PADDING}", str(self.top_padding))
|
||||||
self.write(contents)
|
self.write(contents)
|
||||||
|
|
||||||
|
class BackendHandler(tornado.web.RequestHandler):
|
||||||
|
def initialize(self, store: HITStore, path: str):
|
||||||
|
self.store = store
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
rows = []
|
||||||
|
for hit in self.store.getHITs(100):
|
||||||
|
if hit.submit_hit_at and hit.accept_time:
|
||||||
|
seconds = (hit.submit_hit_at - hit.accept_time).total_seconds()
|
||||||
|
duration_m = int(seconds/60)
|
||||||
|
duration_s = max(int(seconds%60), 0)
|
||||||
|
print(duration_m, duration_s)
|
||||||
|
duration = (f"{duration_m}m" if duration_m else "") + f"{duration_s:02d}s"
|
||||||
|
else:
|
||||||
|
duration = "-"
|
||||||
|
|
||||||
|
fee = f"${hit.fee:.2}" if hit.fee else "-"
|
||||||
|
|
||||||
|
rows.append(
|
||||||
|
f"""
|
||||||
|
<tr><td></td><td>{hit.worker_id}</th>
|
||||||
|
<td>{hit.turk_ip}</td>
|
||||||
|
<td>{hit.turk_country}</td>
|
||||||
|
<td>{fee}</td>
|
||||||
|
<td>{hit.accept_time}</td>
|
||||||
|
<td>{duration}</td><td></td>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
contents = open(os.path.join(self.path, 'backend.html'), 'r').read()
|
||||||
|
contents = contents.replace("{{TBODY}}", "".join(rows))
|
||||||
|
self.write(contents)
|
||||||
|
|
||||||
class StatusPage():
|
class StatusPage():
|
||||||
"""
|
"""
|
||||||
Properties for on the status page, which are send over websockets the moment
|
Properties for on the status page, which are send over websockets the moment
|
||||||
|
@ -365,6 +399,11 @@ class Server:
|
||||||
top_padding=self.config['scanner']['top_padding'],
|
top_padding=self.config['scanner']['top_padding'],
|
||||||
left_padding=self.config['scanner']['left_padding']
|
left_padding=self.config['scanner']['left_padding']
|
||||||
)),
|
)),
|
||||||
|
(r"/backend", BackendHandler,
|
||||||
|
dict(
|
||||||
|
store = self.store,
|
||||||
|
path=self.web_root,
|
||||||
|
)),
|
||||||
(r"/(.*)", StaticFileWithHeaderHandler,
|
(r"/(.*)", StaticFileWithHeaderHandler,
|
||||||
{"path": self.web_root}),
|
{"path": self.web_root}),
|
||||||
], debug=True, autoreload=False)
|
], debug=True, autoreload=False)
|
||||||
|
|
26
www/amazon.svg
Normal file
26
www/amazon.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 21 KiB |
110
www/backend.css
Normal file
110
www/backend.css
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'bebas';
|
||||||
|
src: url('font/BebasNeue-Regular.ttf');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'freesans';
|
||||||
|
src: url('font/FreeSans.ttf')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
:root{
|
||||||
|
|
||||||
|
--base-font-size: 12px;
|
||||||
|
--spec_name-font-size: 120%;
|
||||||
|
--spec_value-font-size: 250%;
|
||||||
|
|
||||||
|
--base-color: #271601; /* tekst */
|
||||||
|
--alt-color: #FFF5DF; /* achtergrond */
|
||||||
|
--amazon-color: #F0C14C;
|
||||||
|
|
||||||
|
/* ////// GAT ACHTERKANT PLEK & POSITIE /////// */
|
||||||
|
/* */ /* */
|
||||||
|
/* */ --pos-x: 20px; /* */
|
||||||
|
/* */ --pos-y: 20px; /* */
|
||||||
|
/* */ --width: 90%; /* 115mm */
|
||||||
|
/* */ --height: 100%; /* 500mm */
|
||||||
|
/* */ /* */
|
||||||
|
/* //////////////////////////////////////////// */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
html, body{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
text-decoration: none;
|
||||||
|
font-family: 'freesans';
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
line-height: 1.1;
|
||||||
|
background: #555;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo{
|
||||||
|
background: #555;
|
||||||
|
width: 100%;
|
||||||
|
padding: 2% 0% 1% 0%;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper{
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
left: var(--pos-x);
|
||||||
|
top: var(--pos-y);
|
||||||
|
width: var(--width);
|
||||||
|
/* height: var(--height); */
|
||||||
|
|
||||||
|
background: var(--alt-color);
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* padding: 2%; */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
table{
|
||||||
|
display: grid;
|
||||||
|
border-collapse: collapse;
|
||||||
|
min-width: 100%;
|
||||||
|
grid-template-columns: repeat(6, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
thead, tbody, tr{
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 2%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
position: -webkit-sticky;
|
||||||
|
position: sticky;
|
||||||
|
background-image: linear-gradient(var(--alt-color), var(--amazon-color)) ;
|
||||||
|
top: 0;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: var(--base-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
th:last-child {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding-top: 2%;
|
||||||
|
padding-bottom: 2%;
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) td {
|
||||||
|
background: #f8f6f9;
|
||||||
|
}
|
32
www/backend.html
Normal file
32
www/backend.html
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="backend.css" />
|
||||||
|
<meta http-equiv="refresh" content="20">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="wrapper">
|
||||||
|
<div id="logo"> <img src="amazon.svg" /> </div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>worker id</th>
|
||||||
|
<th>ip address</th>
|
||||||
|
<th>country</th>
|
||||||
|
<th>fee</th>
|
||||||
|
<th>task start time</th>
|
||||||
|
<th>task completion time</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{TBODY}}
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue