PopufareAPI.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314
  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. import hashlib
  27. import sys
  28. import getopt
  29. POPUFARE_API_VERSION = "2.0.0"
  30. #conn = mysql.connector.connect(user='bus', password='bus', host='localhost', database='busdb', port=3306)
  31. _USER = 'busapi'
  32. _PASSWORD = 'bus'
  33. _HOST = 'localhost'
  34. _DATABASE = 'busdb'
  35. _PORT = 5506
  36. Function = [
  37. "User",
  38. "Card",
  39. "Pass",
  40. "Log",
  41. "PricePoints",
  42. "AdminGetCard", "AdminGetCards", "AdminGetPass", "AdminGetUser",
  43. "AdminGetAdmin", "AdminGetPassesOnCard", "AdminGetPendingQueue", "AdminProcessPendingQueue",
  44. "AdminRemovePendingQueue", "AdminCreateCardBlock", "AdminCreateCard", "AdminSetUser",
  45. "AdminSetAdmin", "AdminAddPass", "AdminAddCard", "AdminAddUser",
  46. "AdminAddAdmin", "AdminRemovePass", "AdminRemovePasses", "AdminRemoveCard",
  47. "AdminRemoveUser", "AdminRemoveAdmin", "AdminTransferCard", "AdminTransferPass",
  48. "AdminGetPassOptions", "AdminAddCardToUser", "AdminRemoveCardFromUser", "AdminGetAdminPermissions",
  49. "AdminAddAdminPermissions", "AdminRemoveAdminPermissions", "AdminAddAdminApiPermissions", "AdminRemoveAdminApiPermissions",
  50. "AdminSetAdminApiPermissions", "AdminGetCustomCard", "AdminGetAdmins", "AdminSearchCards",
  51. "AdminSearchUsers", "AdminSearchAdmins"]
  52. PASS_FIELDS = ["logical_card_id", "issued", "activated", "deactivated", "firstused", "lastused",
  53. "nrides_orig", "nrides_remain", "nday_orig", "nday_expiration",
  54. "active", "expired", "queue_order",
  55. "rule", "comment", "paytype" ]
  56. CARD_FIELDS = ["mag_token", "rfid_token", "comment", "userid", "issued", "deactivated", "lastused",
  57. "firstused", "group_id", "issuetype"]
  58. USER_FIELDS = ["username", "comment", "first_name", "last_name", "phone",
  59. "email", "address", "city", "state", "zip", "passwordhash",
  60. "shipping_address", "shipping_city", "shipping_state", "shipping_zip",
  61. "shipping_name", "shipping_country_code", "shipping_country_name"]
  62. GROUP_FIELDS = ["id", "group_id", "group_name"]
  63. RULECLASS_FIELDS = ["id", "group_id", "group_name"]
  64. def Request(ctx, db_info = { "user":_USER, "pass":_PASSWORD, "host":_HOST, "db":_DATABASE, "port":_PORT}):
  65. #_conn = mysql.connector.connect(user=_USER, password=_PASSWORD, host=_HOST, database=_DATABASE, port=_PORT)
  66. _conn = mysql.connector.connect(user=db_info["user"], password=db_info["pass"], host=db_info["host"], database=db_info["db"], port=db_info["port"])
  67. res = {}
  68. if "function" in ctx:
  69. if ctx["function"] == "CardInfo":
  70. res = CardInfo(_conn, ctx)
  71. elif ctx["function"] == "UserInfo":
  72. res = UserInfo(_conn, ctx)
  73. elif ctx["function"] == "User":
  74. res = User(_conn, ctx)
  75. elif ctx["function"] == "Card":
  76. res = Card(_conn, ctx)
  77. elif ctx["function"] == "Pass":
  78. res = Pass(_conn, ctx)
  79. elif ctx["function"] == "Group":
  80. res = Group(_conn, ctx)
  81. elif ctx["function"] == "Ruleclass":
  82. res = Ruleclass(_conn, ctx)
  83. elif ctx["function"] == "RecycleCard":
  84. res = RecycleCard(_conn, ctx)
  85. elif ctx["function"] == "AddCardBlock":
  86. res = AddCardBlock(_conn, ctx)
  87. elif ctx["function"] == "Search":
  88. res = Search(_conn, ctx)
  89. elif ctx["function"] == "Pending":
  90. res = Pending(_conn, ctx)
  91. elif ctx["function"] == "ping":
  92. res["result"] = "success"
  93. res["data"] = "pong"
  94. _conn.close()
  95. return res
  96. ## _ _ __
  97. ## ___ __ _ _ __ __| (_)_ __ / _| ___
  98. ## / __/ _` | '__/ _` | | '_ \| |_ / _ \
  99. ## | (_| (_| | | | (_| | | | | | _| (_) |
  100. ## \___\__,_|_| \__,_|_|_| |_|_| \___/
  101. ##
  102. def CardInfo(db, ctx):
  103. card_res = {}
  104. action = "get"
  105. if "action" in ctx:
  106. action = ctx["action"]
  107. if action == "get":
  108. print("CardInfo:", ctx)
  109. cardid = -1
  110. if "logical_card_id" in ctx:
  111. cardid = ctx["logical_card_id"]
  112. card_res["logical_card_id"] = cardid
  113. card_res = Card(db, {"action":"get", "logical_card_id": cardid})
  114. card_res["pass"] = []
  115. if card_res["result"] == "success":
  116. ## through each of the passes on the card
  117. ##
  118. pass_query = "select user_pass_id from user_pass where logical_card_id = %s and expired = 0 order by queue_order asc"
  119. pass_cursor = db.cursor()
  120. pass_cursor.execute(pass_query, [card_res["logical_card_id"]])
  121. pass_rows = pass_cursor.fetchall()
  122. for pass_row in pass_rows:
  123. pass_res = Pass(db, {"action":"get", "user_pass_id":pass_row[0]})
  124. card_res["pass"].append(pass_res)
  125. card_res["user"] = {}
  126. if ((card_res["userid"] is not None) and (int(card_res["userid"]) >= 0)):
  127. card_res["user"] = User(db, {"action":"get", "userid": card_res["userid"] })
  128. elif action == "search":
  129. card_res["cards"] = []
  130. res_cardid = Card(db, ctx)
  131. for cid in res_cardid["logical_card_ids"]:
  132. _c = CardInfo(db, {"action":"get", "logical_card_id":cid})
  133. card_res["cards"].append(_c)
  134. card_res["result"] = "success"
  135. return card_res
  136. ## _ __
  137. ## _ _ ___ ___ _ __(_)_ __ / _| ___
  138. ## | | | / __|/ _ \ '__| | '_ \| |_ / _ \
  139. ## | |_| \__ \ __/ | | | | | | _| (_) |
  140. ## \__,_|___/\___|_| |_|_| |_|_| \___/
  141. ##
  142. def UserInfo(db, ctx):
  143. res = {}
  144. res["result"] = "fail"
  145. userid = -1
  146. if ("userid" in ctx):
  147. userid = ctx["userid"]
  148. pass_fields = PASS_FIELDS.copy()
  149. card_fields = CARD_FIELDS.copy()
  150. user_fields = USER_FIELDS.copy()
  151. res["userid"] = userid
  152. cursor = db.cursor()
  153. ## fill in user data
  154. ##
  155. res["user"] = {}
  156. fields = USER_FIELDS.copy()
  157. query = "select " + ",".join(fields) + " from users where userid = %s"
  158. cursor.execute(query, [userid])
  159. row = cursor.fetchone()
  160. if row is None:
  161. res["api_comment"] = "user not found"
  162. return res
  163. res["user"]["userid"] = userid
  164. for idx,f in enumerate(user_fields):
  165. res["user"][f] = row[idx]
  166. ## go through each card and fill in card data and pass data
  167. ##
  168. res["card"] = []
  169. query = "select logical_card_id from user_card where userid = %s and active = 1 order by logical_card_id asc"
  170. card_cursor = db.cursor()
  171. card_cursor.execute(query, [userid])
  172. rows = card_cursor.fetchall()
  173. for row in rows:
  174. card_res = CardInfo(db, {"logical_card_id":row[0]})
  175. res["card"].append(card_res)
  176. res["result"] = "success"
  177. return res
  178. def _update_pass_bits(cursor, passid):
  179. q = "select logical_card_id from user_pass where user_pass_id = %s"
  180. cursor.execute(q, [passid])
  181. rows = cursor.fetchall()
  182. print("\n\nupdating pass bits", passid, "\n\n")
  183. cardid = -1
  184. for row in rows:
  185. cardid = row[0]
  186. break
  187. print("\n\nupdating pass bits cardid:", cardid, "\n\n")
  188. if cardid < 0: return
  189. q = "update user_pass set active = 0 where logical_card_id = %s"
  190. cursor.execute(q, [cardid])
  191. q = "update user_pass set active = 1 where logical_card_id = %s and expired = 0 and queue_order = " + \
  192. "( select min(x.queue_order) from user_pass x where x.logical_card_id = %s and x.expired = 0 )"
  193. cursor.execute(q, [cardid,cardid])
  194. ## _ _
  195. ## _ __ _ _| | ___ ___| | __ _ ___ ___
  196. ## | '__| | | | |/ _ \/ __| |/ _` / __/ __|
  197. ## | | | |_| | | __/ (__| | (_| \__ \__ \
  198. ## |_| \__,_|_|\___|\___|_|\__,_|___/___/
  199. ##
  200. def Ruleclass(db, ctx):
  201. res = {}
  202. ruleclass_fields = RULECLASS_FIELDS.copy()
  203. cursor = db.cursor()
  204. fields = ruleclass_fields.copy()
  205. field_vals = []
  206. if ctx["action"] == "search":
  207. query = "select id, rulename, ruleclass from rule_class"
  208. cursor.execute(query)
  209. rows = cursor.fetchall()
  210. res["ruleclass"] = []
  211. for row in rows:
  212. res["ruleclass"].append({"id":row[0], "rulename":row[1], "ruleclass":row[2]})
  213. db.commit()
  214. return res
  215. ##
  216. ## _ __ __ _ ___ ___
  217. ## | '_ \ / _` / __/ __|
  218. ## | |_) | (_| \__ \__ \
  219. ## | .__/ \__,_|___/___/
  220. ## |_|
  221. def Pass(db, ctx):
  222. res = {}
  223. passid = -1
  224. if ("user_pass_id" in ctx):
  225. passid = ctx["user_pass_id"]
  226. pass_fields = PASS_FIELDS.copy()
  227. cursor = db.cursor()
  228. fields = pass_fields.copy()
  229. field_vals = []
  230. if (ctx["action"] == "get"):
  231. query = "select " + ",".join(pass_fields) + " from user_pass where user_pass_id = %s"
  232. cursor.execute(query, [passid])
  233. row = cursor.fetchone()
  234. if row is not None:
  235. res["result"] = "success"
  236. res["user_pass_id"] = passid
  237. for idx,f in enumerate(pass_fields):
  238. if isinstance(row[idx], datetime.datetime):
  239. res[f] = row[idx].strftime("%Y-%m-%d %H:%M:%S")
  240. else:
  241. res[f] = row[idx]
  242. else:
  243. res["result"] = "fail"
  244. res["api_comment"] = "pass not found"
  245. elif (ctx["action"] == "add"):
  246. if (not "logical_card_id" in ctx) or (ctx["logical_card_id"] == ''):
  247. res["result"] = "fail"
  248. res["api_comment"] = "must have logical_card_id to add pass"
  249. else:
  250. ## fill in some default values
  251. ##
  252. dt = time.strftime('%Y-%m-%d %H:%M:%S')
  253. if "issued" not in ctx: ctx["issued"] = dt
  254. if "expired" not in ctx: ctx["expired"] = 0
  255. if "active" not in ctx: ctx["active"] = 0
  256. if "logical_card_id" in ctx:
  257. cardid = ctx["logical_card_id"]
  258. _q = "select queue_order from user_pass where logical_card_id = %s and expired = 0 order by queue_order desc limit 1"
  259. _c = db.cursor()
  260. _c.execute(_q, [cardid])
  261. _r = _c.fetchone()
  262. if _r is not None:
  263. ctx["queue_order"] = int(_r[0])+1
  264. else:
  265. ctx["active"] = 1
  266. ctx["queue_order"] = 0
  267. else:
  268. ctx["queue_order"] = 0
  269. for f in pass_fields:
  270. if f in ctx: field_vals.append(ctx[f])
  271. else: field_vals.append(None)
  272. query = "insert into user_pass (" + ",".join(fields) + ") values (" + ",".join(["%s"]*len(fields)) + ")"
  273. print(query)
  274. print(fields, field_vals)
  275. cursor.execute(query, field_vals)
  276. res["user_pass_id"] = cursor.lastrowid
  277. res["result"] = "success"
  278. _update_pass_bits(cursor, passid);
  279. elif (ctx["action"] == "update"):
  280. update_field = []
  281. update_val = []
  282. for f in pass_fields:
  283. if f in ctx:
  284. update_field.append(f + "= %s")
  285. update_val.append(ctx[f])
  286. update_val.append(passid)
  287. query = "update user_pass set " + ",".join(update_field) + " where user_pass_id = %s"
  288. cursor.execute(query, update_val)
  289. res["user_pass_id"] = passid
  290. res["result"] = "success"
  291. _update_pass_bits(cursor, passid);
  292. elif (ctx["action"] == "deactivate"):
  293. update_field = []
  294. update_val = []
  295. for f in pass_fields:
  296. if f in ctx:
  297. update_field.append(f + "= %s")
  298. update_val.append(ctx[f])
  299. update_val.append(passid)
  300. query = "update user_pass set active = 0, expired = 1 where user_pass_id = %s"
  301. cursor.execute(query, [passid])
  302. _update_pass_bits(cursor, passid);
  303. res["user_pass_id"] = passid
  304. res["result"] = "success"
  305. elif (ctx["action"] == "delete"):
  306. query = "delete from user_pass where user_pass_id = %s"
  307. cursor.execute(query, [passid])
  308. _update_pass_bits(cursor, passid);
  309. res["result"] = "success"
  310. db.commit()
  311. return res
  312. ## _
  313. ## ___ __ _ _ __ __| |
  314. ## / __/ _` | '__/ _` |
  315. ## | (_| (_| | | | (_| |
  316. ## \___\__,_|_| \__,_|
  317. ##
  318. def Card(db, ctx):
  319. card_fields = CARD_FIELDS.copy()
  320. res = {}
  321. cardid = -1
  322. if ("logical_card_id" in ctx):
  323. cardid = ctx["logical_card_id"]
  324. cursor = db.cursor()
  325. fields = card_fields.copy()
  326. field_vals = []
  327. if (ctx["action"] == "get"):
  328. query = "select " + ",".join(card_fields) + " from user_card where logical_card_id = %s"
  329. cursor.execute(query, [cardid])
  330. row = cursor.fetchone()
  331. if row is not None:
  332. res["logical_card_id"] = cardid
  333. for idx,f in enumerate(card_fields):
  334. if isinstance(row[idx], datetime.datetime):
  335. res[f] = row[idx].strftime("%Y-%m-%d %H:%M:%S")
  336. else:
  337. res[f] = row[idx]
  338. res["result"] = "success"
  339. else:
  340. res["result"] = "fail"
  341. res["api_comment"] = "card not found"
  342. elif (ctx["action"] == "add"):
  343. fields.append("active")
  344. for f in card_fields:
  345. if f in ctx: field_vals.append(ctx[f])
  346. else: field_vals.append(None)
  347. field_vals.append(1)
  348. query = "insert into user_card (" + ",".join(fields) + ") values (" + ",".join(["%s"]*len(fields)) + ")"
  349. cursor.execute(query, field_vals)
  350. res["logical_card_id"] = cursor.lastrowid
  351. res["result"] = "success"
  352. elif (ctx["action"] == "update"):
  353. if not "logical_card_id" in ctx:
  354. res["result"] = "fail"
  355. res["api_comment"] = "must supply a logical_card_id"
  356. else:
  357. update_field = []
  358. update_val = []
  359. query_card_id = ctx["logical_card_id"]
  360. cursor.execute("select logical_card_id from user_card where logical_card_id = %s", [query_card_id])
  361. rows = cursor.fetchall()
  362. if len(rows) == 0:
  363. res["result"] = "fail"
  364. res["api_comment"] = "card not found"
  365. else:
  366. print(">>>>", len(rows))
  367. for row in rows:
  368. logical_card_id = row[0]
  369. for f in card_fields:
  370. if f in ctx:
  371. update_field.append(f + "= %s")
  372. update_val.append(ctx[f])
  373. update_val.append(cardid)
  374. query = "update user_card set " + ",".join(update_field) + " where logical_card_id = %s"
  375. cursor.execute(query, update_val)
  376. res["logical_card_id"] = cardid
  377. res["result"] = "success"
  378. elif (ctx["action"] == "delete"):
  379. query = "delete from user_card where logical_card_id = %s"
  380. cursor.execute(query, [cardid])
  381. res["result"] = "success"
  382. elif (ctx["action"] == "search"):
  383. query = "select logical_card_id from user_card where "
  384. n_search = 0
  385. if "logical_card_id" in ctx:
  386. query += " logical_card_id = %s"
  387. field_vals.append( ctx["logical_card_id"])
  388. n_search += 1
  389. if "mag_token" in ctx:
  390. query += " mag_token like %s "
  391. field_vals.append( '%' + ctx["mag_token"] + '%')
  392. n_search += 1
  393. if "rfid_token" in ctx:
  394. if len(field_vals)>0: query += " and "
  395. query += " rfid_token like %s "
  396. field_vals.append( '%' + ctx["rfid_token"] + '%')
  397. n_search += 1
  398. query_limit = " "
  399. if "limit" in ctx:
  400. query_limit = " limit %s "
  401. search_vals.append(ctx["limit"])
  402. query += query_limit
  403. res["logical_card_ids"] = []
  404. if n_search > 0:
  405. cursor.execute(query, field_vals)
  406. rows = cursor.fetchall()
  407. for row in rows:
  408. res["logical_card_ids"].append(row[0])
  409. res["result"] = "success"
  410. db.commit()
  411. return res
  412. ##
  413. ## __ _ _ __ ___ _ _ _ __
  414. ## / _` | '__/ _ \| | | | '_ \
  415. ## | (_| | | | (_) | |_| | |_) |
  416. ## \__, |_| \___/ \__,_| .__/
  417. ## |___/ |_|
  418. def Group(db,ctx):
  419. group_res = { "result":"fail"}
  420. action = "get"
  421. if "action" in ctx:
  422. action = ctx["action"]
  423. cursor = db.cursor()
  424. if action == "get":
  425. group_res["group"] = []
  426. query = "select group_id, group_name from groups order by group_id asc"
  427. cursor.execute(query)
  428. rows = cursor.fetchall()
  429. for row in rows:
  430. group_res["group"].append({"group_id":row[0], "group_name":row[1]})
  431. group_res["result"] = "success"
  432. elif action == "add":
  433. if "group_name" not in ctx:
  434. group_res["result"] = "fail"
  435. group_res["api_comment"] = "must provide group name to add"
  436. return res
  437. group_name = ctx["group_name"]
  438. group_id = -1
  439. cursor.execute("select max(group_id) from groups")
  440. x = cursor.fetchone()
  441. if x[0] is None:
  442. group_id = 1
  443. else:
  444. group_id = int(x[0]) + 1
  445. cursor.execute("insert into groups (group_id, group_name) values (%s, %s)", [group_id, group_name])
  446. group_res["result"] = "success"
  447. group_res["group_id"] = group_id
  448. group_res["group_name"] = group_name
  449. elif action == "getone":
  450. group_res["group"] = []
  451. query = "select group_id, group_name from groups where group_name = %s "
  452. cursor.execute(query, [ctx["group"]])
  453. row = cursor.fetchone()
  454. if not row:
  455. group_res["result"] = "fail"
  456. group_res["api_comment"] = "invalid group"
  457. else:
  458. group_res["result"] = "success"
  459. group_res["group_id"] = row[0]
  460. elif action == "default":
  461. return Group(db, {"action":"getone", "group":"TEST-ORG"})
  462. db.commit()
  463. return group_res
  464. ##
  465. ## _ _ ___ ___ _ __
  466. ## | | | / __|/ _ \ '__|
  467. ## | |_| \__ \ __/ |
  468. ## \__,_|___/\___|_|
  469. ##
  470. def User(db, ctx):
  471. user_fields = USER_FIELDS.copy()
  472. res = {}
  473. cursor = db.cursor()
  474. fields = user_fields.copy()
  475. user_vals = []
  476. userid = -1
  477. if "userid" in ctx: userid = ctx["userid"]
  478. print("cp.user")
  479. ## USER GET
  480. ##
  481. if (ctx["action"] == "get"):
  482. query = "select " + ",".join(user_fields) + " from users where userid = %s"
  483. cursor.execute(query, [userid])
  484. row = cursor.fetchone()
  485. if row is not None:
  486. res["userid"] = userid
  487. for idx,f in enumerate(user_fields):
  488. if isinstance(row[idx], datetime.datetime):
  489. res[f] = row[idx].strftime("%Y-%m-%d %H:%M:%S")
  490. else:
  491. res[f] = row[idx]
  492. res["result"] = "success"
  493. else:
  494. res["result"] = "fail"
  495. res["api_comment"] = "user not found"
  496. ## USER ADD
  497. ##
  498. elif (ctx["action"] == "add"):
  499. if ((not "password" in ctx) or
  500. (not "username" in ctx) ):
  501. res["api_comment"] = "invalid parameters, need username and password to create account"
  502. res["result"] = "fail"
  503. else:
  504. uname = ctx["username"]
  505. pword = ctx["password"]
  506. fields.append("active")
  507. fields.append("created")
  508. for f in user_fields:
  509. if f in ctx: user_vals.append(ctx[f])
  510. elif f == "passwordhash":
  511. ha = hashlib.sha256()
  512. ha.update(str.encode(uname))
  513. ha.update(str.encode(pword))
  514. user_vals.append(ha.hexdigest())
  515. else: user_vals.append(None)
  516. user_vals.append(1)
  517. user_vals.append(time.strftime('%Y-%m-%d %H:%M:%S'))
  518. query = "insert into users (" + ",".join(fields) + ") values (" + ",".join(["%s"]*len(fields)) + ")"
  519. cursor.execute(query, user_vals)
  520. res["userid"] = cursor.lastrowid
  521. res["result"] = "success"
  522. ## USER UPDATE
  523. ##
  524. elif (ctx["action"] == "update"):
  525. if not "userid" in ctx:
  526. res["result"] = "fail"
  527. res["api_comment"] = "no userid specified"
  528. else:
  529. uname = ''
  530. query = "select username from users where userid = %s";
  531. cursor.execute(query, [userid])
  532. rows = cursor.fetchall()
  533. for row in rows:
  534. uname = row[0]
  535. if uname == '':
  536. res["result"] = "fail"
  537. res["api_comment"] = "could not find username"
  538. else:
  539. update_field = []
  540. update_val = []
  541. print("user_field:", user_fields)
  542. print("ctx:", ctx)
  543. for f in user_fields:
  544. if (f == "passwordhash") and ("password" in ctx):
  545. update_field.append(" passwordhash = %s ")
  546. ha = hashlib.sha256()
  547. ha.update(str.encode(uname))
  548. ha.update(str.encode(ctx["password"]))
  549. update_val.append(ha.hexdigest())
  550. elif f in ctx:
  551. update_field.append(f + "= %s")
  552. update_val.append(ctx[f])
  553. else:
  554. pass
  555. #update_val.append(None)
  556. update_val.append(userid)
  557. if len(update_field) == 0:
  558. print("NOPE")
  559. print("manage_user.update>>>", userid, ":".join(update_field), ":".join(update_val), len(update_field))
  560. query = "update users set " + ",".join(update_field) + " where userid = %s"
  561. print("WTFFF???", query)
  562. cursor.execute(query, update_val)
  563. res["userid"] = userid
  564. res["result"] = "success"
  565. ## USER DELETE
  566. ##
  567. elif (ctx["action"] == "delete"):
  568. query = "delete from users where userid = %s"
  569. cursor.execute(query, [userid])
  570. ## USER SEARCH
  571. ##
  572. elif (ctx["action"] == "search"):
  573. res["userids"] = []
  574. res["userid"] = userid
  575. res["result"] = "success"
  576. search_field = []
  577. search_val = []
  578. for f in user_fields:
  579. if f in ctx:
  580. search_field.append(f + " like %s")
  581. search_val.append('%' + ctx[f] + '%')
  582. query_limit = " "
  583. if "limit" in ctx:
  584. query_limit = " limit %s "
  585. search_val.append(ctx["limit"])
  586. query = "select userid from users where " + " and ".join(search_field) + query_limit
  587. cursor.execute(query, search_val)
  588. rows = cursor.fetchall()
  589. for row in rows:
  590. res["userids"].append(row[0])
  591. db.commit()
  592. return res
  593. ## _ _
  594. ## _ __ ___ ___ _ _ ___| | ___ ___ __ _ _ __ __| |
  595. ## | '__/ _ \/ __| | | |/ __| |/ _ \ / __/ _` | '__/ _` |
  596. ## | | | __/ (__| |_| | (__| | __/ | (_| (_| | | | (_| |
  597. ## |_| \___|\___|\__, |\___|_|\___| \___\__,_|_| \__,_|
  598. ## |___/
  599. def RecycleCard(db, ctx):
  600. res = {"result":"fail"}
  601. cursor = db.cursor()
  602. fields = ["logical_card_id", "rfid_token", "mag_token", "group",
  603. "userid",
  604. "rule", "pass_class", "nrides_remain", "nrides_orig", "nday_orig" ]
  605. val = {}
  606. for f in fields:
  607. if f in ctx:
  608. if ctx[f] and len(ctx[f]) > 0:
  609. val[f] = ctx[f]
  610. if ((not ("logical_card_id" in val)) and
  611. (not ("rfid_token" in val)) and
  612. (not ("mag_token" in val))):
  613. return res
  614. logical_card_id = -1
  615. if not ("logical_card_id" in val):
  616. if "mag_token" in val:
  617. query = "select logical_card_id from user_card where mag_token = %s and active = 1"
  618. cursor.execute(query, [val["mag_token"]])
  619. cid = cursor.fetchone()
  620. if cid is not None:
  621. val["logical_card_id"] = cid[0]
  622. else:
  623. return res
  624. elif "rfid_token" in val:
  625. query = "select logical_card_id from user_card where rfid_token = %s and active = 1"
  626. cursor.execute(query, [val["rfid_token"]])
  627. cid = cursor.fetchone()
  628. if cid is not None:
  629. val["logical_card_id"] = cid[0]
  630. else:
  631. return res
  632. group_info = Group(db, {"action":"default"})
  633. if "group" in val:
  634. group_info = Group(db, {"action":"getone", "group":val["group"]})
  635. if group_info["result"] != "success":
  636. res["api_comment"] = "invalid group"
  637. return res
  638. query = "update user_card set active = 0 where logical_card_id = %s"
  639. cursor.execute(query, [val["logical_card_id"]])
  640. card_info = {"action":"add", "group_id":group_info["group_id"], "active":1}
  641. if "mag_token" in val: card_info["mag_token"] = val["mag_token"]
  642. if "rfid_token" in val: card_info["rfid_token"] = val["rfid_token"]
  643. card_res = Card(db, card_info)
  644. if card_res["result"] != "success":
  645. res["result"] = "fail"
  646. res["api_comment"] = "failed to find card"
  647. return res
  648. res["logical_card_id"] = card_res["logical_card_id"]
  649. if "pass_class" in val:
  650. pass_opt = {"action":"add", "logical_card_id": res["logical_card_id"] }
  651. if val["pass_class"] == "OTHER":
  652. pass_opt["rule"] = val["rule"]
  653. Pass(db, pass_opt)
  654. elif val["pass_class"] == "NRIDE":
  655. pass_opt["rule"] = val["rule"]
  656. pass_opt["nrides_orig"] = val["nrides_orig"]
  657. pass_opt["nrides_remain"] = val["nrides_remain"]
  658. Pass(db, pass_opt)
  659. elif val["pass_class"] == "NDAY":
  660. pass_opt["rule"] = val["rule"]
  661. pass_opt["nday_orig"] = val["nday_orig"]
  662. Pass(db, pass_opt)
  663. return res
  664. ## _
  665. ## ___ ___ __ _ _ __ ___| |__
  666. ## / __|/ _ \/ _` | '__/ __| '_ \
  667. ## \__ \ __/ (_| | | | (__| | | |
  668. ## |___/\___|\__,_|_| \___|_| |_|
  669. ##
  670. def Search(db, ctx):
  671. res = {"result":"fail"}
  672. cursor = db.cursor()
  673. fields = ["search_type", "search_string", "start", "count"]
  674. start = "0"
  675. count = "100"
  676. val = {}
  677. for f in fields:
  678. if f in ctx:
  679. if ctx[f] and len(ctx[f]) > 0:
  680. val[f] = ctx[f]
  681. if "start" in val: start = val["start"]
  682. if "count" in val: count = val["count"]
  683. if val["search_type"] == "card":
  684. t = val["search_string"]
  685. p = '%' + t + '%'
  686. query = "select logical_card_id, mag_token, rfid_token, userid, issued, firstused, lastused, group_id, issuetype"
  687. query += " from user_card where mag_token like %s "
  688. query += " or rfid_token like %s "
  689. query += " or comment like %s "
  690. query += " or issuetype like %s "
  691. query += " and active = 1 "
  692. query += " order by logical_card_id desc"
  693. query += " limit %s,%s "
  694. _v = [p,p, p, p, int(start), int(start) + int(count)]
  695. #cursor.execute(query, [p, p, p, p])
  696. cursor.execute(query, _v)
  697. rows = cursor.fetchall()
  698. res["data"] = []
  699. for row in rows:
  700. _d = {
  701. "iscard": 1,
  702. "logical_card_id": row[0],
  703. "mag_token": row[1],
  704. "rfid_token": row[2],
  705. "userid": row[3],
  706. "issued": row[4],
  707. "firstused": row[5],
  708. "lastused": row[6],
  709. "group_id": row[7],
  710. "issuetype": row[8]
  711. }
  712. res["data"].append( _d )
  713. res["result"] = "success"
  714. elif val["search_type"] == "user":
  715. t = val["search_string"]
  716. p = '%' + t + '%'
  717. fields = [ "username", "first_name", "last_name", "phone", "email", "address", "city","state", "zip", "created",
  718. "shipping_address", "shipping_city", "shipping_state", "shipping_zip", "shipping_name",
  719. "shipping_country_code", "shipping_country_name" ]
  720. query = "select userid, " + ", ".join(fields)
  721. query += " from users "
  722. query += " where " + " like %s or ".join(fields) + " like %s "
  723. query += " and active = 1 "
  724. query += " order by userid desc"
  725. query += " limit %s,%s "
  726. print("user query:", query)
  727. _v = [p]*len(fields)
  728. _v.append(int(start))
  729. _v.append(int(start) + int(count))
  730. #cursor.execute(query, [p]*len(fields))
  731. cursor.execute(query, _v)
  732. rows = cursor.fetchall()
  733. res["data"] = []
  734. for row in rows:
  735. _d = { "userid": row[0], "isuser": 1 }
  736. for idx,f in enumerate(fields):
  737. _d[f] = row[idx+1]
  738. res["data"].append( _d )
  739. res["result"] = "success"
  740. elif val["search_type"] == "admin":
  741. t = val["search_string"]
  742. p = '%' + t + '%'
  743. fields = [ "username", "comment" ]
  744. query = "select userid, group_id, " + ", ".join(fields)
  745. query += " from admins "
  746. query += " where " + " like %s or ".join(fields) + " like %s "
  747. query += " and active = 1 "
  748. query += " order by userid desc"
  749. query += " limit %s,%s "
  750. print("user query:", query)
  751. _v = [p]*len(fields)
  752. _v.append(int(start))
  753. _v.append(int(start) + int(count))
  754. cursor.execute(query, _v)
  755. rows = cursor.fetchall()
  756. res["data"] = []
  757. for row in rows:
  758. _d = { "userid": row[0], "group_id": row[1], "isadmin": 1 }
  759. for idx,f in enumerate(fields):
  760. _d[f] = row[idx+2]
  761. res["data"].append( _d )
  762. res["result"] = "success"
  763. return res
  764. ## _ _ _ _ _ _
  765. ## __ _ __| | __| | ___ __ _ _ __ __| | | |__ | | ___ ___| | __
  766. ## / _` |/ _` |/ _` | / __/ _` | '__/ _` | | '_ \| |/ _ \ / __| |/ /
  767. ## | (_| | (_| | (_| | | (_| (_| | | | (_| | | |_) | | (_) | (__| <
  768. ## \__,_|\__,_|\__,_| \___\__,_|_| \__,_| |_.__/|_|\___/ \___|_|\_\
  769. ##
  770. def AddCardBlock(db, ctx):
  771. res = {"result":"fail"}
  772. cursor = db.cursor()
  773. fields = ["mag_token", "rfid_token", "group_id",
  774. "rule", "nday_orig", "nrides_remain", "nrides_orig",
  775. "count",
  776. "pass_rule", "pass_nrides_remain", "pass_nrides_orig",
  777. "pass_nday_orig", "pass_class"]
  778. val = {}
  779. for f in fields:
  780. if f in ctx:
  781. if ctx[f] and len(ctx[f]) > 0:
  782. val[f] = ctx[f]
  783. if not ("count" in val):
  784. res["result"] = "fail"
  785. res["api_comment"] = "count must be positive"
  786. return res
  787. if ((not ("mag_token" in val)) and
  788. (not ("rfid_token" in val))):
  789. res["result"] = "fail"
  790. res["api_comment"] = "mag_token and/or rfid_token must be specified"
  791. return res
  792. use_mag = False
  793. use_rfid = False
  794. if "mag_token" in val:
  795. use_mag = True
  796. pfx_mag_cred = val["mag_token"].split(":")[0]
  797. base_mag_cred = int(val["mag_token"].split(":")[-1])
  798. if "rfid_token" in val:
  799. use_rfid = True
  800. pfx_rfid_cred = ":".join(val["rfid_token"].split(":")[0:2])
  801. base_rfid_cred = int(val["rfid_token"].split(":")[-1])
  802. logical_card_ids = []
  803. for idx in range(int(val["count"])):
  804. card_info = {"action":"add", "group_id":val["group_id"], "active":1}
  805. if "mag_token" in val: card_info["mag_token"] = val["mag_token"]
  806. if "rfid_token" in val: card_info["rfid_token"] = val["rfid_token"]
  807. if use_mag:
  808. card_info["mag_token"] = pfx_mag_cred + ":" + str(base_mag_cred + idx)
  809. if use_rfid:
  810. card_info["rfid_token"] = pfx_rfid_cred + ":" + str(base_rfid_cred + idx)
  811. card_res = Card(db, card_info)
  812. if card_res["result"] != "success":
  813. res["result"] = "fail"
  814. res["api_comment"] = "failed to add card"
  815. return res
  816. logical_card_id = card_res["logical_card_id"]
  817. if "pass_rule" in val:
  818. pass_info = {"action":"add", "logical_card_id":logical_card_id, "active":1,
  819. "rule" : val["pass_rule"] }
  820. if val["pass_class"] == "OTHER":
  821. pass
  822. elif val["pass_class"] == "NRIDE":
  823. pass_info["nrides_remain"] = val["pass_nrides_remain"]
  824. pass_info["nrides_orig"] = val["pass_nrides_orig"]
  825. elif val["pass_class"] == "NDAY":
  826. pass_info["nday_orig"] = val["pass_nday_orig"]
  827. pass_res = Pass(db, pass_info)
  828. logical_card_ids.append(logical_card_id)
  829. res["result"] = "success"
  830. res["logical_card_ids"] = logical_card_ids
  831. return res
  832. ## ____ _ _
  833. ## | _ \ ___ _ __ __| (_)_ __ __ _
  834. ## | |_) / _ \ '_ \ / _` | | '_ \ / _` |
  835. ## | __/ __/ | | | (_| | | | | | (_| |
  836. ## |_| \___|_| |_|\__,_|_|_| |_|\__, |
  837. ## |___/
  838. def Pending(db, ctx):
  839. res = {"result":"fail"}
  840. cursor = db.cursor()
  841. fields = ["org_card_order_queue_id", "userid", "logical_card_id", "created", "processed", "comment" ]
  842. val = {}
  843. for f in fields:
  844. if f in ctx:
  845. if (type(ctx[f]) == int) or (ctx[f] and ((type(ctx[f]) != int) and (len(ctx[f]) > 0))):
  846. val[f] = ctx[f]
  847. if not "comment" in val:
  848. val["comment"] = ''
  849. if (ctx["action"] == "getall"):
  850. query = "select org_card_order_queue_id, userid, logical_card_id, "
  851. query += " date_format(created, '%Y-%m-%d %H:%i:%S'), "
  852. query += " date_format(processed,'%Y-%m-%d %H:%i:%S'), comment, pending "
  853. query += " from org_card_order_queue "
  854. query += " where pending = 1 "
  855. query += " order by org_card_order_queue_id asc"
  856. cursor.execute(query)
  857. rows = cursor.fetchall()
  858. res["data"] = []
  859. for row in rows:
  860. _c = Card(db, {"action":"get", "logical_card_id":row[2]})
  861. mag_token = ""
  862. if "mag_token" in _c: mag_token = _c["mag_token"]
  863. rfid_token = ""
  864. if "rfid_token" in _c: rfid_token = _c["rfid_token"]
  865. _r = {"org_card_order_queue_id":row[0], "userid":row[1], "logical_card_id":row[2], "created": row[3],
  866. "processed":row[4], "comment":row[5], "pending":row[6],
  867. "mag_token":mag_token, "rfid_token":rfid_token}
  868. res["data"].append(_r)
  869. res["result"] = "success"
  870. if (ctx["action"] == "get"):
  871. query = "select org_card_order_queue_id, userid, logical_card_id, "
  872. query += " date_format(created, '%Y-%m-%d %H:%i:%S'), "
  873. query += " date_format(processed,'%Y-%m-%d %H:%i:%S'), comment, pending "
  874. query += " from org_card_order_queue "
  875. query += " where pending = 1 and org_card_order_queue_id = %s"
  876. cursor.execute(query, [val["org_card_order_queue_id"]])
  877. row = cursor.fetchone()
  878. if row is None:
  879. res["api_comment"] = "pending card not found"
  880. return res
  881. cols = ["org_card_order_queue_id", "userid", "logical_card_id", "created", "processed", "comment", "pending" ]
  882. res["pending_card"] = {}
  883. for idx,f in enumerate(cols):
  884. res["pending_card"][f] = row[idx]
  885. cid = res["pending_card"]["logical_card_id"]
  886. _r = CardInfo(db, {"action":"get", "logical_card_id":cid})
  887. if _r["result"] == "fail":
  888. return _r
  889. res["cardinfo"] = _r
  890. res["result"] = "success"
  891. elif (ctx["action"] == "add"):
  892. query = "insert into org_card_order_queue (userid, logical_card_id, comment, pending, created) values (%s,%s,%s,1,now())"
  893. cursor.execute(query, [val["userid"], val["logical_card_id"], val["comment"]])
  894. res["org_card_order_queue_id"] = cursor.lastrowid
  895. res["result"] = "success"
  896. elif (ctx["action"] == "process"):
  897. query = "update org_card_order_queue set pending = 0, processed = now() where org_card_order_queue_id = %s"
  898. cursor.execute(query, [val["org_card_order_queue_id"]])
  899. res["result"] = "success"
  900. db.commit()
  901. return res
  902. ## _
  903. ## _ __ ___ __ _(_)_ __
  904. ## | '_ ` _ \ / _` | | '_ \
  905. ## | | | | | | (_| | | | | |
  906. ## |_| |_| |_|\__,_|_|_| |_|
  907. ##
  908. def __test(db):
  909. print("main")
  910. print("---------")
  911. print("---------")
  912. print("---------")
  913. res = User(db, {"action":"add", "username":"abe" })
  914. print("user.add:", res)
  915. res = User(db, {"action":"update", "username":"abeabe", "userid":res["userid"]})
  916. print("user.update:", res)
  917. res = User(db, {"action":"get", "userid":res["userid"]})
  918. print("user.get:", res)
  919. res = User(db, {"action":"delete", "userid": res["userid"]})
  920. print("user.delete:", res)
  921. print("---------")
  922. print("---------")
  923. print("---------")
  924. res = Card(db, {"action":"get", "logical_card_id":1})
  925. print("card.get:", res)
  926. res = Card(db, {"action":"add", "mag_token":"2:1234", "rfid_token":"26:20:415", "comment":"testing api", "userid":1})
  927. print("card.add:", res)
  928. res = Card(db, {"action":"update", "mag_token":"2:9234", "logical_card_id":res["logical_card_id"]})
  929. print("card.update:", res)
  930. res = Card(db, {"action":"delete", "logical_card_id":res["logical_card_id"]})
  931. print("card.delete:", res)
  932. print("---------")
  933. print("---------")
  934. print("---------")
  935. res = Pass(db, {"action":"get", "user_pass_id":11})
  936. print("pass.get:", res)
  937. res = Pass(db, {"action":"add", "logical_card_id":1, "queue_order":9, "rule":"TEST-ORG-NDAY", "nday_orig":3})
  938. print("pass.add:", res)
  939. res = Pass(db, {"action":"update", "user_pass_id":res["user_pass_id"], "queue_order":10, "rule":"TEST-ORG-NDAY", "nday_orig":5})
  940. print("pass.update:", res)
  941. res = Pass(db, {"action":"delete", "user_pass_id":res["user_pass_id"]})
  942. print("pass.delete:", res)
  943. print("---------")
  944. print("---------")
  945. print("---------")
  946. res = UserInfo(db, {"userid":348})
  947. print("userinfo:", json.dumps(res, indent=2))
  948. print("---------")
  949. print("---------")
  950. print("---------")
  951. res = Request({"function":"CardInfo", "action":"search", "logical_card_id":1})
  952. print("request.card.search:", res)
  953. def show_version(ofp):
  954. ofp.write("PopufareAPI version " + POPUFARE_API_VERSION + "\n")
  955. ofp.flush()
  956. def show_help(ofp):
  957. show_version(ofp)
  958. ofp.write("\nusage:\n")
  959. ofp.write("\n")
  960. ofp.write(" PopufareAPI [-h] [-v] [-c config] [-u dbuser] [-p dbpass] [-H dbhost] [-D db] [-P dbport] <json_data>\n")
  961. ofp.write("\n")
  962. ofp.write(" -c config file\n")
  963. ofp.write(" -u db user\n")
  964. ofp.write(" -p db pass\n")
  965. ofp.write(" -H db host\n")
  966. ofp.write(" -D db name\n")
  967. ofp.write(" -P db port\n")
  968. ofp.write(" -h help (this screen)\n")
  969. ofp.write(" -v show version\n")
  970. ofp.write("\n")
  971. ofp.write("example:\n")
  972. ofp.write("\n")
  973. ofp.write(" PopufareAPI -d '{\"function\":\"ping\"}\n")
  974. ofp.write("\n")
  975. ofp.flush()
  976. if __name__ == "__main__":
  977. api_opt = {
  978. "db_info": {
  979. "user":"busapi",
  980. "pass":"bus",
  981. "host":"localhost",
  982. "db":"busdb",
  983. "port":5506
  984. },
  985. "config":"",
  986. "data":""
  987. }
  988. data = {}
  989. opts,args = getopt.getopt(sys.argv[1:], "hd:u:p:H:D:P:c:")
  990. for o,a in opts:
  991. if o == "-h":
  992. show_help(sys.stdout)
  993. sys.exit(0)
  994. elif o == "-c":
  995. api_opt["config"] = a
  996. elif o == "-u":
  997. api_opt["db_info"]["user"] = a
  998. elif o == "-p":
  999. api_opt["db_info"]["pass"] = a
  1000. elif o == "-H":
  1001. api_opt["db_info"]["host"] = a
  1002. elif o == "-D":
  1003. api_opt["db_info"]["db"] = a
  1004. elif o == "-P":
  1005. api_opt["db_info"]["port"] = int(a)
  1006. #elif o == "-d":
  1007. # api_opt["data"] = json.loads(a)
  1008. else:
  1009. show_help(sys.stderr)
  1010. sys.exit(-1)
  1011. if len(args) < 1:
  1012. sys.stderr.write("no data request\n")
  1013. show_help(sys.stderr)
  1014. sys.exit(-1)
  1015. api_opt["data"] = json.loads(args[0])
  1016. res = Request(api_opt["data"], api_opt["db_info"])
  1017. print(json.dumps(res,indent=2))
  1018. #conn = mysql.connector.connect(user=_USER, password=_PASSWORD, host=_HOST, database=_DATABASE, port=_PORT)
  1019. #conn = mysql.connector.connect(user=api_opt["dbuser"], password=api_opt["dbpass"], host=api_opt["dbhost"], database=api_opt["db"], port=api_opt["dbport"])
  1020. #main(conn)
  1021. #conn.close()