Compare commits
No commits in common. "main" and "frontend" have entirely different histories.
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
__pycacahe__/*
|
|
||||||
freeriders/__pycacahe__/*
|
|
||||||
instance/
|
|
@ -1,10 +1,3 @@
|
|||||||
# freeriders
|
# freeriders
|
||||||
|
|
||||||
Web app for tracking current ticket numbers
|
Web app for tracking current ticket numbers
|
||||||
|
|
||||||
# Architecture
|
|
||||||
|
|
||||||
- keep all ticket numbers and timestamps in SQL table "tickets"
|
|
||||||
- display last ticket number
|
|
||||||
- don't allow tickets that are out of probable ticket number range
|
|
||||||
- if last submited ticket is timestamped more then 90 minutes ago, try to predict the next number
|
|
||||||
|
@ -4,7 +4,7 @@ Priority: optional
|
|||||||
Architecture: all
|
Architecture: all
|
||||||
Essential: no
|
Essential: no
|
||||||
Installed-Size: 2000
|
Installed-Size: 2000
|
||||||
Depends: gunicorn, python3-flask-sqlalchemy
|
Depends: python3-flask, gunicorn
|
||||||
Homepage: https://gitea.dmz.rs/Decentrala/freeriders
|
Homepage: https://gitea.dmz.rs/Decentrala/freeriders
|
||||||
Maintainer: Decentrala <dmz@dmz.rs>
|
Maintainer: Decentrala <dmz@dmz.rs>
|
||||||
Description: Web app that tracks bus transport ticket numbers
|
Description: Web app that tracks bus transport ticket numbers
|
||||||
|
@ -1,16 +1,5 @@
|
|||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
|
||||||
import os # if you wanna have db credenitalas in os.environ
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
# SQLAlchemy setup
|
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///freeriders.db'
|
|
||||||
|
|
||||||
#MySql setup
|
|
||||||
#app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://user:pass@localhost/dbname'
|
|
||||||
|
|
||||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
|
||||||
db = SQLAlchemy(app)
|
|
||||||
|
|
||||||
from freeriders import routes
|
from freeriders import routes
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import time
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
def datetounix(day, month, year, hour, minute, second):
|
|
||||||
seconds = datetime.datetime(year, month, day, hour, minute, second).timestamp()
|
|
||||||
return int(seconds)
|
|
||||||
|
|
||||||
def formatprefix2(number):
|
|
||||||
return "{:02d}".format(number)
|
|
||||||
|
|
||||||
def formatprefix10(number):
|
|
||||||
return "{:010d}".format(number)
|
|
||||||
|
|
||||||
def predict(timestamp):
|
|
||||||
base_ticket = 7157662
|
|
||||||
base_timestamp = 1695887564
|
|
||||||
step = 0.688
|
|
||||||
|
|
||||||
ticket = base_ticket + (timestamp - base_timestamp) * step
|
|
||||||
return int(ticket)
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
|||||||
from freeriders import db
|
|
||||||
|
|
||||||
class Tickets(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
ticket = db.Column(db.Integer, nullable=False)
|
|
||||||
timestamp = db.Column(db.Integer, nullable=False)
|
|
@ -1,58 +1,10 @@
|
|||||||
from flask import render_template, request, redirect
|
from flask import render_template, request, redirect
|
||||||
from freeriders import app, db
|
from freeriders import app
|
||||||
from freeriders.functions import formatprefix2,formatprefix10, predict
|
|
||||||
from freeriders.models import Tickets
|
|
||||||
from datetime import datetime
|
|
||||||
import time
|
|
||||||
|
|
||||||
@app.route('/sms', methods=['GET'])
|
@app.route('/', methods=['POST', 'GET'])
|
||||||
def sms():
|
def changepassword():
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
timenow = int(time.time())
|
return render_template('index.html')
|
||||||
|
|
||||||
try:
|
|
||||||
lastticket = Tickets.query.order_by(Tickets.timestamp.desc()).first()
|
|
||||||
|
|
||||||
if lastticket.timestamp < timenow - (90 * 60):
|
|
||||||
lastticket = formatprefix10(predict(timenow))
|
|
||||||
else:
|
|
||||||
lastticket = formatprefix10(lastticket.ticket)
|
|
||||||
date = datetime.now()
|
|
||||||
datenow = f'{formatprefix2(date.day)}.{formatprefix2(date.month)}.{date.year}'
|
|
||||||
timenow = f'{formatprefix2(date.hour)}:{formatprefix2(date.minute)}:{formatprefix2(date.second)}'
|
|
||||||
return render_template('sms.html', ticket = lastticket, date = datenow, time = timenow)
|
|
||||||
except:
|
|
||||||
return 'Error retriving last ticket'
|
|
||||||
else:
|
|
||||||
return 'HTTP request method not recogniezed'
|
|
||||||
|
|
||||||
@app.route('/submit', methods=['POST', 'GET'])
|
|
||||||
def submit():
|
|
||||||
PREDICTTIMERANGE = 60 * 60 * 24
|
|
||||||
if request.method == 'GET':
|
|
||||||
return render_template('submit.html')
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
timenow = int(time.time())
|
|
||||||
ticket_input = request.form['ticket']
|
|
||||||
|
|
||||||
if ticket_input.isdigit() and len(ticket_input) == 10 :
|
|
||||||
if int(ticket_input) < predict(timenow + PREDICTTIMERANGE ) and int(ticket_input) > predict(timenow - PREDICTTIMERANGE ) :
|
|
||||||
ticket = Tickets(ticket = int(ticket_input), timestamp = timenow)
|
|
||||||
else:
|
|
||||||
print(int(ticket_input))
|
|
||||||
print(predict(timenow - PREDICTTIMERANGE))
|
|
||||||
print(predict(timenow + PREDICTTIMERANGE))
|
|
||||||
print(predict(timenow))
|
|
||||||
return 'Ticket number is in unexpected range.'
|
|
||||||
else:
|
|
||||||
return 'Ticket format is wrong. Only 10 digits allowed.'
|
|
||||||
|
|
||||||
try:
|
|
||||||
db.session.add(ticket)
|
|
||||||
db.session.commit()
|
|
||||||
return 'Ticket added'
|
|
||||||
except:
|
|
||||||
return 'Adding ticket failed'
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return 'HTTP request method not recogniezed'
|
return 'HTTP request method not recogniezed'
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
:root {
|
:root {
|
||||||
--border-radus: 1rem;
|
--border-radus: 1rem;
|
||||||
--background: #F9F9F9;
|
--background: #EEE;
|
||||||
--header-background: #EEE;
|
--header-background: #DDD;
|
||||||
--header-height: 3rem;
|
--header-height: 3rem;
|
||||||
--input-bar-height: 3rem;
|
--input-bar-height: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
body{
|
body{
|
||||||
@ -14,13 +14,14 @@ body{
|
|||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
max-width: 400px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 2rem 0.5rem;
|
padding: 1rem 0.5rem;
|
||||||
height: calc(100vh - var(--header-height) - var(--input-bar-height));
|
height: calc(100vh - 5rem);
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
@ -29,25 +30,22 @@ header {
|
|||||||
background-color: var(--header-background);
|
background-color: var(--header-background);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
padding: 1rem;
|
||||||
padding: 0 1rem;
|
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: var(--header-height);
|
height: var(header-height);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: var(--input-bar-height);
|
height: var(input-bar-height);
|
||||||
background-color: var(--background);
|
background-color: #FDD;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
gap: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.request {
|
.request {
|
||||||
@ -70,11 +68,3 @@ footer {
|
|||||||
align-self: center;
|
align-self: center;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textbox{
|
|
||||||
background-color: var(--header-background);
|
|
||||||
width: 80%;
|
|
||||||
height: 1rem;
|
|
||||||
padding: 0.2rem;
|
|
||||||
border-radius: 2rem;
|
|
||||||
}
|
|
14
freeriders/templates/index.html
Normal file
14
freeriders/templates/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="/styles/style.css">
|
||||||
|
<link rel="shortcut icon" href="/img/favicon.ico" type="image/x-icon">
|
||||||
|
<title>Ticket tracking</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Karta</title>
|
<title>Karta</title>
|
||||||
<link rel="stylesheet" href="/static/style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
@ -20,8 +20,35 @@
|
|||||||
U Beogradu, za broj telefona ste kupili
|
U Beogradu, za broj telefona ste kupili
|
||||||
VREMENSKU KARTU OD 90 MINUTA U ZONI A po
|
VREMENSKU KARTU OD 90 MINUTA U ZONI A po
|
||||||
ceni od 50 din + osnovna cena poruke,
|
ceni od 50 din + osnovna cena poruke,
|
||||||
koja vazi do {{ date }}, {{ time }}.
|
koja vazi do 03.10.2023, 09:42:54.
|
||||||
Karta broj: {{ ticket }}.
|
Karta broj: 0000343232.
|
||||||
|
Placanjem operateru izmirujete dugovanja
|
||||||
|
za ovu kartu prema JKP Naplata prevozne
|
||||||
|
usluge Beograd. Sacuvajte ovu poruku.
|
||||||
|
</section>
|
||||||
|
<section class='request'>
|
||||||
|
A90
|
||||||
|
</section>
|
||||||
|
<section class='ticket'>
|
||||||
|
U Beogradu, za broj telefona ste kupili
|
||||||
|
VREMENSKU KARTU OD 90 MINUTA U ZONI A po
|
||||||
|
ceni od 50 din + osnovna cena poruke,
|
||||||
|
koja vazi do 03.10.2023, 09:42:54.
|
||||||
|
Karta broj: 0000343232.
|
||||||
|
Placanjem operateru izmirujete dugovanja
|
||||||
|
za ovu kartu prema JKP Naplata prevozne
|
||||||
|
usluge Beograd. Sacuvajte ovu poruku.
|
||||||
|
</section>
|
||||||
|
<section class='date'>08:23</section>
|
||||||
|
<section class='request'>
|
||||||
|
A90
|
||||||
|
</section>
|
||||||
|
<section class='ticket'>
|
||||||
|
U Beogradu, za broj telefona ste kupili
|
||||||
|
VREMENSKU KARTU OD 90 MINUTA U ZONI A po
|
||||||
|
ceni od 50 din + osnovna cena poruke,
|
||||||
|
koja vazi do 03.10.2023, 09:42:54.
|
||||||
|
Karta broj: 0000343232.
|
||||||
Placanjem operateru izmirujete dugovanja
|
Placanjem operateru izmirujete dugovanja
|
||||||
za ovu kartu prema JKP Naplata prevozne
|
za ovu kartu prema JKP Naplata prevozne
|
||||||
usluge Beograd. Sacuvajte ovu poruku.
|
usluge Beograd. Sacuvajte ovu poruku.
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>Submit</title>
|
|
||||||
<link rel="stylesheet" href="/static/style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<form action="/submit" method="POST">
|
|
||||||
<label for="ticket">ticket</label>
|
|
||||||
<input type="text" name="ticket" id="ticket" placeholder="1234567890" required>
|
|
||||||
<button> Submit </button>
|
|
||||||
</form>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
10
init_db.py
10
init_db.py
@ -1,10 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
from freeriders import db
|
|
||||||
|
|
||||||
print('[i] Trying to create databse...')
|
|
||||||
try:
|
|
||||||
db.create_all()
|
|
||||||
print('[+] Success you can proceed with deployment!')
|
|
||||||
except:
|
|
||||||
print('[-] Creating db failed :/')
|
|
||||||
|
|
@ -1,2 +1 @@
|
|||||||
flask
|
flask
|
||||||
SQLAlchemy
|
|
||||||
|
Loading…
Reference in New Issue
Block a user