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 = "*"
|
||||
serial = "*"
|
||||
pyserial = "*"
|
||||
country-converter = "*"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
|
|
97
Pipfile.lock
generated
97
Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "3c0954c7917f567561faffcf18031aa0718c5144698d0de7022dd9ad9d49b1c4"
|
||||
"sha256": "0bb22632889d5e728609ae67e478ff7bdeb7e56b7bb0b2be0f3b5db36ca129c5"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
|
@ -21,18 +21,18 @@
|
|||
},
|
||||
"boto3": {
|
||||
"hashes": [
|
||||
"sha256:98fdd6fa754e17c0d9e87fbb464c43c7e72aaa6b4f78b418eba47b7d15deffee",
|
||||
"sha256:fdaae8edd63ae114107d375862069d17de23e00489a65169b8141ddee6bdf78b"
|
||||
"sha256:5222edc5b20d5c6ab7440fc4f89f987ead05be37ff5cc5359a3b9148d9b5a51e",
|
||||
"sha256:bd3337cfc15613b0091fa567dc3065d94df88e5837ba1adbb1e35b91db728a66"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.10.48"
|
||||
"version": "==1.11.7"
|
||||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:29370f50af7870661609fbfbc4ed01ef2fd531b87b98729700526d1a4b3a2f89",
|
||||
"sha256:7f60edf33c6f5b7c1c9b9377267bdc56495f52704607f713d4c3bd1d82a08334"
|
||||
"sha256:9a17d36ee43f1398c7db3cb29aa2216de94bcb60f058b1c645d71e72a330ddf8",
|
||||
"sha256:e4b82b1a7389f3d16732eb839240c9d3e42470100d5a71415ea2a0a35b911b23"
|
||||
],
|
||||
"version": "==1.13.48"
|
||||
"version": "==1.14.7"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
|
@ -56,6 +56,13 @@
|
|||
"index": "pypi",
|
||||
"version": "==10.0"
|
||||
},
|
||||
"country-converter": {
|
||||
"hashes": [
|
||||
"sha256:bc01ba2592b77a78b4f3e6f76600ca27852d71d1512cf1f320fecbcaaea3c6f9"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.6.7"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
"sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0",
|
||||
|
@ -159,6 +166,56 @@
|
|||
],
|
||||
"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": {
|
||||
"hashes": [
|
||||
"sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be",
|
||||
|
@ -200,7 +257,6 @@
|
|||
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
|
||||
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
|
||||
],
|
||||
"markers": "python_version >= '2.7'",
|
||||
"version": "==2.8.1"
|
||||
},
|
||||
"python-magic": {
|
||||
|
@ -211,6 +267,13 @@
|
|||
"index": "pypi",
|
||||
"version": "==0.4.15"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
|
||||
"sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
|
||||
],
|
||||
"version": "==2019.3"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",
|
||||
|
@ -237,10 +300,10 @@
|
|||
},
|
||||
"s3transfer": {
|
||||
"hashes": [
|
||||
"sha256:6efc926738a3cd576c2a79725fed9afde92378aa5c6a957e3af010cb019fac9d",
|
||||
"sha256:b780f2411b824cb541dbcd2c713d0cb61c7d1bcadae204cdddda2b35cef493ba"
|
||||
"sha256:248dffd2de2dfb870c507b412fc22ed37cd3255293e293c395158e7c55fbe5f9",
|
||||
"sha256:80ed96731b3bd77395cd6197246069092015e1124164b2c152c8f741a823dd04"
|
||||
],
|
||||
"version": "==0.2.1"
|
||||
"version": "==0.3.1"
|
||||
},
|
||||
"serial": {
|
||||
"hashes": [
|
||||
|
@ -252,10 +315,10 @@
|
|||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
|
||||
"sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
|
||||
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
|
||||
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
|
||||
],
|
||||
"version": "==1.13.0"
|
||||
"version": "==1.14.0"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
|
@ -287,10 +350,10 @@
|
|||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293",
|
||||
"sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745"
|
||||
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
|
||||
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
|
||||
],
|
||||
"version": "==1.25.7"
|
||||
"version": "==1.25.8"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
|
|
|
@ -4,16 +4,18 @@ from sqlalchemy import Column, Integer, String, DateTime, Float
|
|||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql.schema import ForeignKey, Sequence
|
||||
from sqlalchemy.engine import create_engine
|
||||
from sqlalchemy.orm.session import sessionmaker
|
||||
from sqlalchemy.orm.session import sessionmaker, object_session
|
||||
import datetime
|
||||
from contextlib import contextmanager
|
||||
import uuid
|
||||
import os
|
||||
import country_converter
|
||||
|
||||
mainLogger = logging.getLogger("sorteerhoed")
|
||||
logger = mainLogger.getChild("store")
|
||||
|
||||
Base = declarative_base()
|
||||
cc = country_converter.CountryConverter()
|
||||
|
||||
"""
|
||||
HIT lifetime:
|
||||
|
@ -92,6 +94,12 @@ class HIT(Base):
|
|||
values['state'] = self.getStatus()
|
||||
values['scan_image'] = self.getImageUrl() if self.scanned_at 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
|
||||
|
||||
def delete(self):
|
||||
|
@ -109,6 +117,20 @@ class HIT(Base):
|
|||
return False
|
||||
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):
|
||||
__tablename__ = 'assignments'
|
||||
id = Column(Integer, Sequence('assignment_id'), primary_key=True) # our sequential hit id
|
||||
|
@ -148,8 +170,22 @@ class Assignment(Base):
|
|||
|
||||
def toDict(self) -> dict:
|
||||
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
|
||||
|
||||
class Store:
|
||||
def __init__(self, db_filename, logLevel=0):
|
||||
|
@ -157,6 +193,7 @@ class Store:
|
|||
if logLevel <= logging.DEBUG:
|
||||
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})
|
||||
Base.metadata.create_all(self.engine)
|
||||
self.Session = sessionmaker(bind=self.engine)
|
||||
|
@ -165,6 +202,15 @@ class Store:
|
|||
self.currentHit = None # mirrors Centralmanagmenet, stored here so we can quickly access it from webserver classes
|
||||
self.updateHooks = []
|
||||
|
||||
# if needsInitialization:
|
||||
# self.insertInitialContent()
|
||||
#
|
||||
# def insertInitialContent(self):
|
||||
# hit = self.createHIT()
|
||||
# assignment = self.newAssignment(hit, 'initial')
|
||||
#
|
||||
|
||||
|
||||
def registerUpdateHook(self, hook):
|
||||
if hook not in self.updateHooks:
|
||||
logger.info(f"Register update hook: {hook}")
|
||||
|
@ -206,11 +252,12 @@ class Store:
|
|||
order_by(HIT.created_at.desc()).first()
|
||||
|
||||
def getNewestHits(self, n = 2) -> list:
|
||||
hits = list(
|
||||
self.session.query(HIT).\
|
||||
q = self.session.query(HIT).\
|
||||
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
|
||||
hits.reverse()
|
||||
return hits
|
||||
|
@ -285,3 +332,4 @@ class Store:
|
|||
return self.session.query(HIT).\
|
||||
filter(HIT.submit_hit_at != None).\
|
||||
order_by(HIT.submit_hit_at.desc()).limit(n)
|
||||
|
||||
|
|
|
@ -224,8 +224,10 @@ class StatusWebSocketHandler(tornado.websocket.WebSocketHandler):
|
|||
# the client connected
|
||||
def open(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
|
||||
def on_close(self):
|
||||
|
@ -369,30 +371,30 @@ class BackendHandler(tornado.web.RequestHandler):
|
|||
|
||||
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)
|
||||
duration = (f"{duration_m}m" if duration_m else "") + f"{duration_s:02d}s"
|
||||
else:
|
||||
duration = "-"
|
||||
# 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)
|
||||
# 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}</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 "-"
|
||||
|
||||
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))
|
||||
contents = open(os.path.join(self.path, 'backend/backend.html'), 'r').read()
|
||||
# contents = contents.replace("{{TBODY}}", "".join(rows))
|
||||
self.write(contents)
|
||||
|
||||
class StatusPage():
|
||||
|
@ -422,11 +424,11 @@ class StatusPage():
|
|||
else:
|
||||
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
|
||||
"""
|
||||
hits = self.store.getNewestHits(2)
|
||||
hits = self.store.getNewestHits(limit)
|
||||
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