Fuck
This commit is contained in:
commit
67bc482155
14 changed files with 695 additions and 0 deletions
191
.gitignore
vendored
Normal file
191
.gitignore
vendored
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/vim,flask
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=vim,flask
|
||||||
|
|
||||||
|
### Flask ###
|
||||||
|
instance/*
|
||||||
|
!instance/.gitignore
|
||||||
|
.webassets-cache
|
||||||
|
.env
|
||||||
|
|
||||||
|
### Flask.Python Stack ###
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
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/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .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
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.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/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
||||||
|
### Vim ###
|
||||||
|
# Swap
|
||||||
|
[._]*.s[a-v][a-z]
|
||||||
|
!*.svg # comment out if you don't need vector files
|
||||||
|
[._]*.sw[a-p]
|
||||||
|
[._]s[a-rt-v][a-z]
|
||||||
|
[._]ss[a-gi-z]
|
||||||
|
[._]sw[a-p]
|
||||||
|
|
||||||
|
# Session
|
||||||
|
Session.vim
|
||||||
|
Sessionx.vim
|
||||||
|
|
||||||
|
# Temporary
|
||||||
|
.netrwhist
|
||||||
|
*~
|
||||||
|
# Auto-generated tag files
|
||||||
|
tags
|
||||||
|
# Persistent undo
|
||||||
|
[._]*.un~
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/vim,flask
|
15
Dockerfile
Normal file
15
Dockerfile
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
RUN apk add python3 py3-pip
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ADD requirements.txt .
|
||||||
|
|
||||||
|
RUN python3 -m pip install -r requirements.txt
|
||||||
|
|
||||||
|
COPY . /app
|
||||||
|
|
||||||
|
ENTRYPOINT [ "python3" ]
|
||||||
|
|
||||||
|
CMD [ "main.py" ]
|
24
app/__init__.py
Normal file
24
app/__init__.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from flask import Flask
|
||||||
|
# from flask_login import LoginManager
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
|
UPLOAD_FOLDER = 'app/static/uploads/'
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.secret_key = 'secret key'
|
||||||
|
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
||||||
|
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
|
||||||
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////db/db.sqlite3'
|
||||||
|
# app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://user:password@sql/ece3553'
|
||||||
|
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||||
|
|
||||||
|
# login_manager = LoginManager(app)
|
||||||
|
|
||||||
|
db = SQLAlchemy(app)
|
||||||
|
|
||||||
|
from app import views
|
||||||
|
from app import admin_views
|
||||||
|
from app import tables
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
db.create_all()
|
9
app/admin_views.py
Normal file
9
app/admin_views.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from functools import wraps
|
||||||
|
from app import app, db
|
||||||
|
from app.tables import Swipe, User
|
||||||
|
from flask import request, redirect, url_for, render_template, session, abort
|
||||||
|
from sqlalchemy import desc
|
||||||
|
# from flask_login import login_user, login_required, logout_user, current_user
|
||||||
|
|
||||||
|
|
||||||
|
|
70
app/static/css/main.css
Normal file
70
app/static/css/main.css
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
body {
|
||||||
|
background-color: #242424;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #141414;
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li {
|
||||||
|
float: left;
|
||||||
|
margin: 0px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li a {
|
||||||
|
display: block;
|
||||||
|
color: white;
|
||||||
|
background-color: #888;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 15px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li a:hover {
|
||||||
|
background-color: #005c75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-cur {
|
||||||
|
background-color: #008cff;
|
||||||
|
transform: translate(0px, 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-cur:hover {
|
||||||
|
background-color: #00bcff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: block;
|
||||||
|
min-height: 200px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hcenter {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hvcenter {
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
22
app/static/js/kiosk.js
Normal file
22
app/static/js/kiosk.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
function update() {
|
||||||
|
fetch('https://time.qtechofficial.com/kiosk/data')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
let tbody = document.getElementById('tbody');
|
||||||
|
tbody.innerHTML = "";
|
||||||
|
console.log(data);
|
||||||
|
data.forEach(person => {
|
||||||
|
let tr = document.createElement('tr');
|
||||||
|
tbody.appendChild(tr);
|
||||||
|
let td_name = document.createElement('td');
|
||||||
|
td_name.innerHTML = person.first_name + " " + person.last_name;
|
||||||
|
tr.appendChild(td_name);
|
||||||
|
let td_time_here = document.createElement('td');
|
||||||
|
td_time_here.textContent = person.time_here;
|
||||||
|
tr.appendChild(td_time_here);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(error => console.error(error))
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(update, 1000);
|
21
app/tables.py
Normal file
21
app/tables.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from app import db
|
||||||
|
|
||||||
|
|
||||||
|
class Swipe(db.Model):
|
||||||
|
__tablename__ = 'swipes'
|
||||||
|
id = db.Column('id', db.Integer, primary_key=True)
|
||||||
|
time = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
|
||||||
|
uuid = db.Column(db.String(100))
|
||||||
|
|
||||||
|
def __init__(self, uuid):
|
||||||
|
self.uuid = uuid
|
||||||
|
|
||||||
|
|
||||||
|
class User(db.Model):
|
||||||
|
__tablename__ = 'users'
|
||||||
|
id = db.Column('id', db.Integer, primary_key=True)
|
||||||
|
uuid = db.Column(db.String(100))
|
||||||
|
first_name = db.Column(db.String(100))
|
||||||
|
last_name = db.Column(db.String(100))
|
||||||
|
role = db.Column(db.String(100))
|
46
app/templates/fuckyou.html
Normal file
46
app/templates/fuckyou.html
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version = "1.0"?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/main.css') }}" />
|
||||||
|
<title>Go Fuck Yourself</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="main_page">
|
||||||
|
<header>
|
||||||
|
<hgroup>
|
||||||
|
<div align="center">
|
||||||
|
<h1>Go Fuck Yourself</h1>
|
||||||
|
<h2>Consider Eating A Dick</h2>
|
||||||
|
</div>
|
||||||
|
</hgroup>
|
||||||
|
</header>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{{ '' }}">Home</a></li>
|
||||||
|
<li><a href="{{ '' }}">Launch Planner</a></li>
|
||||||
|
<li><a href="{{ '' }}">Silos</a></li>
|
||||||
|
<li><a href="{{ '' }}">Notes</a></li>
|
||||||
|
<li><a href="{{ '' }}">Blueprints n Shit</a></li>
|
||||||
|
<li><a href="{{ '' }}">C&C</a></li>
|
||||||
|
<li><a href="{{ '' }}">About</a></li>
|
||||||
|
<li style="float: right;"><a href="{{ '' }}">Logout</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<div class="content">
|
||||||
|
<p class="hcenter">
|
||||||
|
<h3>Why you should go fuck yourself</h3>
|
||||||
|
</p>
|
||||||
|
<p class="hcenter">
|
||||||
|
<p>hello and welcome to my presentation to why you should go fuck yourself.</p>
|
||||||
|
<p>Spoiler Alert, it's because you are a piece of shit that no one likes. But why are you shit? were you raised this way or, unfortunate circumstances to blame?, No, your just terrible. Still confused? of course you are, here's a pie chart that should straighten things up, here's a quick visual aid and i think this next slide really drives the point home, fuck you go fuck yourself.</p>
|
||||||
|
<p>Now this graph is, really interesting. As you can see it shows the number of people that want you to go fuck yourself over time. Of note, this isn't 99% percent of the population. This isn't even 99.999% of the population. This is the 100% of the population, that's right, even you want you to go fuck yourself so what are you doing here? Go on go do it now, You may be asking, does all this hatred represent a weakness on my own part?</p>
|
||||||
|
<p>Could it be that nobody deserves to hear the words "go fuck yourself" because every single human being on the planet is a worthwhile and a valuable member of society? No!, go fuck yourself.</p>
|
||||||
|
<p>In conclusion, i want you to know that i don't really mean anything i'm saying here. I think you're a good person and ultimately i don't want you to go fuck yourself. Just kidding haha, go fuck yourself.</p>
|
||||||
|
<p>Thank you.</p>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
16
app/templates/kiosk.html
Normal file
16
app/templates/kiosk.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version = "1.0"?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/main.css') }}" />
|
||||||
|
<script type="text/javascript" src="{{ url_for('static', filename='js/kiosk.js') }}"></script>
|
||||||
|
<title>Kiosk</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table>
|
||||||
|
<tbody id="tbody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
29
app/templates/users.html
Normal file
29
app/templates/users.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version = "1.0"?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/main.css') }}" />
|
||||||
|
<title>Users</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form action="/users/modify" method="POST">
|
||||||
|
{% for user in users %}
|
||||||
|
<input type="radio" name="uuid" value="{{user.uuid}}">
|
||||||
|
{{ user.id }}, {{ user.uuid }}, {{ user.first_name | safe }}, {{ user.last_name | safe }}, {{ user.role }}
|
||||||
|
</input> <br>
|
||||||
|
{% endfor %}
|
||||||
|
<input type="text", name="first_name", placeholder="First Name" />
|
||||||
|
<input type="text", name="last_name", placeholder="Last Name" /><br>
|
||||||
|
<input type="text", name="new_uuid", placeholder="0000000000" /><br>
|
||||||
|
<select name='role'>
|
||||||
|
<option value='Student'>Student</option>
|
||||||
|
<option value='Mentor'>Mentor</option>
|
||||||
|
</select><br>
|
||||||
|
<button type="submit", name="action", value="set_name">Change Name</button>
|
||||||
|
<button type="submit", name="action", value="set_uid">Change ID</button>
|
||||||
|
<button type="submit", name="action", value="new_user">New User</button>
|
||||||
|
<button type="submit", name="action", value="delete">Delete</button><br>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
240
app/views.py
Normal file
240
app/views.py
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
from app import app, db
|
||||||
|
from app.tables import Swipe, User
|
||||||
|
from flask import request, redirect, url_for, render_template
|
||||||
|
from sqlalchemy import desc
|
||||||
|
from pytz import timezone
|
||||||
|
from datetime import datetime
|
||||||
|
from datetime import date
|
||||||
|
import json
|
||||||
|
# from flask_login import login_user, login_required, logout_user
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return 'No.'
|
||||||
|
|
||||||
|
'''
|
||||||
|
@app.route('/auth', methods=['POST'])
|
||||||
|
def auth():
|
||||||
|
data = request.get_json(force=True)
|
||||||
|
|
||||||
|
print(data)
|
||||||
|
if 'uuid' in data:
|
||||||
|
print(data['uuid'])
|
||||||
|
uuid = AllowedUUIDs.query.filter_by(uuid=data['uuid']).first()
|
||||||
|
print(uuid)
|
||||||
|
if uuid:
|
||||||
|
print('login')
|
||||||
|
login_user(uuid)
|
||||||
|
return ''
|
||||||
|
'''
|
||||||
|
|
||||||
|
@app.route('/users', methods=['GET'])
|
||||||
|
def users():
|
||||||
|
users = db.session.query(User)
|
||||||
|
return render_template('users.html', users=users)
|
||||||
|
|
||||||
|
@app.route('/users/modify', methods=['POST'])
|
||||||
|
def users_modify():
|
||||||
|
data = request.form
|
||||||
|
if data['action'] == 'set_name':
|
||||||
|
if data['first_name'] == '' or data['last_name'] == '':
|
||||||
|
return 'Fill out the form...'
|
||||||
|
if data['uuid'] == '0000005222':
|
||||||
|
return 'No.'
|
||||||
|
|
||||||
|
user = User.query.filter_by(uuid=data['uuid']).first()
|
||||||
|
user.first_name = data['first_name']
|
||||||
|
user.last_name = data['last_name']
|
||||||
|
db.session.commit()
|
||||||
|
elif data['action'] == 'set_uid':
|
||||||
|
if data['uuid'] == '':
|
||||||
|
return 'Fill out the form...'
|
||||||
|
if data['uuid'] == '0000005222':
|
||||||
|
return 'No.'
|
||||||
|
|
||||||
|
user = User.query.filter_by(uuid=data['uuid']).first()
|
||||||
|
user.uuid = data['new_uuid']
|
||||||
|
db.session.commit()
|
||||||
|
elif data['action'] == 'delete':
|
||||||
|
if data['uuid'] == '0000005222':
|
||||||
|
return 'No.'
|
||||||
|
user = User.query.filter_by(uuid=data['uuid']).first()
|
||||||
|
db.session.delete(user)
|
||||||
|
db.session.commit()
|
||||||
|
elif data['action'] == 'new_user':
|
||||||
|
if data['new_uuid'] == '0000005222':
|
||||||
|
return 'No.'
|
||||||
|
user = User()
|
||||||
|
user.uuid = data['new_uuid']
|
||||||
|
user.first_name = data['first_name']
|
||||||
|
user.last_name = data['last_name']
|
||||||
|
user.role = data['role']
|
||||||
|
db.session.add(user)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
return redirect(url_for('users'), code=302)
|
||||||
|
|
||||||
|
@app.route('/swipe', methods=['POST'])
|
||||||
|
def swipe():
|
||||||
|
data = request.get_json(force=True)
|
||||||
|
uuid = data.get('uuid')
|
||||||
|
if not uuid:
|
||||||
|
return 'NACK'
|
||||||
|
|
||||||
|
print(uuid)
|
||||||
|
|
||||||
|
swipe = Swipe(uuid)
|
||||||
|
db.session.add(swipe)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return 'OK'
|
||||||
|
|
||||||
|
@app.route('/log', methods=['GET'])
|
||||||
|
def log():
|
||||||
|
swipes = db.session.query(Swipe)
|
||||||
|
result = ''
|
||||||
|
for swipe in swipes:
|
||||||
|
names = User.query.filter_by(uuid=swipe.uuid).first()
|
||||||
|
if names:
|
||||||
|
result += f'{swipe.time} | {names.first_name} | {names.last_name}<br>'
|
||||||
|
else:
|
||||||
|
result += f'{swipe.time} | WHO DIS {swipe.uuid}<br>'
|
||||||
|
return result
|
||||||
|
|
||||||
|
@app.route('/kiosk', methods=['GET'])
|
||||||
|
def kiosk():
|
||||||
|
return render_template('kiosk.html')
|
||||||
|
|
||||||
|
@app.route('/kiosk/data', methods=['GET'])
|
||||||
|
def kiosk_data():
|
||||||
|
swipes = db.session.query(Swipe).filter(Swipe.time >= '2023-07-16')
|
||||||
|
|
||||||
|
counts = {}
|
||||||
|
last_swipes = {}
|
||||||
|
for swipe in swipes:
|
||||||
|
if swipe.uuid not in counts:
|
||||||
|
counts[swipe.uuid] = 0
|
||||||
|
counts[swipe.uuid] += 1
|
||||||
|
last_swipes[swipe.uuid] = swipe
|
||||||
|
|
||||||
|
res = []
|
||||||
|
for uuid, count in counts.items():
|
||||||
|
# User already signed out
|
||||||
|
if count % 2 == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
swipe = last_swipes[uuid]
|
||||||
|
|
||||||
|
dt = swipe.time
|
||||||
|
tz = timezone('EST')
|
||||||
|
est = dt.astimezone(tz)
|
||||||
|
|
||||||
|
user = db.session.query(User).filter(User.uuid == swipe.uuid).first()
|
||||||
|
|
||||||
|
time_here = datetime.now() - dt
|
||||||
|
time_here_str = f'{time_here.seconds//3600}:{(time_here.seconds//60)%60:02d}'
|
||||||
|
|
||||||
|
rec = {
|
||||||
|
'first_name': user.first_name,
|
||||||
|
'last_name': user.last_name,
|
||||||
|
'time_here': time_here_str
|
||||||
|
}
|
||||||
|
res.append(rec)
|
||||||
|
return json.dumps(res)
|
||||||
|
|
||||||
|
@app.route('/report', methods=['GET', 'POST'])
|
||||||
|
def report():
|
||||||
|
after_date = '2024-08-22'
|
||||||
|
if request.method == 'POST':
|
||||||
|
after_date = request.form['after_date']
|
||||||
|
days = {}
|
||||||
|
swipes = db.session.query(Swipe).filter(Swipe.time >= after_date)
|
||||||
|
for swipe in swipes:
|
||||||
|
dt = swipe.time
|
||||||
|
tz = timezone('EST')
|
||||||
|
est = dt.astimezone(tz)
|
||||||
|
date = est.date()
|
||||||
|
|
||||||
|
if date not in days:
|
||||||
|
days[date] = {}
|
||||||
|
name_dict = days[date]
|
||||||
|
|
||||||
|
if swipe.uuid not in name_dict:
|
||||||
|
name_dict[swipe.uuid] = []
|
||||||
|
name_list = name_dict[swipe.uuid]
|
||||||
|
|
||||||
|
name_list.append(swipe.time)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
result = f'<form action="report" method="POST"><input type="date" name="after_date" value="{after_date}" /><input type="submit" /></form>'
|
||||||
|
result += '<table border=1 style="table-layout:fixed;">'
|
||||||
|
|
||||||
|
name_query = db.session.query(User)
|
||||||
|
names = []
|
||||||
|
result += '<thead><tr><th></th>'
|
||||||
|
for name_obj in name_query:
|
||||||
|
name = f'{name_obj.first_name} {name_obj.last_name}'
|
||||||
|
names.append(name)
|
||||||
|
result += f'<th style="width: 100px; position: sticky; top: 0; background: white;">{name}</th>'
|
||||||
|
result += '</tr></thead>'
|
||||||
|
|
||||||
|
|
||||||
|
totals = {}
|
||||||
|
last_weekday = 0
|
||||||
|
for date, name_dict in days.items():
|
||||||
|
day = date.isoformat()
|
||||||
|
column = {}
|
||||||
|
for uuid, times in name_dict.items():
|
||||||
|
names_query = User.query.filter_by(uuid=uuid).first()
|
||||||
|
if names_query:
|
||||||
|
name = f'{names_query.first_name} {names_query.last_name}'
|
||||||
|
else:
|
||||||
|
name = f'Unknown: {uuid}'
|
||||||
|
|
||||||
|
# result += f'{swipe.time} | WHO DIS {swipe.uuid}<br>'
|
||||||
|
# result += f'{day} {uuid}: {times}<br>'
|
||||||
|
if len(times) % 2 == 1:
|
||||||
|
# result += f'{day} | {name} uneven scans<br>'
|
||||||
|
column[name] = 'X'
|
||||||
|
continue
|
||||||
|
total_hours = 0.0
|
||||||
|
for i in range(len(times) // 2):
|
||||||
|
in_ = times[i * 2 + 0]
|
||||||
|
out = times[i * 2 + 1]
|
||||||
|
dt = out - in_
|
||||||
|
total_hours += dt.seconds / 60 / 60
|
||||||
|
if name not in totals:
|
||||||
|
totals[name] = 0.0
|
||||||
|
totals[name] += total_hours
|
||||||
|
# result += f'{day} | {name}: {total_hours:0.2f}<br>'
|
||||||
|
column[name] = f'{total_hours:0.2f}'
|
||||||
|
if date.weekday() < last_weekday:
|
||||||
|
result += f'<tr><td colspan="{len(names)+1}">'
|
||||||
|
result += '<hr style="margin: 0; border: none; background: black; height: 10px" />'
|
||||||
|
result += '</td></tr>'
|
||||||
|
last_weekday = date.weekday()
|
||||||
|
|
||||||
|
result += f'<tr><td style="white-space: nowrap;">{day}</td>'
|
||||||
|
for name in names:
|
||||||
|
if name in column:
|
||||||
|
result += f'<td>{column[name]}</td>'
|
||||||
|
else:
|
||||||
|
result += '<td></td>'
|
||||||
|
result += '</tr>'
|
||||||
|
|
||||||
|
result += '<tr><td>Totals</td>'
|
||||||
|
for name_obj in name_query:
|
||||||
|
name = f'{name_obj.first_name} {name_obj.last_name}'
|
||||||
|
if name in totals:
|
||||||
|
total = totals[name]
|
||||||
|
else:
|
||||||
|
total = 0.0
|
||||||
|
result += f'<td>{total:0.2f}</td>'
|
||||||
|
result += '</tr>'
|
||||||
|
result += '</table>'
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
2
env
Normal file
2
env
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export FLASK_APP=main.py
|
||||||
|
export FLASK_ENV=development
|
5
main.py
Normal file
5
main.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from app import app
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', port=5000)
|
||||||
|
|
5
requirements.txt
Normal file
5
requirements.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Flask
|
||||||
|
flask-sqlalchemy
|
||||||
|
flask-login
|
||||||
|
pymysql
|
||||||
|
pytz
|
Loading…
Reference in a new issue