2025-05-19 20:17:55 +03:00
|
|
|
from flask import Flask, request
|
2025-05-07 20:19:03 +03:00
|
|
|
import psycopg2 as psql
|
|
|
|
from os import environ as env
|
2025-05-19 19:02:53 +03:00
|
|
|
import datetime
|
2025-05-19 20:17:55 +03:00
|
|
|
import time
|
|
|
|
import threading
|
|
|
|
import uuid
|
2025-05-07 20:19:03 +03:00
|
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
db_params = {
|
|
|
|
'database': env.get('DB_NAME'),
|
|
|
|
'user': env.get('DB_USER'),
|
|
|
|
'password': env.get('DB_PASS'),
|
|
|
|
'host': env.get('DB_HOST'),
|
|
|
|
'port': int(env.get('DB_PORT'))
|
|
|
|
}
|
|
|
|
|
2025-05-25 19:43:00 +03:00
|
|
|
db = None
|
|
|
|
|
|
|
|
for _ in range(5):
|
|
|
|
try:
|
|
|
|
db = psql.connect(**db_params)
|
|
|
|
except Exception as e:
|
|
|
|
print(f"Failed to connect ({e}), retrying...")
|
|
|
|
time.sleep(5)
|
2025-05-07 20:19:03 +03:00
|
|
|
|
2025-05-19 20:17:55 +03:00
|
|
|
class PassQueue:
|
|
|
|
def __init__(self):
|
|
|
|
self.queue = []
|
|
|
|
|
|
|
|
def start(self):
|
|
|
|
while True:
|
|
|
|
if not self.queue:
|
|
|
|
time.sleep(1)
|
|
|
|
continue
|
|
|
|
|
|
|
|
p = self.queue.pop(0)
|
|
|
|
c = db.cursor()
|
|
|
|
|
|
|
|
try:
|
|
|
|
c.execute("BEGIN")
|
2025-05-19 20:36:14 +03:00
|
|
|
c.execute(f"INSERT INTO public.pass (uuid, movie_uuid, user_first, user_last, user_email, pass_type, pass_price, pass_requested_at, payment_received)"
|
|
|
|
f"VALUES ('{uuid.uuid4().hex}', '{p.movie_uuid}', '{p.first}', '{p.last}', '{p.email}', {p.type}, {p.price}, '{time.strftime('%Y-%m-%d %H:%M:%S')}', false)")
|
2025-05-19 20:17:55 +03:00
|
|
|
c.execute("COMMIT")
|
|
|
|
db.commit()
|
|
|
|
except Exception as e:
|
|
|
|
print(e)
|
|
|
|
c.execute("ROLLBACK")
|
|
|
|
db.rollback()
|
|
|
|
|
|
|
|
pq = PassQueue()
|
|
|
|
pq_thread = threading.Thread(target = pq.start, args = [])
|
|
|
|
pq_thread.start()
|
|
|
|
|
2025-05-19 19:02:53 +03:00
|
|
|
@app.route("/movies", methods = ['GET'])
|
2025-05-07 21:03:55 +03:00
|
|
|
def movies():
|
2025-05-07 20:19:03 +03:00
|
|
|
c = db.cursor()
|
2025-05-07 21:03:55 +03:00
|
|
|
|
2025-05-19 19:02:53 +03:00
|
|
|
fields = ['uuid',
|
|
|
|
'name',
|
|
|
|
'scheduled_datetime',
|
|
|
|
'movie_details',
|
|
|
|
'image_url',
|
|
|
|
'max_passes']
|
|
|
|
|
2025-05-07 21:03:55 +03:00
|
|
|
try:
|
2025-05-19 19:02:53 +03:00
|
|
|
c.execute(f"SELECT {','.join(fields)} FROM public.movie")
|
|
|
|
except Exception as e:
|
|
|
|
c.close()
|
|
|
|
print(e)
|
2025-05-07 21:03:55 +03:00
|
|
|
return {
|
|
|
|
"error": "Failed to list movies",
|
|
|
|
"result": None
|
|
|
|
}, 503
|
|
|
|
|
|
|
|
res = c.fetchall()
|
|
|
|
c.close()
|
|
|
|
|
|
|
|
return {
|
|
|
|
"error": "",
|
2025-05-19 19:02:53 +03:00
|
|
|
"result": [{fields[i]: v
|
|
|
|
if type(v) != datetime.datetime
|
|
|
|
else v.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
for i, v in enumerate(r)}
|
|
|
|
for r in res]
|
2025-05-07 21:03:55 +03:00
|
|
|
}
|
|
|
|
|
2025-05-19 19:02:53 +03:00
|
|
|
@app.route("/movie/<movie_uuid>", methods = ['GET'])
|
2025-05-07 21:03:55 +03:00
|
|
|
def movie_byid(movie_uuid):
|
|
|
|
c = db.cursor()
|
|
|
|
|
2025-05-19 19:02:53 +03:00
|
|
|
fields = ['name',
|
|
|
|
'scheduled_datetime',
|
|
|
|
'movie_details',
|
|
|
|
'image_url',
|
|
|
|
'max_passes']
|
|
|
|
|
2025-05-07 20:19:03 +03:00
|
|
|
try:
|
2025-05-19 19:02:53 +03:00
|
|
|
c.execute(f"SELECT {','.join(fields)} FROM public.movie WHERE uuid='{movie_uuid}'")
|
|
|
|
except Exception as e:
|
|
|
|
c.close()
|
|
|
|
print(e)
|
2025-05-07 21:03:55 +03:00
|
|
|
return {
|
|
|
|
"error": "Failed to search for a movie",
|
|
|
|
"result": None
|
|
|
|
}, 503
|
2025-05-07 20:19:03 +03:00
|
|
|
|
2025-05-07 21:03:55 +03:00
|
|
|
res = c.fetchone()
|
2025-05-07 20:19:03 +03:00
|
|
|
c.close()
|
2025-05-07 21:03:55 +03:00
|
|
|
|
|
|
|
if res:
|
|
|
|
return {
|
|
|
|
"error": "",
|
2025-05-19 19:02:53 +03:00
|
|
|
"result": {fields[i]: v
|
|
|
|
if type(v) != datetime.datetime
|
|
|
|
else v.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
for i, v in enumerate(res)}
|
2025-05-07 21:03:55 +03:00
|
|
|
}
|
|
|
|
else:
|
|
|
|
return {
|
|
|
|
"error": f"No movie with uuid={movie_uuid} has been found",
|
|
|
|
"result": None
|
|
|
|
}, 404
|
2025-05-19 19:02:53 +03:00
|
|
|
|
|
|
|
@app.route("/passes", methods = ['GET'])
|
|
|
|
def passes():
|
|
|
|
c = db.cursor()
|
|
|
|
|
|
|
|
fields = ["uuid",
|
|
|
|
"movie_uuid",
|
|
|
|
"user_first",
|
|
|
|
"user_last",
|
|
|
|
"user_email",
|
|
|
|
"pass_type",
|
|
|
|
"pass_price",
|
|
|
|
"pass_requested_at",
|
|
|
|
"payment_received",
|
|
|
|
"payment_received_at"]
|
|
|
|
|
|
|
|
try:
|
|
|
|
c.execute(f"SELECT {','.join(fields)} FROM public.pass")
|
|
|
|
except Exception as e:
|
|
|
|
c.close()
|
|
|
|
print(e)
|
|
|
|
return {
|
|
|
|
"error": "Failed to list out passes",
|
|
|
|
"result": None
|
|
|
|
}
|
|
|
|
|
|
|
|
res = c.fetchall()
|
|
|
|
c.close()
|
|
|
|
|
|
|
|
return {
|
|
|
|
"error": "",
|
|
|
|
"result": [{fields[i]: v
|
|
|
|
if type(v) != datetime.datetime
|
|
|
|
else v.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
for i, v in enumerate(r)}
|
|
|
|
for r in res]}
|
2025-05-19 20:17:55 +03:00
|
|
|
|
|
|
|
class PassValidator:
|
|
|
|
def __init__(self):
|
|
|
|
self.first = None
|
|
|
|
self.last = None
|
|
|
|
self.email = None
|
|
|
|
self.type = None
|
|
|
|
self.price = None
|
|
|
|
self.movie_uuid = None
|
|
|
|
|
|
|
|
def validate(self):
|
|
|
|
if not self.first:
|
|
|
|
return "First name missing"
|
|
|
|
elif not self.last:
|
|
|
|
return "Last name missing"
|
|
|
|
elif not self.email:
|
|
|
|
return "Email missing"
|
|
|
|
elif not self.type:
|
|
|
|
return "Pass type missing"
|
|
|
|
elif not self.price:
|
|
|
|
return "Pass price missing"
|
|
|
|
elif not self.movie_uuid:
|
|
|
|
return "Movie uuid missing"
|
|
|
|
|
|
|
|
def queue(self):
|
|
|
|
pq.queue.append(self)
|
|
|
|
|
|
|
|
def fill_pass_from_form(p, f):
|
|
|
|
print(f)
|
|
|
|
|
|
|
|
def fill_pass_from_json(p, j):
|
|
|
|
fields = ("first",
|
|
|
|
"last",
|
|
|
|
"email",
|
|
|
|
"type",
|
|
|
|
"price",
|
|
|
|
"movie_uuid")
|
|
|
|
|
|
|
|
for i in fields:
|
|
|
|
exec(f"p.{i} = j.get('{i}')")
|
|
|
|
|
|
|
|
@app.route("/apply_for_pass", methods = ['POST'])
|
|
|
|
def apply_for_pass():
|
|
|
|
p = PassValidator()
|
|
|
|
|
|
|
|
if request.form:
|
|
|
|
fill_pass_from_form(p, request.form)
|
|
|
|
elif request.is_json:
|
|
|
|
fill_pass_from_json(p, request.json)
|
|
|
|
else:
|
|
|
|
return {"error": "only form and json interfaces are supported", "result": None}, 403
|
|
|
|
|
|
|
|
v = p.validate()
|
|
|
|
|
|
|
|
if not v:
|
|
|
|
p.queue()
|
|
|
|
return {"error": None, "result": "OK"}
|
|
|
|
else:
|
|
|
|
return {"error": v, "result": None}
|