You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
9.3 KiB
298 lines
9.3 KiB
from flask import Flask, make_response, request, current_app, abort |
|
from simplejson import dumps |
|
from pymongo import MongoClient, DESCENDING # ASCENDING |
|
import datetime |
|
import dateutil.parser |
|
import bson |
|
from settings import mongo_config |
|
from datetime import timedelta |
|
from functools import update_wrapper |
|
|
|
|
|
def crossdomain(origin=None, methods=None, headers=None, |
|
max_age=21600, attach_to_all=True, |
|
automatic_options=True): |
|
if methods is not None: |
|
methods = ', '.join(sorted(x.upper() for x in methods)) |
|
if headers is not None and not isinstance(headers, basestring): |
|
headers = ', '.join(x.upper() for x in headers) |
|
if not isinstance(origin, basestring): |
|
origin = ', '.join(origin) |
|
if isinstance(max_age, timedelta): |
|
max_age = max_age.total_seconds() |
|
|
|
def get_methods(): |
|
if methods is not None: |
|
return methods |
|
|
|
options_resp = current_app.make_default_options_response() |
|
return options_resp.headers['allow'] |
|
|
|
def decorator(f): |
|
def wrapped_function(*args, **kwargs): |
|
if automatic_options and request.method == 'OPTIONS': |
|
resp = current_app.make_default_options_response() |
|
else: |
|
resp = make_response(f(*args, **kwargs)) |
|
if not attach_to_all and request.method != 'OPTIONS': |
|
return resp |
|
|
|
h = resp.headers |
|
|
|
h['Access-Control-Allow-Origin'] = origin |
|
h['Access-Control-Allow-Methods'] = get_methods() |
|
h['Access-Control-Max-Age'] = str(max_age) |
|
if headers is not None: |
|
h['Access-Control-Allow-Headers'] = headers |
|
return resp |
|
|
|
f.provide_automatic_options = False |
|
return update_wrapper(wrapped_function, f) |
|
return decorator |
|
|
|
|
|
app = Flask(__name__) |
|
client = MongoClient(mongo_config) |
|
db = client.showtimes |
|
|
|
miscObjHandler = lambda obj: ( |
|
obj.isoformat() if isinstance(obj, datetime.datetime) |
|
or isinstance(obj, datetime.date) |
|
else str(obj) if isinstance(obj, bson.objectid.ObjectId) else None) |
|
|
|
|
|
@app.route('/flask/') |
|
@crossdomain(origin='*') |
|
def hello_world(): |
|
return 'This comes from Flask ^_^' |
|
|
|
|
|
@app.route('/movies/', methods=['GET'], defaults={'lang': 'en'}) |
|
@app.route('/movies/<lang>/', methods=['GET']) |
|
@crossdomain(origin='*') |
|
def movie_list(lang): |
|
result = db.movies.find().sort('release_date', DESCENDING) |
|
movies = [] |
|
for i in result: |
|
if 'original' in i['title']: |
|
i['original_title'] = i['title']['original'] |
|
i['title'] = i['title'][lang] |
|
i['cast'] = i['cast'][lang] |
|
i['tagline'] = i['tagline'][lang] |
|
i['storyline'] = i['storyline'][lang] |
|
i['director'] = i['director'][lang] |
|
movies.append(i) |
|
|
|
json_dict = { |
|
'meta': { |
|
'total_count': len(movies) |
|
}, |
|
'objects': movies |
|
} |
|
r = make_response(dumps(json_dict, default=miscObjHandler)) |
|
r.mimetype = 'application/json' |
|
return r |
|
|
|
|
|
@app.route('/movie/<mid>/<lang>/', methods=['GET']) |
|
@crossdomain(origin='*') |
|
def movie_item(mid, lang): |
|
''' |
|
Compatibility with #ShowtimesTH on Android as of v1.4 |
|
''' |
|
lang = lang if lang in ('en', 'th') else 'en' |
|
try: |
|
movie = db.movies.find_one({'tmdb_id': int(mid)}) |
|
except ValueError: |
|
movie = db.movies.find_one({'_id': bson.objectid.ObjectId(mid)}) |
|
|
|
if not movie: |
|
movie = {} |
|
else: |
|
for k in ('title', 'tagline', 'director', 'cast', 'storyline'): |
|
if movie[k][lang]: |
|
movie[k] = movie[k][lang] |
|
else: |
|
movie[k] = movie[k]['th' if lang == 'en' else lang] |
|
try: |
|
del movie['indexes'] |
|
except KeyError: |
|
pass |
|
r = make_response(dumps(movie, default=miscObjHandler)) |
|
r.mimetype = 'application/json' |
|
return r |
|
|
|
|
|
@app.route('/movie/<mid>/', methods=['GET', 'POST']) |
|
def raw_movie_item(mid): |
|
if request.method == 'GET': |
|
try: |
|
movie = db.movies.find_one({'tmdb_id': int(mid)}) |
|
except ValueError: |
|
movie = db.movies.find_one({'_id': bson.objectid.ObjectId(mid)}) |
|
|
|
r = make_response(dumps(movie, default=miscObjHandler)) |
|
r.mimetype = 'application/json' |
|
return r |
|
|
|
## This is POST -- updating with information provided |
|
if not request.json: |
|
abort(400) |
|
try: |
|
key = {'_id': bson.objectid.ObjectId(mid)} |
|
prev = db.movies.find_one(key) |
|
if not prev: |
|
abort(500) |
|
is_allowed = True |
|
## simple check if dict is smaller, then we don't update that |
|
posting_keys = request.json.keys() |
|
prev_keys = prev.keys() |
|
missing_fields = set(prev_keys) - set(posting_keys) |
|
is_allowed = False if missing_fields else True |
|
if is_allowed: |
|
result = db.movies.update(key, {'$set': request.json}, False) |
|
else: |
|
result = { # mimicking mongo update result for consistency |
|
'updatedExisting': False, 'ok': 0, 'nModified': 0, |
|
'error': "missing_fields:%s" % (','.join(missing_fields)), |
|
} |
|
r = make_response(dumps(result, default=miscObjHandler)) |
|
r.mimetype = 'application/json' |
|
return r |
|
except bson.errors.InvalidId: |
|
abort(500) |
|
|
|
|
|
@app.route('/groups/', methods=['GET']) |
|
@crossdomain(origin='*') |
|
def groups(): |
|
result = db.groups.find() |
|
known_groups = [] # ['sf', 'major', 'etc'] |
|
if result.count() == 0: |
|
for i in ('sf', 'major', 'etc'): |
|
db.groups.insert({'name': str(i)}) |
|
known_groups.append({'name': str(i)}) |
|
for i in result: |
|
known_groups.append(i) |
|
|
|
json_dict = { |
|
'meta': { |
|
'total_count': len(known_groups) |
|
}, |
|
'objects': known_groups |
|
} |
|
r = make_response(dumps(json_dict, default=miscObjHandler)) |
|
r.mimetype = 'application/json' |
|
return r |
|
|
|
|
|
@app.route('/group/<string:group_id>/', methods=['GET', 'POST']) |
|
@crossdomain(origin='*') |
|
def group(group_id): |
|
if request.method == 'GET': |
|
result = db.groups.find_one({'_id': bson.objectid.ObjectId(group_id)}) |
|
r = make_response(dumps(result, default=miscObjHandler)) |
|
r.mimetype = 'application/json' |
|
return r |
|
|
|
## This is POST -- updating with information provided |
|
if not request.json or not 'name' in request.json: |
|
abort(400) |
|
try: |
|
key = {'_id': bson.objectid.ObjectId(group_id)} |
|
result = db.groups.update(key, {'$set': request.json}, False) |
|
r = make_response(dumps(result, default=miscObjHandler)) |
|
r.mimetype = 'application/json' |
|
return r |
|
except bson.errors.InvalidId: |
|
abort(500) |
|
|
|
|
|
@app.route('/theaters/', methods=['GET']) |
|
@app.route('/theaters/<group>/', methods=['GET']) |
|
@crossdomain(origin='*') |
|
def list_theaters(group=None): |
|
if not group: |
|
result = db.theater.find() |
|
else: |
|
result = db.theater.find({'group': group}) |
|
items = [i for i in result] |
|
json_dict = { |
|
'meta': { |
|
'total_count': len(items) |
|
}, |
|
'objects': items |
|
} |
|
r = make_response(dumps(json_dict, default=miscObjHandler)) |
|
r.mimetype = 'application/json' |
|
return r |
|
|
|
|
|
@app.route('/theater/<string:theater_id>/', methods=['GET', 'POST']) |
|
@crossdomain(origin='*') |
|
def get_theater(theater_id): |
|
''' |
|
TODO: cannot add theater yet |
|
''' |
|
if request.method == 'GET': |
|
result = db.theater.find_one({'_id': bson.objectid.ObjectId(theater_id)}) |
|
r = make_response(dumps(result, default=miscObjHandler)) |
|
r.mimetype = 'application/json' |
|
return r |
|
|
|
## This is POST -- updating with information provided |
|
if not request.json or \ |
|
not 'name' in request.json or \ |
|
not 'group' in request.json or not 'code' in request.json: |
|
abort(400) |
|
try: |
|
key = {'_id': bson.objectid.ObjectId(theater_id)} |
|
prev = db.theater.find_one(key) |
|
if not prev: |
|
abort(500) |
|
is_allowed = True |
|
for k in ['code', 'group', 'name']: |
|
if request.json[k] != prev[k]: |
|
is_allowed = False |
|
if is_allowed: |
|
result = db.theater.update(key, {'$set': request.json}, False) |
|
else: |
|
result = { # mimicking mongo update result for consistency |
|
'updatedExisting': False, 'ok': 0, 'nModified': 0, |
|
'error': "cannot alter mandatory fields", |
|
} |
|
r = make_response(dumps(result, default=miscObjHandler)) |
|
r.mimetype = 'application/json' |
|
return r |
|
except bson.errors.InvalidId: |
|
abort(500) |
|
|
|
|
|
@app.route('/showtimes/<group>/', methods=['GET']) |
|
@app.route('/showtimes/<group>/<code>/', methods=['GET']) |
|
@crossdomain(origin='*') |
|
def list_showtimes(group=None, code=None): |
|
day = request.args.get('d', '') |
|
q = {} |
|
if day: |
|
q['date'] = dateutil.parser.parse(day) |
|
if group: |
|
q['group'] = group |
|
if code: |
|
q['theater'] = code |
|
|
|
result = db.showtimes.find(q).sort('date', DESCENDING) |
|
items = [i for i in result[:300]] |
|
json_dict = { |
|
'meta': { |
|
'total_count': len(items) |
|
}, |
|
'objects': items |
|
} |
|
r = make_response(dumps(json_dict, default=miscObjHandler)) |
|
r.mimetype = 'application/json' |
|
return r |
|
|
|
|
|
if __name__ == '__main__': |
|
app.run(debug=True)
|
|
|