PopufareAPI.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. #!/usr/bin/python3
  2. #
  3. # Copyright (c) 2019 Clementine Computing LLC.
  4. #
  5. # This file is part of PopuFare.
  6. #
  7. # PopuFare is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU Affero General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # PopuFare is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU Affero General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Affero General Public License
  18. # along with PopuFare. If not, see <https://www.gnu.org/licenses/>.
  19. #
  20. ## WORK IN PROGRESS
  21. import json
  22. import mysql.connector
  23. import time
  24. import datetime
  25. import copy
  26. #conn = mysql.connector.connect(user='bus', password='bus', host='localhost', database='busdb', port=3306)
  27. _USER = 'busapi'
  28. _PASSWORD = 'bus'
  29. _HOST = 'localhost'
  30. _DATABASE = 'busdb'
  31. _PORT = 5506
  32. Function = [
  33. "User",
  34. "Card",
  35. "Pass",
  36. "Log",
  37. "PricePoints",
  38. "AdminGetCard", "AdminGetCards", "AdminGetPass", "AdminGetUser",
  39. "AdminGetAdmin", "AdminGetPassesOnCard", "AdminGetPendingQueue", "AdminProcessPendingQueue",
  40. "AdminRemovePendingQueue", "AdminCreateCardBlock", "AdminCreateCard", "AdminSetUser",
  41. "AdminSetAdmin", "AdminAddPass", "AdminAddCard", "AdminAddUser",
  42. "AdminAddAdmin", "AdminRemovePass", "AdminRemovePasses", "AdminRemoveCard",
  43. "AdminRemoveUser", "AdminRemoveAdmin", "AdminTransferCard", "AdminTransferPass",
  44. "AdminGetPassOptions", "AdminAddCardToUser", "AdminRemoveCardFromUser", "AdminGetAdminPermissions",
  45. "AdminAddAdminPermissions", "AdminRemoveAdminPermissions", "AdminAddAdminApiPermissions", "AdminRemoveAdminApiPermissions",
  46. "AdminSetAdminApiPermissions", "AdminGetCustomCard", "AdminGetAdmins", "AdminSearchCards",
  47. "AdminSearchUsers", "AdminSearchAdmins"]
  48. PASS_FIELDS = ["logical_card_id", "issued", "activated", "deactivated", "firstused", "lastused",
  49. "nrides_orig", "nrides_remain", "nday_orig", "nday_expiration",
  50. "active", "expired", "queue_order",
  51. "rule", "comment", "paytype" ]
  52. CARD_FIELDS = ["mag_token", "rfid_token", "comment", "userid", "issued", "deactivated", "lastused",
  53. "firstused", "group_id", "issuetype"]
  54. USER_FIELDS = ["username", "comment", "first_name", "last_name", "phone",
  55. "email", "address", "city", "state", "zip", "passwordhash",
  56. "shipping_address", "shipping_city", "shipping_state", "shipping_zip",
  57. "shipping_name", "shipping_country_code", "shipping_country_name"]
  58. GROUP_FIELDS = ["id", "group_id", "group_name"]
  59. RULECLASS_FIELDS = ["id", "group_id", "group_name"]
  60. def Request(ctx):
  61. _conn = mysql.connector.connect(user=_USER, password=_PASSWORD, host=_HOST, database=_DATABASE, port=_PORT)
  62. res = {}
  63. if "function" in ctx:
  64. if ctx["function"] == "CardInfo":
  65. res = CardInfo(_conn, ctx)
  66. elif ctx["function"] == "UserInfo":
  67. res = UserInfo(_conn, ctx)
  68. elif ctx["function"] == "User":
  69. res = User(_conn, ctx)
  70. elif ctx["function"] == "Card":
  71. res = Card(_conn, ctx)
  72. elif ctx["function"] == "Pass":
  73. res = Pass(_conn, ctx)
  74. elif ctx["function"] == "Group":
  75. res = Group(_conn, ctx)
  76. elif ctx["function"] == "Ruleclass":
  77. res = Ruleclass(_conn, ctx)
  78. _conn.close()
  79. return res
  80. def CardInfo(db, ctx):
  81. card_res = {}
  82. action = "get"
  83. if "action" in ctx:
  84. action = ctx["action"]
  85. if action == "get":
  86. print("CardInfo:", ctx)
  87. cardid = -1
  88. if "logical_card_id" in ctx:
  89. cardid = ctx["logical_card_id"]
  90. card_res["logical_card_id"] = cardid
  91. card_res = Card(db, {"action":"get", "logical_card_id": cardid})
  92. card_res["pass"] = []
  93. ## through each of the passes on the card
  94. ##
  95. pass_query = "select user_pass_id from user_pass where logical_card_id = %s and expired = 0 order by queue_order asc"
  96. pass_cursor = db.cursor()
  97. pass_cursor.execute(pass_query, [card_res["logical_card_id"]])
  98. pass_rows = pass_cursor.fetchall()
  99. for pass_row in pass_rows:
  100. pass_res = Pass(db, {"action":"get", "user_pass_id":pass_row[0]})
  101. card_res["pass"].append(pass_res)
  102. card_res["user"] = {}
  103. if ((card_res["userid"] is not None) and (int(card_res["userid"]) >= 0)):
  104. card_res["user"] = User(db, {"action":"get", "userid": card_res["userid"] })
  105. elif action == "search":
  106. card_res["cards"] = []
  107. res_cardid = Card(db, ctx)
  108. for cid in res_cardid["logical_card_ids"]:
  109. _c = CardInfo(db, {"action":"get", "logical_card_id":cid})
  110. card_res["cards"].append(_c)
  111. return card_res
  112. def UserInfo(db, ctx):
  113. res = {}
  114. userid = -1
  115. if ("userid" in ctx):
  116. userid = ctx["userid"]
  117. pass_fields = PASS_FIELDS.copy()
  118. card_fields = CARD_FIELDS.copy()
  119. user_fields = USER_FIELDS.copy()
  120. res["userid"] = userid
  121. cursor = db.cursor()
  122. ## fill in user data
  123. ##
  124. res["user"] = {}
  125. fields = USER_FIELDS.copy()
  126. query = "select " + ",".join(fields) + " from users where userid = %s"
  127. cursor.execute(query, [userid])
  128. row = cursor.fetchone()
  129. if row is None:
  130. res["api_comment"] = "user not found"
  131. return res
  132. res["user"]["userid"] = userid
  133. for idx,f in enumerate(user_fields):
  134. res["user"][f] = row[idx]
  135. ## go through each card and fill in card data and pass data
  136. ##
  137. res["card"] = []
  138. query = "select logical_card_id from user_card where userid = %s and active = 1 order by logical_card_id asc"
  139. card_cursor = db.cursor()
  140. card_cursor.execute(query, [userid])
  141. rows = card_cursor.fetchall()
  142. for row in rows:
  143. card_res = CardInfo(db, {"logical_card_id":row[0]})
  144. res["card"].append(card_res)
  145. return res
  146. def _update_pass_bits(cursor, passid):
  147. q = "select logical_card_id from user_pass where user_pass_id = %s"
  148. cursor.execute(q, [passid])
  149. rows = cursor.fetchall()
  150. print("\n\nupdating pass bits", passid, "\n\n")
  151. cardid = -1
  152. for row in rows:
  153. cardid = row[0]
  154. break
  155. print("\n\nupdating pass bits cardid:", cardid, "\n\n")
  156. if cardid < 0: return
  157. q = "update user_pass set active = 0 where logical_card_id = %s"
  158. cursor.execute(q, [cardid])
  159. q = "update user_pass set active = 1 where logical_card_id = %s and expired = 0 and queue_order = " + \
  160. "( select min(x.queue_order) from user_pass x where x.logical_card_id = %s and x.expired = 0 )"
  161. cursor.execute(q, [cardid,cardid])
  162. def Ruleclass(db, ctx):
  163. res = {}
  164. ruleclass_fields = RULECLASS_FIELDS.copy()
  165. cursor = db.cursor()
  166. fields = ruleclass_fields.copy()
  167. field_vals = []
  168. if ctx["action"] == "search":
  169. query = "select id, rulename, ruleclass from rule_class"
  170. cursor.execute(query)
  171. rows = cursor.fetchall()
  172. res["ruleclass"] = []
  173. for row in rows:
  174. res["ruleclass"].append({"id":row[0], "rulename":row[1], "ruleclass":row[2]})
  175. db.commit()
  176. return res
  177. def Pass(db, ctx):
  178. res = {}
  179. passid = -1
  180. if ("user_pass_id" in ctx):
  181. passid = ctx["user_pass_id"]
  182. pass_fields = PASS_FIELDS.copy()
  183. cursor = db.cursor()
  184. fields = pass_fields.copy()
  185. field_vals = []
  186. if (ctx["action"] == "get"):
  187. query = "select " + ",".join(pass_fields) + " from user_pass where user_pass_id = %s"
  188. cursor.execute(query, [passid])
  189. row = cursor.fetchone()
  190. if row is not None:
  191. res["user_pass_id"] = passid
  192. for idx,f in enumerate(pass_fields):
  193. if isinstance(row[idx], datetime.datetime):
  194. res[f] = row[idx].strftime("%Y-%m-%d %H:%M:%S")
  195. else:
  196. res[f] = row[idx]
  197. else:
  198. res["api_comment"] = "pass not found"
  199. elif (ctx["action"] == "add"):
  200. ## fill in some default values
  201. ##
  202. dt = time.strftime('%Y-%m-%d %H:%M:%S')
  203. if "issued" not in ctx: ctx["issued"] = dt
  204. if "expired" not in ctx: ctx["expired"] = 0
  205. if "active" not in ctx: ctx["active"] = 0
  206. if "logical_card_id" in ctx:
  207. cardid = ctx["logical_card_id"]
  208. _q = "select queue_order from user_pass where logical_card_id = %s and expired = 0 order by queue_order desc limit 1"
  209. _c = db.cursor()
  210. _c.execute(_q, [cardid])
  211. _r = _c.fetchone()
  212. if _r is not None:
  213. ctx["queue_order"] = int(_r[0])+1
  214. else:
  215. ctx["active"] = 1
  216. ctx["queue_order"] = 0
  217. else:
  218. ctx["queue_order"] = 0
  219. for f in pass_fields:
  220. if f in ctx: field_vals.append(ctx[f])
  221. else: field_vals.append(None)
  222. query = "insert into user_pass (" + ",".join(fields) + ") values (" + ",".join(["%s"]*len(fields)) + ")"
  223. #print(query)
  224. #print(fields, field_vals)
  225. cursor.execute(query, field_vals)
  226. res["user_pass_id"] = cursor.lastrowid
  227. _update_pass_bits(cursor, passid);
  228. elif (ctx["action"] == "update"):
  229. update_field = []
  230. update_val = []
  231. for f in pass_fields:
  232. if f in ctx:
  233. update_field.append(f + "= %s")
  234. update_val.append(ctx[f])
  235. update_val.append(passid)
  236. query = "update user_pass set " + ",".join(update_field) + " where user_pass_id = %s"
  237. cursor.execute(query, update_val)
  238. res["user_pass_id"] = passid
  239. _update_pass_bits(cursor, passid);
  240. elif (ctx["action"] == "deactivate"):
  241. update_field = []
  242. update_val = []
  243. for f in pass_fields:
  244. if f in ctx:
  245. update_field.append(f + "= %s")
  246. update_val.append(ctx[f])
  247. update_val.append(passid)
  248. query = "update user_pass set active = 0, expired = 1 where user_pass_id = %s"
  249. cursor.execute(query, [passid])
  250. _update_pass_bits(cursor, passid);
  251. res["user_pass_id"] = passid
  252. elif (ctx["action"] == "delete"):
  253. query = "delete from user_pass where user_pass_id = %s"
  254. cursor.execute(query, [passid])
  255. _update_pass_bits(cursor, passid);
  256. db.commit()
  257. return res
  258. def Card(db, ctx):
  259. card_fields = CARD_FIELDS.copy()
  260. res = {}
  261. cardid = -1
  262. if ("logical_card_id" in ctx):
  263. cardid = ctx["logical_card_id"]
  264. cursor = db.cursor()
  265. fields = card_fields.copy()
  266. field_vals = []
  267. if (ctx["action"] == "get"):
  268. query = "select " + ",".join(card_fields) + " from user_card where logical_card_id = %s"
  269. cursor.execute(query, [cardid])
  270. row = cursor.fetchone()
  271. if row is not None:
  272. res["logical_card_id"] = cardid
  273. for idx,f in enumerate(card_fields):
  274. if isinstance(row[idx], datetime.datetime):
  275. res[f] = row[idx].strftime("%Y-%m-%d %H:%M:%S")
  276. else:
  277. res[f] = row[idx]
  278. else:
  279. res["api_comment"] = "card not found"
  280. elif (ctx["action"] == "add"):
  281. fields.append("active")
  282. for f in card_fields:
  283. if f in ctx: field_vals.append(ctx[f])
  284. else: field_vals.append(None)
  285. field_vals.append(1)
  286. query = "insert into user_card (" + ",".join(fields) + ") values (" + ",".join(["%s"]*len(fields)) + ")"
  287. cursor.execute(query, field_vals)
  288. res["logical_card_id"] = cursor.lastrowid
  289. elif (ctx["action"] == "update"):
  290. update_field = []
  291. update_val = []
  292. for f in card_fields:
  293. if f in ctx:
  294. update_field.append(f + "= %s")
  295. update_val.append(ctx[f])
  296. update_val.append(cardid)
  297. query = "update user_card set " + ",".join(update_field) + " where logical_card_id = %s"
  298. cursor.execute(query, update_val)
  299. res["logical_card_id"] = cardid
  300. elif (ctx["action"] == "delete"):
  301. query = "delete from user_card where logical_card_id = %s"
  302. cursor.execute(query, [cardid])
  303. elif (ctx["action"] == "search"):
  304. query = "select logical_card_id from user_card where "
  305. n_search = 0
  306. if "logical_card_id" in ctx:
  307. query += " logical_card_id = %s"
  308. field_vals.append( ctx["logical_card_id"])
  309. n_search += 1
  310. if "mag_token" in ctx:
  311. query += " mag_token like %s "
  312. field_vals.append( '%' + ctx["mag_token"] + '%')
  313. n_search += 1
  314. if "rfid_token" in ctx:
  315. if len(field_vals)>0: query += " and "
  316. query += " rfid_token like %s "
  317. field_vals.append( '%' + ctx["rfid_token"] + '%')
  318. n_search += 1
  319. query_limit = " "
  320. if "limit" in ctx:
  321. query_limit = " limit %s "
  322. search_vals.append(ctx["limit"])
  323. query += query_limit
  324. res["logical_card_ids"] = []
  325. if n_search > 0:
  326. cursor.execute(query, field_vals)
  327. rows = cursor.fetchall()
  328. for row in rows:
  329. res["logical_card_ids"].append(row[0])
  330. db.commit()
  331. return res
  332. def Group(db,ctx):
  333. group_res = { }
  334. action = "get"
  335. if "action" in ctx:
  336. cation = ctx["action"]
  337. cursor = db.cursor()
  338. if action == "get":
  339. print("Group:", ctx)
  340. group_res["group"] = []
  341. query = "select group_id, group_name from groups order by group_id asc"
  342. cursor.execute(query)
  343. rows = cursor.fetchall()
  344. for row in rows:
  345. group_res["group"].append({"group_id":row[0], "group_name":row[1]})
  346. db.commit()
  347. return group_res
  348. def User(db, ctx):
  349. user_fields = USER_FIELDS.copy()
  350. res = {}
  351. cursor = db.cursor()
  352. fields = user_fields.copy()
  353. user_vals = []
  354. userid = -1
  355. if "userid" in ctx: userid = ctx["userid"]
  356. if (ctx["action"] == "get"):
  357. query = "select " + ",".join(user_fields) + " from users where userid = %s"
  358. cursor.execute(query, [userid])
  359. row = cursor.fetchone()
  360. if row is not None:
  361. res["userid"] = userid
  362. for idx,f in enumerate(user_fields):
  363. if isinstance(row[idx], datetime.datetime):
  364. res[f] = row[idx].strftime("%Y-%m-%d %H:%M:%S")
  365. else:
  366. res[f] = row[idx]
  367. else:
  368. res["api_comment"] = "user not found"
  369. elif (ctx["action"] == "add"):
  370. fields.append("active")
  371. fields.append("created")
  372. for f in user_fields:
  373. if f in ctx: user_vals.append(ctx[f])
  374. else: user_vals.append(None)
  375. user_vals.append(1)
  376. user_vals.append(time.strftime('%Y-%m-%d %H:%M:%S'))
  377. query = "insert into users (" + ",".join(fields) + ") values (" + ",".join(["%s"]*len(fields)) + ")"
  378. cursor.execute(query, user_vals)
  379. res["userid"] = cursor.lastrowid
  380. elif (ctx["action"] == "update"):
  381. update_field = []
  382. update_val = []
  383. for f in user_fields:
  384. if f in ctx:
  385. update_field.append(f + "= %s")
  386. update_val.append(ctx[f])
  387. update_val.append(userid)
  388. query = "update users set " + ",".join(update_field) + " where userid = %s"
  389. cursor.execute(query, update_val)
  390. res["userid"] = userid
  391. elif (ctx["action"] == "delete"):
  392. query = "delete from users where userid = %s"
  393. cursor.execute(query, [userid])
  394. elif (ctx["action"] == "search"):
  395. res["userids"] = []
  396. res["userid"] = userid
  397. search_field = []
  398. search_val = []
  399. for f in user_fields:
  400. if f in ctx:
  401. search_field.append(f + " like %s")
  402. search_val.append('%' + ctx[f] + '%')
  403. query_limit = " "
  404. if "limit" in ctx:
  405. query_limit = " limit %s "
  406. search_val.append(ctx["limit"])
  407. query = "select userid from users where " + " and ".join(search_field) + query_limit
  408. cursor.execute(query, search_val)
  409. rows = cursor.fetchall()
  410. for row in rows:
  411. res["userids"].append(row[0])
  412. db.commit()
  413. return res
  414. def main(db):
  415. print("main")
  416. print("---------")
  417. print("---------")
  418. print("---------")
  419. res = User(db, {"action":"add", "username":"abe" })
  420. print("user.add:", res)
  421. res = User(db, {"action":"update", "username":"abeabe", "userid":res["userid"]})
  422. print("user.update:", res)
  423. res = User(db, {"action":"get", "userid":res["userid"]})
  424. print("user.get:", res)
  425. res = User(db, {"action":"delete", "userid": res["userid"]})
  426. print("user.delete:", res)
  427. print("---------")
  428. print("---------")
  429. print("---------")
  430. res = Card(db, {"action":"get", "logical_card_id":1})
  431. print("card.get:", res)
  432. res = Card(db, {"action":"add", "mag_token":"2:1234", "rfid_token":"26:20:415", "comment":"testing api", "userid":1})
  433. print("card.add:", res)
  434. res = Card(db, {"action":"update", "mag_token":"2:9234", "logical_card_id":res["logical_card_id"]})
  435. print("card.update:", res)
  436. res = Card(db, {"action":"delete", "logical_card_id":res["logical_card_id"]})
  437. print("card.delete:", res)
  438. print("---------")
  439. print("---------")
  440. print("---------")
  441. res = Pass(db, {"action":"get", "user_pass_id":11})
  442. print("pass.get:", res)
  443. res = Pass(db, {"action":"add", "logical_card_id":1, "queue_order":9, "rule":"TEST-ORG-NDAY", "nday_orig":3})
  444. print("pass.add:", res)
  445. res = Pass(db, {"action":"update", "user_pass_id":res["user_pass_id"], "queue_order":10, "rule":"TEST-ORG-NDAY", "nday_orig":5})
  446. print("pass.update:", res)
  447. res = Pass(db, {"action":"delete", "user_pass_id":res["user_pass_id"]})
  448. print("pass.delete:", res)
  449. print("---------")
  450. print("---------")
  451. print("---------")
  452. res = UserInfo(db, {"userid":348})
  453. print("userinfo:", json.dumps(res, indent=2))
  454. print("---------")
  455. print("---------")
  456. print("---------")
  457. res = Request({"function":"CardInfo", "action":"search", "logical_card_id":1})
  458. print("request.card.search:", res)
  459. if __name__ == "__main__":
  460. conn = mysql.connector.connect(user=_USER, password=_PASSWORD, host=_HOST, database=_DATABASE, port=_PORT)
  461. main(conn)
  462. conn.close()