Backend as credit fades
This commit is contained in:
parent
08f2e4fd3e
commit
54bb2787bc
7 changed files with 367 additions and 50 deletions
1
Pipfile
1
Pipfile
|
@ -17,6 +17,7 @@ Pillow = "*"
|
||||||
tqdm = "*"
|
tqdm = "*"
|
||||||
serial = "*"
|
serial = "*"
|
||||||
pyserial = "*"
|
pyserial = "*"
|
||||||
|
country-converter = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
|
|
97
Pipfile.lock
generated
97
Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "3c0954c7917f567561faffcf18031aa0718c5144698d0de7022dd9ad9d49b1c4"
|
"sha256": "0bb22632889d5e728609ae67e478ff7bdeb7e56b7bb0b2be0f3b5db36ca129c5"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -21,18 +21,18 @@
|
||||||
},
|
},
|
||||||
"boto3": {
|
"boto3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:98fdd6fa754e17c0d9e87fbb464c43c7e72aaa6b4f78b418eba47b7d15deffee",
|
"sha256:5222edc5b20d5c6ab7440fc4f89f987ead05be37ff5cc5359a3b9148d9b5a51e",
|
||||||
"sha256:fdaae8edd63ae114107d375862069d17de23e00489a65169b8141ddee6bdf78b"
|
"sha256:bd3337cfc15613b0091fa567dc3065d94df88e5837ba1adbb1e35b91db728a66"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.10.48"
|
"version": "==1.11.7"
|
||||||
},
|
},
|
||||||
"botocore": {
|
"botocore": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:29370f50af7870661609fbfbc4ed01ef2fd531b87b98729700526d1a4b3a2f89",
|
"sha256:9a17d36ee43f1398c7db3cb29aa2216de94bcb60f058b1c645d71e72a330ddf8",
|
||||||
"sha256:7f60edf33c6f5b7c1c9b9377267bdc56495f52704607f713d4c3bd1d82a08334"
|
"sha256:e4b82b1a7389f3d16732eb839240c9d3e42470100d5a71415ea2a0a35b911b23"
|
||||||
],
|
],
|
||||||
"version": "==1.13.48"
|
"version": "==1.14.7"
|
||||||
},
|
},
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -56,6 +56,13 @@
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==10.0"
|
"version": "==10.0"
|
||||||
},
|
},
|
||||||
|
"country-converter": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:bc01ba2592b77a78b4f3e6f76600ca27852d71d1512cf1f320fecbcaaea3c6f9"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.6.7"
|
||||||
|
},
|
||||||
"docutils": {
|
"docutils": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0",
|
"sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0",
|
||||||
|
@ -159,6 +166,56 @@
|
||||||
],
|
],
|
||||||
"version": "==1.5.2"
|
"version": "==1.5.2"
|
||||||
},
|
},
|
||||||
|
"numpy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1786a08236f2c92ae0e70423c45e1e62788ed33028f94ca99c4df03f5be6b3c6",
|
||||||
|
"sha256:17aa7a81fe7599a10f2b7d95856dc5cf84a4eefa45bc96123cbbc3ebc568994e",
|
||||||
|
"sha256:20b26aaa5b3da029942cdcce719b363dbe58696ad182aff0e5dcb1687ec946dc",
|
||||||
|
"sha256:2d75908ab3ced4223ccba595b48e538afa5ecc37405923d1fea6906d7c3a50bc",
|
||||||
|
"sha256:39d2c685af15d3ce682c99ce5925cc66efc824652e10990d2462dfe9b8918c6a",
|
||||||
|
"sha256:56bc8ded6fcd9adea90f65377438f9fea8c05fcf7c5ba766bef258d0da1554aa",
|
||||||
|
"sha256:590355aeade1a2eaba17617c19edccb7db8d78760175256e3cf94590a1a964f3",
|
||||||
|
"sha256:70a840a26f4e61defa7bdf811d7498a284ced303dfbc35acb7be12a39b2aa121",
|
||||||
|
"sha256:77c3bfe65d8560487052ad55c6998a04b654c2fbc36d546aef2b2e511e760971",
|
||||||
|
"sha256:9537eecf179f566fd1c160a2e912ca0b8e02d773af0a7a1120ad4f7507cd0d26",
|
||||||
|
"sha256:9acdf933c1fd263c513a2df3dceecea6f3ff4419d80bf238510976bf9bcb26cd",
|
||||||
|
"sha256:ae0975f42ab1f28364dcda3dde3cf6c1ddab3e1d4b2909da0cb0191fa9ca0480",
|
||||||
|
"sha256:b3af02ecc999c8003e538e60c89a2b37646b39b688d4e44d7373e11c2debabec",
|
||||||
|
"sha256:b6ff59cee96b454516e47e7721098e6ceebef435e3e21ac2d6c3b8b02628eb77",
|
||||||
|
"sha256:b765ed3930b92812aa698a455847141869ef755a87e099fddd4ccf9d81fffb57",
|
||||||
|
"sha256:c98c5ffd7d41611407a1103ae11c8b634ad6a43606eca3e2a5a269e5d6e8eb07",
|
||||||
|
"sha256:cf7eb6b1025d3e169989416b1adcd676624c2dbed9e3bcb7137f51bfc8cc2572",
|
||||||
|
"sha256:d92350c22b150c1cae7ebb0ee8b5670cc84848f6359cf6b5d8f86617098a9b73",
|
||||||
|
"sha256:e422c3152921cece8b6a2fb6b0b4d73b6579bd20ae075e7d15143e711f3ca2ca",
|
||||||
|
"sha256:e840f552a509e3380b0f0ec977e8124d0dc34dc0e68289ca28f4d7c1d0d79474",
|
||||||
|
"sha256:f3d0a94ad151870978fb93538e95411c83899c9dc63e6fb65542f769568ecfa5"
|
||||||
|
],
|
||||||
|
"version": "==1.18.1"
|
||||||
|
},
|
||||||
|
"pandas": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:00dff3a8e337f5ed7ad295d98a31821d3d0fe7792da82d78d7fd79b89c03ea9d",
|
||||||
|
"sha256:22361b1597c8c2ffd697aa9bf85423afa9e1fcfa6b1ea821054a244d5f24d75e",
|
||||||
|
"sha256:255920e63850dc512ce356233081098554d641ba99c3767dde9e9f35630f994b",
|
||||||
|
"sha256:26382aab9c119735908d94d2c5c08020a4a0a82969b7e5eefb92f902b3b30ad7",
|
||||||
|
"sha256:33970f4cacdd9a0ddb8f21e151bfb9f178afb7c36eb7c25b9094c02876f385c2",
|
||||||
|
"sha256:4545467a637e0e1393f7d05d61dace89689ad6d6f66f267f86fff737b702cce9",
|
||||||
|
"sha256:52da74df8a9c9a103af0a72c9d5fdc8e0183a90884278db7f386b5692a2220a4",
|
||||||
|
"sha256:61741f5aeb252f39c3031d11405305b6d10ce663c53bc3112705d7ad66c013d0",
|
||||||
|
"sha256:6a3ac2c87e4e32a969921d1428525f09462770c349147aa8e9ab95f88c71ec71",
|
||||||
|
"sha256:7458c48e3d15b8aaa7d575be60e1e4dd70348efcd9376656b72fecd55c59a4c3",
|
||||||
|
"sha256:78bf638993219311377ce9836b3dc05f627a666d0dbc8cec37c0ff3c9ada673b",
|
||||||
|
"sha256:8153705d6545fd9eb6dd2bc79301bff08825d2e2f716d5dced48daafc2d0b81f",
|
||||||
|
"sha256:975c461accd14e89d71772e89108a050fa824c0b87a67d34cedf245f6681fc17",
|
||||||
|
"sha256:9962957a27bfb70ab64103d0a7b42fa59c642fb4ed4cb75d0227b7bb9228535d",
|
||||||
|
"sha256:adc3d3a3f9e59a38d923e90e20c4922fc62d1e5a03d083440468c6d8f3f1ae0a",
|
||||||
|
"sha256:bbe3eb765a0b1e578833d243e2814b60c825b7fdbf4cdfe8e8aae8a08ed56ecf",
|
||||||
|
"sha256:df8864824b1fe488cf778c3650ee59c3a0d8f42e53707de167ba6b4f7d35f133",
|
||||||
|
"sha256:e45055c30a608076e31a9fcd780a956ed3b1fa20db61561b8d88b79259f526f7",
|
||||||
|
"sha256:ee50c2142cdcf41995655d499a157d0a812fce55c97d9aad13bc1eef837ed36c"
|
||||||
|
],
|
||||||
|
"version": "==0.25.3"
|
||||||
|
},
|
||||||
"pillow": {
|
"pillow": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be",
|
"sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be",
|
||||||
|
@ -200,7 +257,6 @@
|
||||||
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
|
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
|
||||||
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
|
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '2.7'",
|
|
||||||
"version": "==2.8.1"
|
"version": "==2.8.1"
|
||||||
},
|
},
|
||||||
"python-magic": {
|
"python-magic": {
|
||||||
|
@ -211,6 +267,13 @@
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.4.15"
|
"version": "==0.4.15"
|
||||||
},
|
},
|
||||||
|
"pytz": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
|
||||||
|
"sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
|
||||||
|
],
|
||||||
|
"version": "==2019.3"
|
||||||
|
},
|
||||||
"pyyaml": {
|
"pyyaml": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",
|
"sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",
|
||||||
|
@ -237,10 +300,10 @@
|
||||||
},
|
},
|
||||||
"s3transfer": {
|
"s3transfer": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:6efc926738a3cd576c2a79725fed9afde92378aa5c6a957e3af010cb019fac9d",
|
"sha256:248dffd2de2dfb870c507b412fc22ed37cd3255293e293c395158e7c55fbe5f9",
|
||||||
"sha256:b780f2411b824cb541dbcd2c713d0cb61c7d1bcadae204cdddda2b35cef493ba"
|
"sha256:80ed96731b3bd77395cd6197246069092015e1124164b2c152c8f741a823dd04"
|
||||||
],
|
],
|
||||||
"version": "==0.2.1"
|
"version": "==0.3.1"
|
||||||
},
|
},
|
||||||
"serial": {
|
"serial": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -252,10 +315,10 @@
|
||||||
},
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
|
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
|
||||||
"sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
|
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
|
||||||
],
|
],
|
||||||
"version": "==1.13.0"
|
"version": "==1.14.0"
|
||||||
},
|
},
|
||||||
"sqlalchemy": {
|
"sqlalchemy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -287,10 +350,10 @@
|
||||||
},
|
},
|
||||||
"urllib3": {
|
"urllib3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293",
|
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
|
||||||
"sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745"
|
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
|
||||||
],
|
],
|
||||||
"version": "==1.25.7"
|
"version": "==1.25.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {}
|
"develop": {}
|
||||||
|
|
|
@ -4,16 +4,18 @@ 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
|
||||||
from sqlalchemy.orm.session import sessionmaker
|
from sqlalchemy.orm.session import sessionmaker, object_session
|
||||||
import datetime
|
import datetime
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
import uuid
|
import uuid
|
||||||
import os
|
import os
|
||||||
|
import country_converter
|
||||||
|
|
||||||
mainLogger = logging.getLogger("sorteerhoed")
|
mainLogger = logging.getLogger("sorteerhoed")
|
||||||
logger = mainLogger.getChild("store")
|
logger = mainLogger.getChild("store")
|
||||||
|
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
cc = country_converter.CountryConverter()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
HIT lifetime:
|
HIT lifetime:
|
||||||
|
@ -92,6 +94,12 @@ class HIT(Base):
|
||||||
values['state'] = self.getStatus()
|
values['state'] = self.getStatus()
|
||||||
values['scan_image'] = self.getImageUrl() if self.scanned_at else None
|
values['scan_image'] = self.getImageUrl() if self.scanned_at else None
|
||||||
values['svg_image'] = self.getSvgImageUrl() if self.isSubmitted() else None
|
values['svg_image'] = self.getSvgImageUrl() if self.isSubmitted() else None
|
||||||
|
values['preceding_assignments'] = [a.toShortDict() for a in self.getBasedOnAssignments()]
|
||||||
|
values['preceding_assignments'].append({
|
||||||
|
'worker_id': 'Ruben van de Ven & Merijn van Moll',
|
||||||
|
'turk_country': 'the Netherlands',
|
||||||
|
'turk_country_code': 'NL'
|
||||||
|
})
|
||||||
return values
|
return values
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
|
@ -108,6 +116,20 @@ class HIT(Base):
|
||||||
if not a:
|
if not a:
|
||||||
return False
|
return False
|
||||||
return bool(a.confirmed_at)
|
return bool(a.confirmed_at)
|
||||||
|
|
||||||
|
def getBasedOnAssignments(self):
|
||||||
|
"""
|
||||||
|
Get preceding assignments, one per worker, excluding the one who did this HIT
|
||||||
|
"""
|
||||||
|
assignment = self.getLastAssignment()
|
||||||
|
session = object_session(self)
|
||||||
|
q = session.query(Assignment).\
|
||||||
|
filter(Assignment.submit_page_at < self.created_at).\
|
||||||
|
group_by(Assignment.worker_id).\
|
||||||
|
order_by(Assignment.created_at.desc())
|
||||||
|
if assignment and assignment.worker_id:
|
||||||
|
q = q.filter(Assignment.worker_id != assignment.worker_id)
|
||||||
|
return q
|
||||||
|
|
||||||
class Assignment(Base):
|
class Assignment(Base):
|
||||||
__tablename__ = 'assignments'
|
__tablename__ = 'assignments'
|
||||||
|
@ -148,8 +170,22 @@ class Assignment(Base):
|
||||||
|
|
||||||
def toDict(self) -> dict:
|
def toDict(self) -> dict:
|
||||||
values = {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
values = {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||||
|
if self.turk_country:
|
||||||
|
values['turk_country_code'] = cc.convert([self.turk_country], to='ISO2')
|
||||||
|
else:
|
||||||
|
values['turk_country_code'] = None
|
||||||
|
return values
|
||||||
|
|
||||||
|
def toShortDict(self) -> dict:
|
||||||
|
values = {
|
||||||
|
'worker_id': self.worker_id,
|
||||||
|
'turk_country': self.turk_country
|
||||||
|
}
|
||||||
|
if self.turk_country:
|
||||||
|
values['turk_country_code'] = cc.convert([self.turk_country], to='ISO2')
|
||||||
|
else:
|
||||||
|
values['turk_country_code'] = None
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
class Store:
|
class Store:
|
||||||
def __init__(self, db_filename, logLevel=0):
|
def __init__(self, db_filename, logLevel=0):
|
||||||
|
@ -157,6 +193,7 @@ class Store:
|
||||||
if logLevel <= logging.DEBUG:
|
if logLevel <= logging.DEBUG:
|
||||||
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
|
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
|
||||||
|
|
||||||
|
needsInitialization = not os.path.exists(path)
|
||||||
self.engine = create_engine('sqlite:///'+path, echo=False, connect_args={'check_same_thread': False})
|
self.engine = create_engine('sqlite:///'+path, echo=False, connect_args={'check_same_thread': False})
|
||||||
Base.metadata.create_all(self.engine)
|
Base.metadata.create_all(self.engine)
|
||||||
self.Session = sessionmaker(bind=self.engine)
|
self.Session = sessionmaker(bind=self.engine)
|
||||||
|
@ -164,6 +201,15 @@ class Store:
|
||||||
|
|
||||||
self.currentHit = None # mirrors Centralmanagmenet, stored here so we can quickly access it from webserver classes
|
self.currentHit = None # mirrors Centralmanagmenet, stored here so we can quickly access it from webserver classes
|
||||||
self.updateHooks = []
|
self.updateHooks = []
|
||||||
|
|
||||||
|
# if needsInitialization:
|
||||||
|
# self.insertInitialContent()
|
||||||
|
#
|
||||||
|
# def insertInitialContent(self):
|
||||||
|
# hit = self.createHIT()
|
||||||
|
# assignment = self.newAssignment(hit, 'initial')
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
def registerUpdateHook(self, hook):
|
def registerUpdateHook(self, hook):
|
||||||
if hook not in self.updateHooks:
|
if hook not in self.updateHooks:
|
||||||
|
@ -206,11 +252,12 @@ class Store:
|
||||||
order_by(HIT.created_at.desc()).first()
|
order_by(HIT.created_at.desc()).first()
|
||||||
|
|
||||||
def getNewestHits(self, n = 2) -> list:
|
def getNewestHits(self, n = 2) -> list:
|
||||||
hits = list(
|
q = self.session.query(HIT).\
|
||||||
self.session.query(HIT).\
|
|
||||||
filter(HIT.deleted_at==None).\
|
filter(HIT.deleted_at==None).\
|
||||||
order_by(HIT.created_at.desc()).limit(2)
|
order_by(HIT.created_at.desc())
|
||||||
)
|
if n is not None:
|
||||||
|
q = q.limit(n)
|
||||||
|
hits = list(q)
|
||||||
# select DESC, because we want latest, then reverse list to get in right order
|
# select DESC, because we want latest, then reverse list to get in right order
|
||||||
hits.reverse()
|
hits.reverse()
|
||||||
return hits
|
return hits
|
||||||
|
@ -285,3 +332,4 @@ class Store:
|
||||||
return self.session.query(HIT).\
|
return self.session.query(HIT).\
|
||||||
filter(HIT.submit_hit_at != None).\
|
filter(HIT.submit_hit_at != None).\
|
||||||
order_by(HIT.submit_hit_at.desc()).limit(n)
|
order_by(HIT.submit_hit_at.desc()).limit(n)
|
||||||
|
|
||||||
|
|
|
@ -224,8 +224,10 @@ class StatusWebSocketHandler(tornado.websocket.WebSocketHandler):
|
||||||
# the client connected
|
# the client connected
|
||||||
def open(self):
|
def open(self):
|
||||||
self.__class__.connections.add(self)
|
self.__class__.connections.add(self)
|
||||||
self.write_message(json.dumps(self.statusPage.fetch(), cls=DateTimeEncoder))
|
limit = 2
|
||||||
|
if 'all' in self.request.query_arguments:
|
||||||
|
limit = None
|
||||||
|
self.write_message(json.dumps(self.statusPage.fetch(limit), cls=DateTimeEncoder))
|
||||||
|
|
||||||
# client disconnected
|
# client disconnected
|
||||||
def on_close(self):
|
def on_close(self):
|
||||||
|
@ -369,30 +371,30 @@ class BackendHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
rows = []
|
rows = []
|
||||||
for hit in self.store.getHITs(100):
|
# for hit in self.store.getHITs(100):
|
||||||
if hit.submit_hit_at and hit.accept_time:
|
# if hit.submit_hit_at and hit.accept_time:
|
||||||
seconds = (hit.submit_hit_at - hit.accept_time).total_seconds()
|
# seconds = (hit.submit_hit_at - hit.accept_time).total_seconds()
|
||||||
duration_m = int(seconds/60)
|
# duration_m = int(seconds/60)
|
||||||
duration_s = max(int(seconds%60), 0)
|
# duration_s = max(int(seconds%60), 0)
|
||||||
duration = (f"{duration_m}m" if duration_m else "") + f"{duration_s:02d}s"
|
# duration = (f"{duration_m}m" if duration_m else "") + f"{duration_s:02d}s"
|
||||||
else:
|
# else:
|
||||||
duration = "-"
|
# duration = "-"
|
||||||
|
#
|
||||||
|
# fee = f"${hit.fee:.2}" if hit.fee else "-"
|
||||||
|
#
|
||||||
|
# rows.append(
|
||||||
|
# f"""
|
||||||
|
# <tr><td></td><td>{hit.worker_id}</td>
|
||||||
|
# <td>{hit.turk_ip}</td>
|
||||||
|
# <td>{hit.turk_country}</td>
|
||||||
|
# <td>{fee}</td>
|
||||||
|
# <td>{hit.accept_time}</td>
|
||||||
|
# <td>{duration}</td><td></td>
|
||||||
|
# """
|
||||||
|
# )
|
||||||
|
|
||||||
fee = f"${hit.fee:.2}" if hit.fee else "-"
|
contents = open(os.path.join(self.path, 'backend/backend.html'), 'r').read()
|
||||||
|
# contents = contents.replace("{{TBODY}}", "".join(rows))
|
||||||
rows.append(
|
|
||||||
f"""
|
|
||||||
<tr><td></td><td>{hit.worker_id}</td>
|
|
||||||
<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)
|
self.write(contents)
|
||||||
|
|
||||||
class StatusPage():
|
class StatusPage():
|
||||||
|
@ -422,11 +424,11 @@ class StatusPage():
|
||||||
else:
|
else:
|
||||||
logger.warn("Status: no server loop to call update command")
|
logger.warn("Status: no server loop to call update command")
|
||||||
|
|
||||||
def fetch(self):
|
def fetch(self, limit = 2):
|
||||||
"""
|
"""
|
||||||
Fetch latest, used on connection of status page
|
Fetch latest, used on connection of status page
|
||||||
"""
|
"""
|
||||||
hits = self.store.getNewestHits(2)
|
hits = self.store.getNewestHits(limit)
|
||||||
return [hit.toDict() for hit in hits]
|
return [hit.toDict() for hit in hits]
|
||||||
|
|
||||||
|
|
||||||
|
|
40
www/backend/backend.html
Normal file
40
www/backend/backend.html
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/backend/style.css" />
|
||||||
|
<script src='../worker_specs/dateformat.js'></script>
|
||||||
|
<script src='../worker_specs/reconnecting-websocket.min.js'></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="wrapper">
|
||||||
|
<div v-if="hit && hit.assignment" :class="[{'invisible': !hit.visible}, 'hit']">
|
||||||
|
<img :src="hit.scan_image">
|
||||||
|
|
||||||
|
<div class='credits'>
|
||||||
|
<span class='worker_id'>{{hit.assignment.worker_id}}</span>
|
||||||
|
<span class='country'>{{hit.assignment.turk_country}}</span>
|
||||||
|
<template v-if="hit.preceding_assignments.length">
|
||||||
|
<div id='collaborators'>
|
||||||
|
<div id='collab_items'>
|
||||||
|
<span v-for="a of hit.preceding_assignments" class='credit'>
|
||||||
|
<span class='worker_id'>{{a.worker_id}}</span>
|
||||||
|
<span class='country'>{{a.turk_country_code}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/backend/script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
60
www/backend/script.js
Normal file
60
www/backend/script.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
var app = new Vue({
|
||||||
|
el: '#wrapper',
|
||||||
|
data: {
|
||||||
|
hit: {}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
var hits = {};
|
||||||
|
var hitIds = [];
|
||||||
|
|
||||||
|
// fetch ?all in database
|
||||||
|
let ws = new ReconnectingWebSocket('ws://localhost:8888/status/ws?all')
|
||||||
|
|
||||||
|
|
||||||
|
ws.addEventListener('open', () => {
|
||||||
|
// ws.send('hi server')
|
||||||
|
})
|
||||||
|
|
||||||
|
ws.addEventListener('message', (event) => {
|
||||||
|
console.log('message: ')
|
||||||
|
|
||||||
|
let load_hits = JSON.parse(event.data)
|
||||||
|
for(let hit of load_hits) {
|
||||||
|
console.log(hit);
|
||||||
|
hits[hit.id] = hit;
|
||||||
|
if(hit.scanned_at && hitIds.indexOf(hit.id) < 0){
|
||||||
|
// only add if indeed scanned
|
||||||
|
hitIds.push(hit.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var currentI = 0;
|
||||||
|
setInterval(function(e){
|
||||||
|
if(hitIds.length < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentI = (currentI + 1) % hitIds.length;
|
||||||
|
let currentHitId = hitIds[currentI];
|
||||||
|
console.log(currentHitId);
|
||||||
|
let hit = hits[currentHitId];
|
||||||
|
hit['visible'] = false;
|
||||||
|
app.hit['visible'] = false;
|
||||||
|
setTimeout(() => {
|
||||||
|
app.hit = hits[currentHitId]
|
||||||
|
}, 1000);
|
||||||
|
setTimeout(() => {
|
||||||
|
app.hit['visible'] = true;
|
||||||
|
let wrapperEl = document.getElementById("collaborators");
|
||||||
|
let innerEl = document.getElementById("collab_items");
|
||||||
|
let size = 100;
|
||||||
|
do {
|
||||||
|
innerEl.style.fontSize = size + "%";
|
||||||
|
size --;
|
||||||
|
} while(innerEl.clientHeight > wrapperEl.clientHeight && size > 10);
|
||||||
|
}, 1100);
|
||||||
|
|
||||||
|
}, 4000);
|
103
www/backend/style.css
Normal file
103
www/backend/style.css
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'bebas';
|
||||||
|
src: url('/font/BebasNeue-Regular.ttf');
|
||||||
|
}
|
||||||
|
|
||||||
|
:root{
|
||||||
|
|
||||||
|
--base-font-size: 30px;
|
||||||
|
--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: 0px; /* */
|
||||||
|
/* */ --pos-y: 50px; /* */
|
||||||
|
/* */ --width: 100%; /* 285mm */
|
||||||
|
/* */ --height: 100%; /* 500mm */
|
||||||
|
/* */ /* */
|
||||||
|
/* //////////////////////////////////////////// */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body{
|
||||||
|
background: black;
|
||||||
|
margin:0;
|
||||||
|
color:white;
|
||||||
|
font-family: bebas;
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hit{
|
||||||
|
position:absolute;
|
||||||
|
top:var(--pos-y);
|
||||||
|
left:0;
|
||||||
|
right:0;
|
||||||
|
bottom:0;
|
||||||
|
text-align: center;
|
||||||
|
transition: opacity 1s;
|
||||||
|
opacity: 1;
|
||||||
|
/*animation: fadeIn 1s, fadeOut 1s 2s;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.hit.invisible{
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn{
|
||||||
|
from{opacity:0;}
|
||||||
|
to{opacity:1;}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeOut{
|
||||||
|
from{opacity:1;}
|
||||||
|
to{opacity:0;}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.hit img{
|
||||||
|
display: block;
|
||||||
|
margin: 10vh auto 7vh;
|
||||||
|
width: 1000px;
|
||||||
|
height: 500px;
|
||||||
|
border: solid 1px white;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.credits{
|
||||||
|
font-size: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credits::before{
|
||||||
|
content:'created by';
|
||||||
|
display:block;
|
||||||
|
font-size: 60%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.country{color: gray;}
|
||||||
|
.country::before{content:'(';}
|
||||||
|
.country::after{content:')';}
|
||||||
|
|
||||||
|
#collaborators{
|
||||||
|
height: 30vh;
|
||||||
|
width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
#collab_items{
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
#collaborators::before{
|
||||||
|
margin-top: 7vh;
|
||||||
|
content:'in collaboration with';
|
||||||
|
display:block;
|
||||||
|
font-size: 60%;
|
||||||
|
}
|
||||||
|
#collaborators .credit{
|
||||||
|
margin-right:10px;
|
||||||
|
}
|
Loading…
Reference in a new issue