Init
This commit is contained in:
commit
f78c9d349a
27 changed files with 4998 additions and 0 deletions
427
.gitignore
vendored
Normal file
427
.gitignore
vendored
Normal file
|
@ -0,0 +1,427 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/python,flask,node,emacs,linux,windows,macos
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=python,flask,node,emacs,linux,windows,macos
|
||||
|
||||
### Emacs ###
|
||||
# -*- mode: gitignore; -*-
|
||||
*~
|
||||
\#*\#
|
||||
/.emacs.desktop
|
||||
/.emacs.desktop.lock
|
||||
*.elc
|
||||
auto-save-list
|
||||
tramp
|
||||
.\#*
|
||||
|
||||
# Org-mode
|
||||
.org-id-locations
|
||||
*_archive
|
||||
|
||||
# flymake-mode
|
||||
*_flymake.*
|
||||
|
||||
# eshell files
|
||||
/eshell/history
|
||||
/eshell/lastdir
|
||||
|
||||
# elpa packages
|
||||
/elpa/
|
||||
|
||||
# reftex files
|
||||
*.rel
|
||||
|
||||
# AUCTeX auto folder
|
||||
/auto/
|
||||
|
||||
# cask packages
|
||||
.cask/
|
||||
dist/
|
||||
|
||||
# Flycheck
|
||||
flycheck_*.el
|
||||
|
||||
# server auth directory
|
||||
/server/
|
||||
|
||||
# projectiles files
|
||||
.projectile
|
||||
|
||||
# directory configuration
|
||||
.dir-locals.el
|
||||
|
||||
# network security
|
||||
/network-security.data
|
||||
|
||||
|
||||
### Flask ###
|
||||
instance/*
|
||||
!instance/.gitignore
|
||||
.webassets-cache
|
||||
|
||||
### Flask.Python Stack ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
pytestdebug.log
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
doc/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
### Linux ###
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
|
||||
# C extensions
|
||||
|
||||
# Distribution / packaging
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
|
||||
# Installer logs
|
||||
|
||||
# Unit test / coverage reports
|
||||
|
||||
# Translations
|
||||
|
||||
# Django stuff:
|
||||
|
||||
# Flask stuff:
|
||||
|
||||
# Scrapy stuff:
|
||||
|
||||
# Sphinx documentation
|
||||
|
||||
# PyBuilder
|
||||
|
||||
# Jupyter Notebook
|
||||
|
||||
# IPython
|
||||
|
||||
# pyenv
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
|
||||
# Celery stuff
|
||||
|
||||
# SageMath parsed files
|
||||
|
||||
# Environments
|
||||
|
||||
# Spyder project settings
|
||||
|
||||
# Rope project settings
|
||||
|
||||
# mkdocs documentation
|
||||
|
||||
# mypy
|
||||
|
||||
# Pyre type checker
|
||||
|
||||
# pytype static type analyzer
|
||||
|
||||
### Windows ###
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/python,flask,node,emacs,linux,windows,macos
|
15
Pipfile
Normal file
15
Pipfile
Normal file
|
@ -0,0 +1,15 @@
|
|||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[packages]
|
||||
flask = "*"
|
||||
walrus = "*"
|
||||
flask-wtf = "*"
|
||||
wtforms = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.8"
|
125
Pipfile.lock
generated
Normal file
125
Pipfile.lock
generated
Normal file
|
@ -0,0 +1,125 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "f58e03f82108622c44893db80327f71087a066a46a5232716aa9e9f3e0cfcdb5"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.8"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
|
||||
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
|
||||
],
|
||||
"version": "==7.1.2"
|
||||
},
|
||||
"flask": {
|
||||
"hashes": [
|
||||
"sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060",
|
||||
"sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.1.2"
|
||||
},
|
||||
"flask-wtf": {
|
||||
"hashes": [
|
||||
"sha256:57b3faf6fe5d6168bda0c36b0df1d05770f8e205e18332d0376ddb954d17aef2",
|
||||
"sha256:d417e3a0008b5ba583da1763e4db0f55a1269d9dd91dcc3eb3c026d3c5dbd720"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.14.3"
|
||||
},
|
||||
"itsdangerous": {
|
||||
"hashes": [
|
||||
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
|
||||
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
|
||||
],
|
||||
"version": "==1.1.0"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
|
||||
"sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"
|
||||
],
|
||||
"version": "==2.11.2"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
|
||||
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
|
||||
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
|
||||
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
|
||||
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
|
||||
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
|
||||
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
|
||||
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
|
||||
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
|
||||
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
|
||||
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
|
||||
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
|
||||
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
|
||||
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
|
||||
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
|
||||
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
|
||||
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
|
||||
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
|
||||
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
|
||||
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
|
||||
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
|
||||
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
|
||||
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
|
||||
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
|
||||
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
|
||||
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
|
||||
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
|
||||
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
|
||||
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
|
||||
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
|
||||
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
|
||||
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
|
||||
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
|
||||
],
|
||||
"version": "==1.1.1"
|
||||
},
|
||||
"redis": {
|
||||
"hashes": [
|
||||
"sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2",
|
||||
"sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"
|
||||
],
|
||||
"version": "==3.5.3"
|
||||
},
|
||||
"walrus": {
|
||||
"hashes": [
|
||||
"sha256:6752420331b0110af6b3c6d32e61252dbafbd05ae2fc1a5fbfa6d42d2382062a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.8.1"
|
||||
},
|
||||
"werkzeug": {
|
||||
"hashes": [
|
||||
"sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43",
|
||||
"sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"
|
||||
],
|
||||
"version": "==1.0.1"
|
||||
},
|
||||
"wtforms": {
|
||||
"hashes": [
|
||||
"sha256:7b504fc724d0d1d4d5d5c114e778ec88c37ea53144683e084215eed5155ada4c",
|
||||
"sha256:81195de0ac94fbc8368abbaf9197b88c4f3ffd6c2719b5bf5fc9da744f3d829c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.3.3"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
31
api.py
Normal file
31
api.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
from flask import request, jsonify
|
||||
from walrus import Walrus
|
||||
from time import time
|
||||
|
||||
import uuid
|
||||
import struct
|
||||
|
||||
from countdown import api_api
|
||||
|
||||
db = Walrus(host='localhost', port=6379, db=0)
|
||||
|
||||
@app.route('/api/vi1/<uuid:id>', methods=['GET'])
|
||||
def get_countdown(id):
|
||||
ct = db.Hash(str(id))
|
||||
|
||||
resp = ct.as_dict(decode=True)
|
||||
resp['left'] = float(ct['total']) - (time() - float(ct['start']))
|
||||
|
||||
return resp
|
||||
|
||||
@api_v1.route('/api/v1', methods=['POST'])
|
||||
def create_countdown():
|
||||
countdown = request.json
|
||||
ct_id = str(uuid.uuid4())
|
||||
ct = db.Hash(ct_id)
|
||||
ct.update(start=time(), total=countdown['total'])
|
||||
|
||||
resp = ct.as_dict(decode=True)
|
||||
resp['id'] = ct_id
|
||||
|
||||
return resp
|
0
api/__init__.py
Normal file
0
api/__init__.py
Normal file
7
api/v1/__init__.py
Normal file
7
api/v1/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from flask import Blueprint
|
||||
|
||||
api_v1 = Blueprint('api.v1', __name__)
|
||||
|
||||
import api.v1.clock
|
||||
import api.v1.countdown
|
||||
|
7
api/v1/clock.py
Normal file
7
api/v1/clock.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from time import time
|
||||
|
||||
from api.v1 import api_v1
|
||||
|
||||
@api_v1.route('/time/<float:t1>')
|
||||
def netime_time(t1: float) -> str:
|
||||
return str(time())
|
31
api/v1/countdown.py
Normal file
31
api/v1/countdown.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
from flask import request, jsonify
|
||||
from walrus import Walrus
|
||||
from time import time
|
||||
|
||||
import uuid
|
||||
import struct
|
||||
|
||||
from api.v1 import api_v1
|
||||
|
||||
db = Walrus(host='localhost', port=6379, db=0)
|
||||
|
||||
@api_v1.route('/countdown/<uuid:id>', methods=['GET'])
|
||||
def get_countdown(id):
|
||||
ct = db.Hash(str(id))
|
||||
|
||||
resp = ct.as_dict(decode=True)
|
||||
resp['left'] = float(ct['total']) - (time() - float(ct['start']))
|
||||
|
||||
return resp
|
||||
|
||||
@api_v1.route('/countdown', methods=['POST'])
|
||||
def create_countdown():
|
||||
countdown = request.json
|
||||
ct_id = str(uuid.uuid4())
|
||||
ct = db.Hash(ct_id)
|
||||
ct.update(start=time(), total=countdown['total'])
|
||||
|
||||
resp = ct.as_dict(decode=True)
|
||||
resp['id'] = ct_id
|
||||
|
||||
return resp
|
6
countdown/__init__.py
Normal file
6
countdown/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from flask import Blueprint
|
||||
|
||||
app = Blueprint('countdown', __name__)
|
||||
|
||||
import views
|
||||
import apii
|
4
countdown/forms.py
Normal file
4
countdown/forms.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
from flask_wtf import FlaskForm, TimeField
|
||||
|
||||
class CountdownAdminForm(FlaskForm):
|
||||
totalTime = TimeField('Time')
|
19
countdown/views.py
Normal file
19
countdown/views.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
from flask import render_template, request, flash, redirect, url_for
|
||||
|
||||
from countdown import app, forms
|
||||
|
||||
@app.route('/<uuid:id>', methods=['GET'])
|
||||
def countdown(id):
|
||||
return render_template('countdown.html', id)
|
||||
|
||||
@app.route('/', methods=['GET', 'POST', 'PUT'])
|
||||
def countdown_admin():
|
||||
form = CountdownAdminForm(request.form)
|
||||
if request.method == 'POST' and form.validate():
|
||||
user = User(form.username.data, form.email.data,
|
||||
form.password.data)
|
||||
db_session.add(user)
|
||||
flash('Thanks for registering')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
return render_template('countdown_admin.html', form=form, clock=None)
|
3
css/netclock.scss
Normal file
3
css/netclock.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
body {
|
||||
color: red;
|
||||
}
|
4
forms.py
Normal file
4
forms.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
from flask_wtf import FlaskForm, TimeField
|
||||
|
||||
class CountdownAdminForm(FlaskForm):
|
||||
totalTime = TimeField('Time')
|
24
js/countdown.js
Normal file
24
js/countdown.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import log from 'loglevel';
|
||||
import $ from 'jquery';
|
||||
|
||||
|
||||
const updateCountdown = () => $.getJSON({
|
||||
url: "api/v1/countdown/070f478f-e168-488c-918e-adb37c9c0cbd",
|
||||
data: Date.now(),
|
||||
success: function(countdown) {
|
||||
if(countdown.left <= 0) {
|
||||
clearInterval(scheduledUpdater);
|
||||
$('#clock').text(0);
|
||||
return;
|
||||
}
|
||||
|
||||
let floor = Math.floor(countdown.left);
|
||||
|
||||
let frac = countdown.left - floor;
|
||||
let milli = Math.floor(frac * 1000); // get in millisecond resolution
|
||||
|
||||
setTimeout(() => $('#clock').text(floor), milli);
|
||||
}
|
||||
});
|
||||
|
||||
let scheduledUpdater = setInterval(updateCountdown, 1000);
|
6
js/netclock.js
Normal file
6
js/netclock.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import '../css/netclock.scss';
|
||||
import log from 'loglevel';
|
||||
|
||||
if (process.env.LOG_LEVEL) {
|
||||
log.setDefaultLevel(process.env.LOG_LEVEL);
|
||||
}
|
7
netclock.py
Normal file
7
netclock.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from flask import Flask
|
||||
app = Flask(__name__)
|
||||
|
||||
from api.v1 import api_v1
|
||||
app.register_blueprint(api_v1, url_prefix="/api/v1")
|
||||
|
||||
import views
|
4155
package-lock.json
generated
Normal file
4155
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
27
package.json
Normal file
27
package.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "netclock",
|
||||
"version": "0.0.1",
|
||||
"description": "A collection of time widgets for the web",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.dev.js",
|
||||
"watch": "webpack --watch --config webpack.dev.js",
|
||||
"publish": "webpack --config webpack.prod.js"
|
||||
},
|
||||
"author": "Armin Friedl",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"css-loader": "^4.2.2",
|
||||
"sass": "^1.26.10",
|
||||
"sass-loader": "^10.0.2",
|
||||
"style-loader": "^1.2.1",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-merge": "^5.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"jquery": "^3.5.1",
|
||||
"loglevel": "^1.7.0"
|
||||
}
|
||||
}
|
BIN
static/favicon.ico
Normal file
BIN
static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
16
templates/base.html
Normal file
16
templates/base.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Netclock {% if self.title() %} - {% endif %}{% block title %}{% endblock title %}</title>
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% block body %}{% endblock body %}
|
||||
|
||||
<script src="{{ url_for('static', filename='dist/netclock.bundle.js') }}"></script>
|
||||
{% block scripts %}{% endblock scripts %}
|
||||
</body>
|
||||
|
||||
</html>
|
11
templates/countdown.html
Normal file
11
templates/countdown.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Countdown{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
Hello from Countdown
|
||||
<div id="clock"></div>
|
||||
{% endblock body %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='dist/countdown.bundle.js') }}"></script>
|
||||
{% endblock scripts %}
|
11
templates/countdown_admin.html
Normal file
11
templates/countdown_admin.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Countdown{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
Hello from Countdown
|
||||
<div id="clock"></div>
|
||||
{% endblock body %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='dist/countdown.bundle.js') }}"></script>
|
||||
{% endblock scripts %}
|
3
templates/netclock.html
Normal file
3
templates/netclock.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block body %}Index{% endblock body %}
|
9
views.py
Normal file
9
views.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from flask import Flask, render_template, request, flash
|
||||
from netclock import app
|
||||
|
||||
from forms import CountdownAdminForm
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('netclock.html')
|
||||
|
29
webpack.common.js
Normal file
29
webpack.common.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
netclock: './js/netclock.js',
|
||||
countdown: './js/countdown.js'
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin()
|
||||
],
|
||||
output: {
|
||||
filename: '[name].bundle.js',
|
||||
path: path.resolve(__dirname, 'static', 'dist')
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.s[ac]ss$/i,
|
||||
use: [
|
||||
// Creates `style` nodes from JS strings
|
||||
'style-loader',
|
||||
// Translates CSS into CommonJS
|
||||
'css-loader',
|
||||
// Compiles Sass to CSS
|
||||
'sass-loader',
|
||||
]
|
||||
}]
|
||||
}
|
||||
};
|
14
webpack.dev.js
Normal file
14
webpack.dev.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
const common = require('./webpack.common.js');
|
||||
const webpack = require('webpack');
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'development',
|
||||
devtool: 'eval-source-map',
|
||||
plugins: [
|
||||
new webpack.EnvironmentPlugin({
|
||||
LOG_LEVEL: 'trace'
|
||||
})
|
||||
]
|
||||
});
|
7
webpack.prod.js
Normal file
7
webpack.prod.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
const common = require('./webpack.common.js');
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'production'
|
||||
});
|
Loading…
Reference in a new issue