Advertisement
Guest User

Untitled

a guest
Jan 13th, 2019
122
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 23.60 KB | None | 0 0
  1. import datetime
  2. import decimal
  3. import json
  4.  
  5. import database_connection
  6. import flask
  7. from flask import request, jsonify, Response
  8. from flask_cors import CORS
  9. from sqlalchemy.exc import IntegrityError
  10.  
  11. dao = database_connection.DataAccessObject()
  12.  
  13. app = flask.Flask(__name__)
  14. cors = CORS(app, resources={r"/*": {"origins": "*"}})
  15. app.config["DEBUG"] = True
  16.  
  17.  
  18. def dict_factory(cursor, row):
  19. d = {}
  20. for idx, col in enumerate(cursor.description):
  21. d[col[0]] = row[idx]
  22. return d
  23.  
  24.  
  25. def alchemy_encoder(obj):
  26. """JSON encoder function for SQLAlchemy special classes."""
  27. if isinstance(obj, datetime.date):
  28. return obj.isoformat()
  29. elif isinstance(obj, decimal.Decimal):
  30. return float(obj)
  31.  
  32.  
  33. class InvalidUsage(Exception):
  34. status_code = 400
  35.  
  36. def __init__(self, message, status_code=None, payload=None):
  37. Exception.__init__(self)
  38. self.message = message
  39. if status_code is not None:
  40. self.status_code = status_code
  41. self.payload = payload
  42.  
  43. def to_dict(self):
  44. rv = dict(self.payload or ())
  45. rv['message'] = self.message
  46. return rv
  47.  
  48.  
  49. @app.errorhandler(InvalidUsage)
  50. def handle_invalid_usage(error):
  51. response = jsonify(error.to_dict())
  52. response.status_code = error.status_code
  53. return response
  54.  
  55.  
  56. @app.route('/', methods=['GET'])
  57. def home():
  58. return '''<h1>RecipeX API</h1>
  59. <p>Merry Christmas everybody!</p>'''
  60.  
  61.  
  62. @app.route('/api/v1/all', methods=['GET'])
  63. def api_users():
  64. all_users = json.dumps(dao.json_query('SELECT * FROM users;'), default=alchemy_encoder)
  65. return all_users
  66.  
  67.  
  68. # Pobranie informacji o sesji
  69. @app.route('/api/v1/session-info', methods=['GET'])
  70. def api_session_info():
  71. session_key = request.args.get('session_key')
  72. result = dao.json_query("select * from sessions where session_key = {}".format(session_key))
  73. if len(result) == 0:
  74. return Response("", status=404, mimetype="application/json")
  75. return Response(json.dumps(result[0], default=alchemy_encoder), mimetype="application/json")
  76.  
  77.  
  78. @app.route('/api/v1/user/add', methods=['POST'])
  79. def api_register():
  80. params = request.get_json()
  81. login = params['login']
  82. password = params['password']
  83. if 'email' in params:
  84. email = params['email']
  85. if len(email) > 0:
  86. query = "select * from users where email = '{email}'".format(email=email)
  87. result = dao.json_query(query)
  88. if len(result) > 0:
  89. return Response(json.dumps({'email': True}), status=409, mimetype='application/json')
  90. else:
  91. email = ''
  92. if 'role' in params:
  93. role = params['role']
  94. else:
  95. role = 'user'
  96.  
  97. query = "select * from users where login = '{login}'".format(login=login)
  98. result = dao.json_query(query)
  99. if len(result) > 0:
  100. return Response(json.dumps({'login': True}), status=409, mimetype='application/json')
  101. query = """insert into users(login,email,password,role)
  102. values('{login}','{email}','{password}','{role}')
  103. """.format(login=login, email=email, password=password, role=role)
  104. dao.run_query(query)
  105. return api_login()
  106.  
  107.  
  108. @app.route('/api/v1/login', methods=['POST', 'GET'])
  109. def api_login():
  110. params = request.get_json()
  111. login = params['login']
  112. password = params['password']
  113. users = dao.json_query(
  114. "SELECT * FROM USERS where login = '{}' and password = '{}'".format(login, password)
  115. )
  116. if len(users) > 0:
  117. user = users[0]
  118. user.pop('password', None)
  119. query = """insert into sessions(user_id, expires_at)
  120. values({id}, now() + interval '7 days') returning session_key, expires_at
  121. """.format(id=user['id'])
  122. session = dao.json_query(query)[0]
  123. return Response(json.dumps(session, default=alchemy_encoder), mimetype='application/json')
  124. else:
  125. print('empty')
  126. raise InvalidUsage('Zły login i/ lub hasło', status_code=403)
  127.  
  128.  
  129. # Wylogowanie - usunięcie sesji
  130. @app.route('/api/v1/logout', methods=['POST', 'GET'])
  131. def api_logout():
  132. headers = request.headers
  133. if 'Authorization' in headers:
  134. session_key = headers['Authorization']
  135. dao.json_query("DELETE FROM sessions WHERE session_key = {}".format(session_key))
  136. return Response("", status=204)
  137. else:
  138. raise InvalidUsage("", status_code=404)
  139.  
  140.  
  141. # wyciągnięcie informacji o użytkowniku sesji
  142. @app.route('/api/v1/user-info', methods=['GET'])
  143. def current_user_info():
  144. user_id = get_current_session_user()[0]
  145. if user_id:
  146. user = dao.json_query("select login, email from users where id = {user_id};".format(user_id=user_id))
  147. if user:
  148. user = user[0]
  149. rec_rec = dao.json_query(
  150. "select recipe_id, rank_position from recommendations where user_id = {}".format(user_id)
  151. )
  152. new_rec = []
  153. for row in rec_rec:
  154. recipe = Recipe(row['recipe_id'])
  155. rec_data = json.loads(recipe.get_short_data())
  156. row.pop('recipe_id')
  157. row = {**row, **rec_data}
  158. new_rec.append(row)
  159. rec_rec = new_rec
  160. user['recommended_recipes'] = rec_rec
  161. pref = dao.json_query(
  162. """select b.* from preferences a join products b on a.product_id = b.id
  163. where a.user_id = {} and rating;""".format(user_id)
  164. )
  165. user['preferred_products'] = [row for row in pref]
  166. not_pref = dao.json_query(
  167. """select b.* from preferences a join products b on a.product_id = b.id
  168. where a.user_id = {} and not rating;""".format(user_id)
  169. )
  170. user['not_preferred_products'] = [row for row in not_pref]
  171. return Response(json.dumps(user, default=alchemy_encoder), mimetype='application/json')
  172. else:
  173. raise InvalidUsage('Brak użytkownika', status_code=404)
  174.  
  175.  
  176. # Zmiana hasla
  177. @app.route('/api/v1/password_change/', methods=['PUT', 'GET'])
  178. def api_pass_change():
  179. user_id, msg = get_current_session_user()
  180. if user_id is None or msg != "session_running":
  181. # niezalogowany użytkownik
  182. raise InvalidUsage("", status_code=401)
  183. current_password = request.form['currentPassword']
  184. new_password = request.form['newPassword']
  185. try:
  186. update = dao.json_query(
  187. "update users set password = '{}' where id = {} and password = '{}' returning id".format(
  188. new_password, user_id, current_password
  189. )
  190. )
  191. if update:
  192. return '', 204
  193. else:
  194. raise InvalidUsage('Nieprawidlowe haslo', 401)
  195. except IntegrityError:
  196. raise InvalidUsage('Nowe haslo jest nieprawidlowe', 405)
  197.  
  198.  
  199. @app.route('/api/v1/category/add', methods=["POST"])
  200. def api_add_category():
  201. user_id, msg = get_current_session_user()
  202. if not user_id or msg != "session_running":
  203. # niezalogowany użytkownik
  204. raise InvalidUsage("", status_code=401)
  205. user_role_result = dao.json_query("select role from users where id = {}".format(user_id))
  206. if len(user_role_result) == 0 or user_role_result[0]['role'] != 'admin':
  207. # brak użytkownika lub użytkownik nie jest adminem
  208. raise InvalidUsage("", status_code=403)
  209. name = request.form['name']
  210. _id = request.form['id']
  211. cats_with_id = dao.json_query("select name from categories where id = '{}'".format(_id))
  212. if len(cats_with_id) > 0:
  213. # jest już kategoria z takim id
  214. return Response(json.dumps({'id': True}), status=409)
  215. result = dao.json_query("INSERT INTO categories(id, name) VALUES('{}', '{}') returning *".format(_id, name))
  216.  
  217. return Response(json.dumps(result[0]), mimetype='application/json')
  218.  
  219.  
  220. @app.route('/api/v1/product/all', methods=["GET"])
  221. def api_get_products():
  222. result = dao.json_query("select * from products")
  223. return Response(json.dumps(result), mimetype='application/json')
  224.  
  225.  
  226. @app.route('/api/v1/product/add', methods=["POST"])
  227. def api_add_product():
  228. user_id, msg = get_current_session_user()
  229. if not user_id or msg != "session_running":
  230. # niezalogowany użytkownik
  231. raise InvalidUsage("", status_code=401)
  232. user_role_result = dao.json_query("select role from users where id = {}".format(user_id))
  233. if len(user_role_result) == 0 or user_role_result[0]['role'] not in ['moderator', 'admin']:
  234. # brak użytkownika lub użytkownik nie jest adminem ani moderatorem
  235. raise InvalidUsage("", status_code=403)
  236. name = request.form['name']
  237. measurement = request.form['measurement']
  238. result = dao.json_query(
  239. "INSERT INTO products(id, name, measurement) VALUES(default, '{}', '{}') returning *".format(name, measurement))
  240. return Response(json.dumps(result[0]), mimetype='application/json')
  241.  
  242.  
  243. @app.route('/api/v1/product/edit/', methods=["POST"])
  244. def api_edit_product():
  245. user_id, msg = get_current_session_user()
  246. if not user_id or msg != "session_running":
  247. # niezalogowany użytkownik
  248. raise InvalidUsage("", status_code=401)
  249. user_role_result = dao.json_query("select role from users where id = {}".format(user_id))
  250. if len(user_role_result) == 0 or user_role_result[0]['role'] not in ['moderator', 'admin']:
  251. # brak użytkownika lub użytkownik nie jest adminem ani moderatorem
  252. raise InvalidUsage("", status_code=403)
  253. product_id = request.args.get('id')
  254. product_result = dao.json_query("select * from products where id = {}".format(product_id))
  255. if len(product_result) == 0:
  256. # nie ma takiego produktu
  257. raise InvalidUsage(json.dumps({'id': True}), status_code=404)
  258. name = product_result[0]['name']
  259. measurement = product_result[0]['measurement']
  260. if 'name' in request.form:
  261. name = request.form['name']
  262. if 'measurement' in request.form:
  263. measurement = request.form['measurement']
  264. result = dao.json_query(
  265. "update products set name = '{}', measurement = '{}' where id = {} returning *".format(name, measurement,
  266. product_id))
  267. return Response(json.dumps(result[0]), mimetype='application/json')
  268.  
  269.  
  270. @app.route('/api/v1/recipe/', methods=['GET'])
  271. def recipe_data():
  272. recipe_id = request.args.get('id')
  273. if recipe_id:
  274. recipe_instance = Recipe(recipe_id)
  275. return Response(recipe_instance.get_data(), mimetype='application/json')
  276. else:
  277. raise InvalidUsage('Nie znaleziono przepisu', 404)
  278.  
  279.  
  280. class Recipe:
  281.  
  282. def __init__(self, recipe_id):
  283. self.id = recipe_id
  284.  
  285. def get_short_data(self):
  286. recipes = dao.json_query("""select id,title,description,image,duration,portions,difficulty_level
  287. from recipes where id = {}""".format(self.id))
  288. if len(recipes) > 0:
  289. recipe = recipes[0]
  290. recipe['rating'] = self.get_recipe_rating()
  291. return json.dumps(recipe, default=alchemy_encoder)
  292. else:
  293. raise InvalidUsage('Nie znaleziono przepisu', 404)
  294.  
  295. def get_short_data_params(self, sort_by, items):
  296. if sort_by == "rating-down" or sort_by == "rating-up":
  297. basic_query = """select a.*, b.rating from
  298. (select id,title,description,image,duration,portions,difficulty_level
  299. from recipes where id in ({})) a
  300. join
  301. (select sum(rating)/count(*) as rating, recipe_id
  302. from rated where recipe_id in ({}) group by recipe_id) b
  303. on a.id = b.recipe_id order by rating
  304. """.format(self.id, self.id)
  305. if sort_by == "rating-down":
  306. basic_query = basic_query + " desc"
  307. else:
  308. order_query = ""
  309. if sort_by == 'duration-down':
  310. order_query = ' order by duration desc'
  311. elif sort_by == 'duration-up':
  312. order_query = ' order by duration'
  313. elif sort_by == 'difficulty_level_down':
  314. order_query = ' order by difficulty_level desc'
  315. elif sort_by == 'difficulty_level_up':
  316. order_query = ' order by difficulty_level'
  317. elif sort_by == 'created_at_down':
  318. order_query = ' order by created_at desc'
  319. elif sort_by == 'created_at_up':
  320. order_query = ' order by created_at'
  321. basic_query = """select id,title,description,image,duration,portions,difficulty_level
  322. from recipes where id in ({})""".format(
  323. self.id) + order_query
  324. if items:
  325. query = basic_query + " limit {items}".format(items=items)
  326. else:
  327. query = basic_query
  328. recipes = dao.json_query(query)
  329. if len(recipes) == 1:
  330. recipe = recipes[0]
  331. recipe['rating'] = self.get_recipe_rating()
  332. return json.dumps(recipe, default=alchemy_encoder)
  333. elif len(recipes) > 1:
  334. new_recipes = []
  335. for recipe in recipes:
  336. recipe['rating'] = self.get_recipe_rating()
  337. new_recipes.append(recipe)
  338. recipes = new_recipes
  339. return json.dumps(recipes, default=alchemy_encoder)
  340. else:
  341. raise InvalidUsage('Nie znaleziono przepisu', 404)
  342.  
  343. def get_data(self):
  344. recipes = dao.json_query("select * from recipes where id = {}".format(self.id))
  345. if len(recipes) > 0:
  346. recipe = recipes[0]
  347. recipe.update({'creator': self.get_recipe_creator(recipe), 'categories': self.get_recipe_categories(),
  348. 'devices': self.get_recipe_devices(), 'ingredients': self.get_recipe_ingredients(),
  349. 'rating': self.get_recipe_rating(), 'comments': self.get_recipe_comments()})
  350. return json.dumps(recipe, default=alchemy_encoder)
  351. else:
  352. raise InvalidUsage('Nie znaleziono przepisu', 404)
  353.  
  354. # @staticmethod
  355. def get_recipe_creator(self, recipe):
  356. creator_id = recipe.pop('creator_id')
  357. creator = dao.json_query("select id,login from users where id = {}".format(creator_id))
  358. if len(creator) > 0:
  359. creator = creator[0]
  360. return creator
  361.  
  362. def get_recipe_categories(self):
  363. categories = dao.json_query(
  364. """with cte as (select category_id from recipe_category where recipe_id in ({}))
  365. select * from categories where id in (select category_id from cte)""".format(
  366. self.id))
  367. return categories
  368.  
  369. def get_recipe_devices(self):
  370. devices = dao.json_query(
  371. """with cte as (select device_id from devices_to_use where recipe_id in ({}))
  372. select * from devices where id in (select device_id from cte)""".format(
  373. self.id))
  374. return devices
  375.  
  376. def get_recipe_ingredients(self):
  377. ingredients = dao.json_query(
  378. "select * from products a join ingredients b on a.id = b.product_id where b.recipe_id in ({})".format(
  379. self.id))
  380. return ingredients
  381.  
  382. # def get_recipe_main_rating(self):
  383. # ratings = dao.json_query(
  384. # """select (sum(rating)/count(*) as overall
  385. # from rated where recipe_id in ({})""".format(
  386. # self.id))
  387. # if len(ratings) > 0:
  388. # rating = ratings[0]
  389. # return rating
  390.  
  391. def get_recipe_rating(self):
  392. ratings = dao.json_query(
  393. """select count(*) as quantity, round(sum(rating)/count(*),2) as overall
  394. from rated where recipe_id in ({})""".format(
  395. self.id))
  396. if len(ratings) > 0:
  397. rating = ratings[0]
  398. user_id = get_current_session_user()[0]
  399. if user_id:
  400. user_ratings = dao.json_query(
  401. "select rating from rated where recipe_id in ({}) and user_id = {}".format(self.id, user_id))
  402. if len(user_ratings) > 0:
  403. user_rating = user_ratings[0]
  404. rating['user_rating'] = user_rating['rating']
  405. return rating
  406.  
  407. def get_recipe_comments(self):
  408. comments = dao.json_query(
  409. "select id,date,content,author_id from comments where parent_comment_id is null and recipe_id in ({})".format(
  410. self.id))
  411. for row in comments:
  412. author_id = row.pop('author_id')
  413. author = dao.json_query("select id,login from users where id = {}".format(author_id))
  414. row['creator'] = author
  415. replies = dao.json_query(
  416. "select id,date,content,author_id from comments where parent_comment_id = {}".format(row['id']))
  417. for reply in replies:
  418. author_id = reply.pop('author_id')
  419. author = dao.json_query("select id,login from users where id = {}".format(author_id))
  420. reply['creator'] = author
  421. row['replies'] = replies
  422. return comments
  423.  
  424.  
  425. def get_current_session_user():
  426. if 'Authorization' in request.headers:
  427. session_key = request.headers['Authorization']
  428. session = dao.json_query(
  429. """select case when expires_at > now() then user_id else null end
  430. from sessions where session_key = {}""".format(
  431. session_key))
  432. if len(session) > 0 and 'case' in session[0] and session[0]['case'] is not None:
  433. user_id = session[0]['case']
  434. return user_id, 'session_running'
  435. else:
  436. return None, 'session_expired'
  437. else:
  438. return None, 'user_not_registered'
  439.  
  440.  
  441. def table_to_string(table):
  442. table = str(table).replace("[", "")
  443. table = str(table).replace("]", "")
  444. return table
  445.  
  446.  
  447. @app.route('/api/v1/recipes-list', methods=["GET"])
  448. def list_of_recipes():
  449. params = request.get_json()
  450. queries = []
  451. if 'query' in params:
  452. browser_input = params['query']
  453. if browser_input and browser_input != '':
  454. query = "select distinct id from recipes where lower(title) like '%%{browser_input}%%'".format(
  455. browser_input=browser_input.lower())
  456. queries.append(query)
  457. if 'categories' in params:
  458. categories = params['categories']
  459. if categories and categories != '':
  460. query = """select distinct a.id from recipes a join recipe_category b on a.id = b.recipe_id
  461. where b.category_id in ({})""".format(table_to_string(categories))
  462. queries.append(query)
  463. if 'products_to_include' in params:
  464. products_to_include = params['products_to_include']
  465. query = """select id from recipes a join ingredients b on a.id = b.recipe_id
  466. where b.product_id in ({}) group by a.id having count(*) = {}""".format(
  467. table_to_string(products_to_include), len(products_to_include))
  468. res = dao.list_query(query)
  469. if len(res) < 3:
  470. query = """select id from recipes a join ingredients b on a.id = b.recipe_id
  471. where b.product_id in ({}) group by a.id order by count(*) limit 10""".format(
  472. table_to_string(products_to_include))
  473. queries.append(query)
  474. if 'products_to_exclude' in params:
  475. products_to_exclude = params['products_to_exclude']
  476. if products_to_exclude and products_to_exclude != '':
  477. query = """select distinct id from recipes where id not in
  478. (select a.id from recipes a join ingredients b on a.id = b.recipe_id
  479. where b.product_id in ({}))""".format(table_to_string(products_to_exclude))
  480. queries.append(query)
  481. if 'min_duration' in params:
  482. min_duration = params['min_duration']
  483. query = "select distinct id from recipes where duration >= {}".format(min_duration)
  484. queries.append(query)
  485. if 'max_duration' in params:
  486. max_duration = params['max_duration']
  487. query = "select distinct id from recipes where duration <= {}".format(max_duration)
  488. queries.append(query)
  489. if 'min_difficulty_level' in params:
  490. min_difficulty_level = params['min_difficulty_level']
  491. query = "select distinct id from recipes where difficulty_level >= {}".format(min_difficulty_level)
  492. queries.append(query)
  493. if 'max_difficulty_level' in params:
  494. max_difficulty_level = params['max_difficulty_level']
  495. query = "select distinct id from recipes where difficulty_level <= {}".format(max_difficulty_level)
  496. queries.append(query)
  497.  
  498. if len(queries) > 0:
  499. main_query = "select c.* from (({}) a join (".format(queries[0])
  500. old_letter = 'a'
  501. letter = 'b'
  502. for item in queries[1:]:
  503. main_query = main_query + """{item}) {letter}
  504. on {old_letter}.id = {letter}.id join (""".format(item=item, letter=letter,
  505. old_letter=old_letter)
  506. letter = chr(ord(letter) + 1)
  507. if len(queries) == 1:
  508. main_query = main_query[:-8] + ") c"
  509. else:
  510. main_query = main_query[:-7] + ") c"
  511. else:
  512. main_query = "select id from recipes"
  513. res = dao.list_query(main_query)
  514. if len(res) > 0:
  515. if 'min_rating' in params or 'max_rating' in params:
  516. if 'min_rating' in params:
  517. min_rating = params['min_rating']
  518. else:
  519. min_rating = 0
  520. if 'max_rating' in params:
  521. max_rating = params['max_rating']
  522. else:
  523. max_rating = 5
  524. new_res = []
  525. for row in res:
  526. rating = dao.list_query(
  527. """select sum(rating)/count(*)
  528. from rated where recipe_id in ({})""".format(
  529. table_to_string(res)))
  530. if len(rating) > 0:
  531. if min_rating <= rating[0] <= max_rating:
  532. new_res.append(row)
  533. res = new_res
  534. if len(res) > 0:
  535. res = ','.join(str(v) for v in res)
  536. recipes = Recipe(res)
  537. if 'sort_by' in params:
  538. sort_by = params['sort_by']
  539. else:
  540. sort_by = None
  541. if 'items' in params:
  542. items = params['items']
  543. else:
  544. items = None
  545. recipes = recipes.get_short_data_params(sort_by, items)
  546. if 'products_to_include' in params:
  547. recipes = json.loads(recipes)
  548. products_to_include = params['products_to_include']
  549. new_recipes = []
  550. for recipe in recipes:
  551. query = """select count(*) from recipes a join ingredients b on a.id = b.recipe_id
  552. where b.product_id in ({}) and a.id = {}""".format(table_to_string(products_to_include),
  553. recipe['id'])
  554. products_number = dao.list_query(query)
  555. if len(products_number) > 0:
  556. recipe['number_of_user_products'] = products_number[0]
  557. new_recipes.append(recipe)
  558. recipes = new_recipes
  559. else:
  560. recipes = []
  561. else:
  562. recipes = []
  563. return Response(json.dumps(recipes), mimetype='application/json')
  564.  
  565.  
  566. app.run()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement