From 2245baef3e1f7dda0d823ee2e80eaef798f54d34 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 13:43:45 -0500 Subject: [PATCH 01/30] make database path portable --- mocha_server.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mocha_server.py b/mocha_server.py index 536f204..bc7729d 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -4,11 +4,13 @@ Handle API requests to the database """ import json import sqlite3 +from os import path # NOTE: closing a database makes the next query in a script not work -# DATABASE = '/usr/local/www/mocha-server/mocha.db' -DATABASE = 'mocha.db' +DATABASE = '/usr/local/www/mocha-server/mocha.db' +if not path.exists(DATABASE): + DATABASE = 'mocha.db' conn = sqlite3.connect(DATABASE) conn.row_factory = sqlite3.Row From f33de9618d18b5c9566d40b1d45895a935265913 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 13:51:36 -0500 Subject: [PATCH 02/30] cleanup --- "\\" | 104 --------------- manage.py | 15 --- mochaserver/__init__.py | 0 .../__pycache__/__init__.cpython-36.pyc | Bin 136 -> 0 bytes .../__pycache__/settings.cpython-36.pyc | Bin 2253 -> 0 bytes mochaserver/__pycache__/urls.cpython-36.pyc | Bin 915 -> 0 bytes mochaserver/__pycache__/wsgi.cpython-36.pyc | Bin 547 -> 0 bytes mochaserver/settings.py | 120 ------------------ mochaserver/urls.py | 21 --- mochaserver/wsgi.py | 16 --- 10 files changed, 276 deletions(-) delete mode 100644 "\\" delete mode 100755 manage.py delete mode 100644 mochaserver/__init__.py delete mode 100644 mochaserver/__pycache__/__init__.cpython-36.pyc delete mode 100644 mochaserver/__pycache__/settings.cpython-36.pyc delete mode 100644 mochaserver/__pycache__/urls.cpython-36.pyc delete mode 100644 mochaserver/__pycache__/wsgi.cpython-36.pyc delete mode 100644 mochaserver/settings.py delete mode 100644 mochaserver/urls.py delete mode 100644 mochaserver/wsgi.py diff --git "a/\\" "b/\\" deleted file mode 100644 index fd0ee7e..0000000 --- "a/\\" +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env python3 -""" -Handle API requests to the database -""" -import json -import sqlite3 - - -# DATABASE = '/usr/local/www/mocha-server/mocha.db' -DATABASE = 'mocha.db' - -conn = sqlite3.connect(DATABASE) -conn.row_factory = sqlite3.Row -cursor = conn.cursor() - - - -# TODO: Add fetching of list of users -# TODO: Add fetching of top N users by score -# TODO: Add ability to store and retrieve avatars (as image files?) - -def get_top_N(search_parameter, N): - """ - - """ - cursor.execute("select * from users order by p0 desc limit p1", {'p0':search_parameter, 'p1':N}) - conn.commit() - conn.close() - - -# add new parameters as needed -def update_row(user_id, updated_username): - print() - -def insert_row(user_id, username): - """ - Inserts a row for a NEW user with given parameters - This may work better with AUTOINCREMENT to avoid arbitrary ids and duplicates - """ - - cursor.execute("insert into users values (?,?)", (user_id, username)) - conn.commit() - conn.close() - - -def fetch_user(user_id): - """ - Returns a JSON object containing the requested user - Also can return a list of all users if user_id == "*" - """ - - if user_id != '*': # must use (?), (item,) format - cursor.execute("SELECT * FROM users WHERE user_id=(?)", (user_id,)) - else: - cursor.execute("SELECT * FROM users") - - output = cursor.fetchall() - output = json.dumps([dict(row) for row in output]) - conn.close() - - if output == '[]': - output = None - - return output - - -def process_request(uri): - """ - Handles the API endpoint. - Currently supports: - - /mocha/users/"user_id" Returns JSON of the specified user - - /mocha/users/* Returns JSON list of all users - """ - parts = uri.split('/')[1:] - assert parts[0] == 'mocha' - - output = None - - if parts[1] == 'users': - output = fetch_user(parts[2]) - - return output - - - -def application(environ, start_response): - """ - mod_wsgi entry point - """ - status = '200 OK' - output = process_request(environ['REQUEST_URI']) - - if output is None: - status = '404 Not Found' - output = '' - - output = output.encode('UTF-8') - - response_headers = [('Content-type', 'application/json'), - ('Content-Length', str(len(output)))] - - start_response(status, response_headers) - - return [output] diff --git a/manage.py b/manage.py deleted file mode 100755 index 72b3b98..0000000 --- a/manage.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mochaserver.settings") - try: - from django.core.management import execute_from_command_line - except ImportError as exc: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) from exc - execute_from_command_line(sys.argv) diff --git a/mochaserver/__init__.py b/mochaserver/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/mochaserver/__pycache__/__init__.cpython-36.pyc b/mochaserver/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 741e871b21fe81698e31264dcacc39d80264d90a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136 zcmXr!<>it)emaT)2p)q77+?f49Dul(1xTbY1T$zd`mJOr0tq9CU#9vQ`MIh3S;^_8 zmHHu$zM=ZL`NM!O=7 zUilmRf&P-twEsY_ed=H6Q;%dj@i@#h^;)adxqRpB(RWslmzS%ze*e>0U&`hF%FX;L zxZcK#{ectYj2z^l;O31y$qx&|;>;|Ng%A037$qngWe|*tlQR}!0ZLGY3M@j^DH&Bb z2TQOFD{vlG;et~(&cQ`v2`)J~5MT{1!xgv+*BpW7;j@o|lQ)*(y0HQ`;AR%$_LP5)I+_N6! zbGd4LX!%YcGQv6a9VYgJQ1k+OU@;QDC!shBgCViGRITn3pM(}CARZiv_aO7zw7{dj z$hZ~qiRC>pVu&P8fFDT@q%HJ*nc;>+q&|l5EKUPo#6KTU3`$+{E#^R~1I|b6ds&9S zX3{vuGy^;EWa7&RDDz-MZ6;5XmdEgXK*WCJx}r-N7lZ!ErYs*Y5TT*?-f|pm<45=3A3X_fTg<)xZX3u$LZ08TkDm77aC__SZQK84_-t$BJb&s~^qD`r zyEWLNdb~E?k7Nfv59xuF)h@m?FOB#hzA-0EU5L;5(TK44>YS1h#;}^F6&@-q2R*LO zdvL73omQR2n-iT!0bFuug+yY+jzY?hByF;29qZz)d8kQnX~th0%VxYbP6xr)=>@)2 z%WnT+%EG=>!|4qrhcl*6=W?;IbhCLV_>5JfcE+|D$N5c63pMN98W(TI>&YiJ;ZpnU z(6ZkmsSu9VGIPpd$5X6Tm<)wPoUHU}LjEflo ze&r@MgN=y=A&c+NgPgXJfE+|l{Ph_uL-GzAgU8=YY>!Er*7Os|X|3bObH>MY{Di|) zw;?3u9i{eKZPyK<-soycMcLakvsJmXr#CunExtKhvP0%j$r_EY*4c|p%0T*7OXFly-AKI0NZB;Xh$9i zseKw@f%uydGm1UE7FT3E&#`OdjcZBSH1Q}l&7=^pL>O6ofYqm=k3>(()&a{T71K=5 z64Nv)JBp^J?=q=sYOSm4=4S`$|_$mb#seZuYv(TBrRoS=80mUQ^LkEm_^yb{naEvr$u0t|Y=RpnkGgS9B!} zt|ix$p8m?*Q#5VA)2*9tlx72!9VFUvQ)%z^lwH-Vb?Pd@Hd?A_bdVO69;V%R_^dr% zH6K1`C#6)T8@Ll4Bh}Jue#?dHNS$o@=R0{2ctjrBPIM&eN~@rkIbE++OT~0l KE9gHSrGEkEh56h7 diff --git a/mochaserver/__pycache__/urls.cpython-36.pyc b/mochaserver/__pycache__/urls.cpython-36.pyc deleted file mode 100644 index 6eff192a1a3b5bb80559cbedde9f4b3c937d16b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 915 zcma)5!EV$r5KXe_wxlim0rD*yX&blYwgN;|QBesvKzqq0^~T;LtJscgr?6c48oq=B ze~>FDj$Ao0_GYCB38|4{jh*qE_ufpN9Un(ezyHX72cGxS8$A<Mnw?jTUxFu+He zu|J~Ts(CAwSs=7hk|Vy!N!t#Gqx|zB-$@1mBZY7k#8$I7o)5Yz$=MtU z!r6=1#&Hr|Hbm=_B?zM<)c>)NreewxNu5zUI&T2Wv40>No1YS$Taa1u52I^QG##x) z2`0FDR$2kg-1&3uj6pyJ+GBIsEJ3Tm%Ei{MZ97tH_>Sa|J9nl$nC-HWB?bd`m129~ zaN{J2e!;kUiQn%L%4-2ZXdVADbzA&CIq3KE9Ofg>v&RD^P-0ZQR4}n#e>mc)>`%~# zx;Cs2E%E+v)XZ<5dIaCy^5+cO?%{P&b{l+i@$MG8;Xm0<|8_IP7AS2u{lRlCY1goq PPXI+vVF~>s|7h|TDN!E< diff --git a/mochaserver/__pycache__/wsgi.cpython-36.pyc b/mochaserver/__pycache__/wsgi.cpython-36.pyc deleted file mode 100644 index 911b8d1a6b68eba0abfd9048b2716a40c3c2cd04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 547 zcmYjNv2NQi5Tz(5Hq@r*5OfT<8fe6{-HM_}fFy>4#2M_MfEJk*&oTvy6iC{sv*rW( z9sQCH{R3vq)L-aST516k;PH-k_uj+%aBz?u{rr`Ej}UrBJ0}kLX}Is z(G?UfO_H%A@U77nEOAR9-4c_MeU(6%B`oamm4ssnYmj8kjA&bm)xgPo&YDJwf;pkp zd>-&FTP6cUs6bk`4Wd;57nUr9gtx>3BunQS`;k(v3!CzasZ#&fROp(5qVLlay43eh zQx1*Pn;Mj(_qG%?`5i1llVRi!OK|!2FW-^j-<)5aO-8r*^kOy}Pe#-H=Jx#V`r_em z$AQ#>b3&DN*!TTB4>I!HV{QHGPE1;81L+pp;6Ud4pw_}@<&&_;VZm0?`4>B^Kafen zU>p9t3(9MtRZ*@sbar-gM_cOS&i$_Vmv1T3X5$B+I-fre0*c*+9v Date: Tue, 17 Apr 2018 13:55:57 -0500 Subject: [PATCH 03/30] Remove __pycache__ --- .gitignore | 1 + __pycache__/mocha.cpython-36.pyc | Bin 455 -> 0 bytes __pycache__/mocha_server.cpython-36.pyc | Bin 3262 -> 0 bytes 3 files changed, 1 insertion(+) delete mode 100644 __pycache__/mocha.cpython-36.pyc delete mode 100644 __pycache__/mocha_server.cpython-36.pyc diff --git a/.gitignore b/.gitignore index f7275bb..93526df 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ venv/ +__pycache__/ diff --git a/__pycache__/mocha.cpython-36.pyc b/__pycache__/mocha.cpython-36.pyc deleted file mode 100644 index 402920d54cd2307c308034928447fe0b9e7b0069..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 455 zcmYjOO-sWt7*5iz8{G#YUc7k{dYB!05fMKaQ$(G(t(RU#X=2;aB_&CR9o?z^8GniY zp;u4-g`G^NgT3%Rd7nJV!xM7WZkzY5FZUV(@C|kfsrg2=I8cBAhB0sfMp*j*F2ryQ zEd56*VCYvTtE&BXU#;Ro0fs9iL9T8cf&$JU)(W`Na2*>GNR%s9z&8}2m7c>K<$8f2 z$hDyIZVZ>kX|H$m^5`~4gI8xTo=lz5!?NZ~?z-peR-aKxsO-utA!}2TcWEcF7tv*B zt2_}J$k0N(ipMntmR4#)^3ovGXPA^)OsG{W>x3>M&ZyfF(&KWG6@)Jc|05?!RyMsPi6h^W v5u;Tg(NX}Q4)+mKZ6aQ6SV+ap#Ae?3??6-OW#e+ra2k^<^<_X*&ugGx1|e}j diff --git a/__pycache__/mocha_server.cpython-36.pyc b/__pycache__/mocha_server.cpython-36.pyc deleted file mode 100644 index 7c2ae9ef713aae005855784574b82525aadade22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3262 zcmbtWTXWmS6~-l4A8X#mru?*R$t*=R4=4<>m0pjekUc zUUHm&I@dl5{ZF9i3@YwK4kw%rNJMN)Bg);#6}0aOcS<53dKKZftHSRGQzr_z$Ng`d zXn|LF721#od;!`;9`Z$Km-rH2hIW~+@H^11@EU&$+B=*cI`!53gYdIB;{(CAf4|3+ zcr_NW$P9L5zp&c@%#F-HIr&H%wu;x|x!ZB{&rklxC+rp@i*c@lZUuzg3?Lhnhv<4pF#wtZ)f-TcPY%TAbY?4a`qj&NwWuUx zrKKvW;%kwNjVOYSFrfdzpz!)yW`)nk!;vn$L}nSRmcwCc>Ta=&2ct6Dcw$kJV>23? z$Q^5S^)6KbNs5KJ9PgrZt57+_BOzHKHTn(-+<dCNXf1e zT5p64J7c9pW(H>uEKmWq>Wpb2h8DZ0QXM~FM`HtsHe;2gS(n8OZOph7nq|_!VZxe; zNyUZQuy?fbpWg;CyMi5^u`R}hPS_v?MYFAZ{XZ;@uh#x=n6zf96NNgjQ-$hQK2~#B zTJxzYjQtYI4Ojs{+s%Ikt7g5b0z7E}s}QS2tbmjpwT#sYRCO<^X%VZW*B-?RkYt34 zJbZgWnyQA&Zc0)D$=-Y)W=kOX0a>Lr8n|;Hy$y2-?!*sUq!CLUD2Z{<@j19_oixEI z{gFyD^vWs%x}EZb32t2n5&nHK!g4_yT=)|>(dxw^3c-Ej-Z_BLDJ@J1z?780 zHMH+qNsuAk{M&G0F9QL97GnknvyN2IYc^h3)?ufq>9KBlA^@N>W$AFC)k`%S#%Bz{ z$c}^o9hPgipSSk+nmY%LFB;7jQf+)9Q_f;_lo}NSj#Ca-Wt{QxXpkoGU^p(nms%kh zs6<{;-$^_Gx@Pyc9&FW{b?=&CuPjN`58#PWFyn+bG*%vq8rwyUp=O*)ji?_&djquy zMr*4-KxYC`>eyB9&78g@_$(}4eo6>x6{sG#7D>Kz_S;Avo&5luy(IY{Kedi{VBHoK z{@cU-CX+{ff&!4026HF5fY zkSSy`-2Il`wLzSFcOC8@Q@v)$UkG>4DTEG~vxNWD;9vDFqyaFFo)_eQx0WgM6X10) zIVLG0<0A=PP*V0h5HLL+jU;&KZ|p7%84sb9;d#@>+Re2~B3Z*(#%3rhG1jp+=#fa$ zPI?_wxb%&q)^YUeyK&STn6naHkj>Y~cTI(LbeeD;8>-R4PIVQ`=g}pyMAc8=e@=!NRj^pY zHKD^n{g56xXc#aB#9;0o2a0j;oN^!kA)~9{3?!Hfh8h^b%&kMa^xT_3t~c>)uIF|! zYg^3k*~bqhZ=dRJ3f42gaMpl!BCq`7(IdA1yJ+#C@#XWzVXOW8V6T}kfBfiU)`T$q zOpY^NIJy74_3YuVFP3(I1K^B@=4>P`YVoLKNYK Date: Tue, 17 Apr 2018 13:59:39 -0500 Subject: [PATCH 04/30] Remove extra file --- .env | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index e69de29..0000000 From dc355b5600034e237db11d47849d96b05a91c5a1 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:00:10 -0500 Subject: [PATCH 05/30] Remove extra file --- pip-selfcheck.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 pip-selfcheck.json diff --git a/pip-selfcheck.json b/pip-selfcheck.json deleted file mode 100644 index 72a077b..0000000 --- a/pip-selfcheck.json +++ /dev/null @@ -1 +0,0 @@ -{"last_check":"2018-04-06T05:49:45Z","pypi_version":"9.0.3"} \ No newline at end of file From 17c2f61217d47c24651917c0da51ad9e1a727276 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:00:10 -0500 Subject: [PATCH 06/30] Remove extra file --- pip-selfcheck.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 pip-selfcheck.json diff --git a/pip-selfcheck.json b/pip-selfcheck.json deleted file mode 100644 index 72a077b..0000000 --- a/pip-selfcheck.json +++ /dev/null @@ -1 +0,0 @@ -{"last_check":"2018-04-06T05:49:45Z","pypi_version":"9.0.3"} \ No newline at end of file From 03e386998764032faf16e697a57a5f72d99810ef Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:07:14 -0500 Subject: [PATCH 07/30] oops wrong thing --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 93526df..71d4498 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ venv/ __pycache__/ +pip-selfcheck.json From 9ac52676a4d50500186612e0606f22c020b6c855 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:07:22 -0500 Subject: [PATCH 08/30] no more django --- requirements.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index e7042e2..e69de29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +0,0 @@ -Django==2.0.4 -django-environ==0.4.4 -pytz==2018.3 -six==1.11.0 From c43d136344f5903378ee6094916d291f3c369593 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:09:11 -0500 Subject: [PATCH 09/30] Script doesn't even work --- RUN_THIS_TO_SETUP_VENV.sh | 5 ----- 1 file changed, 5 deletions(-) delete mode 100755 RUN_THIS_TO_SETUP_VENV.sh diff --git a/RUN_THIS_TO_SETUP_VENV.sh b/RUN_THIS_TO_SETUP_VENV.sh deleted file mode 100755 index 06ecb8f..0000000 --- a/RUN_THIS_TO_SETUP_VENV.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/bash - -set -e -source "./venv/bin/activate" -pip install -Ur ./requirements.txt From f6014749e1fa81a5f8ca9a9b7f3f87a41283fde9 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:10:47 -0500 Subject: [PATCH 10/30] Add README --- README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..b21d076 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +API endpoints: + +```/mocha/users/{user_id}``` From 5e9098f1e9c004be79964a00ece36e5c423d15ea Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:13:52 -0500 Subject: [PATCH 11/30] Update documentation --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b21d076..07db75b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ -API endpoints: +# API endpoints: -```/mocha/users/{user_id}``` +| Endpoint | Return | +| ------------------------ | ----------------------- | +| `/mocha/users/{user_id}` | row with the given user | +| `/mocha/users/*` | all rows | From 0f35b91e8e1d702e6b37f6819519e0cdd2500c29 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:24:04 -0500 Subject: [PATCH 12/30] Add endpoint for get_top_N --- README.md | 9 +++++---- mocha_server.py | 11 +++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 07db75b..1335d4b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # API endpoints: -| Endpoint | Return | -| ------------------------ | ----------------------- | -| `/mocha/users/{user_id}` | row with the given user | -| `/mocha/users/*` | all rows | +| Endpoint | Return | +| ------------------------ | ------------------------------ | +| `/mocha/users/{user_id}` | row with the given user | +| `/mocha/users/*` | all rows | +| `/mocha/top/{n}` | user with the n highest scores | diff --git a/mocha_server.py b/mocha_server.py index bc7729d..fbcd6ec 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -104,6 +104,15 @@ def fetch_user(user_id): return output +def fetch_top_n(n): + output = get_top_N('score', True, n) + + if output == '[]': + output = None + + return output + + def process_request(uri): """ Handles the API endpoint. @@ -118,6 +127,8 @@ def process_request(uri): if parts[1] == 'users': output = fetch_user(parts[2]) + elif parts[1] == 'top': + output = fetch_top_n(parts[2]) return output From d02aaf2495d54ef1b22add78f1af6593ce7526b5 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:25:01 -0500 Subject: [PATCH 13/30] Add base url --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 1335d4b..c741398 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # API endpoints: +Base url = `https://corder.tech` + + | Endpoint | Return | | ------------------------ | ------------------------------ | | `/mocha/users/{user_id}` | row with the given user | From 189901033a5cf0298c48aa2712a15d3d20b1c07c Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:26:30 -0500 Subject: [PATCH 14/30] Add shebang to test script --- test.py | 1 + 1 file changed, 1 insertion(+) mode change 100644 => 100755 test.py diff --git a/test.py b/test.py old mode 100644 new mode 100755 index 9cf54e4..45e32b4 --- a/test.py +++ b/test.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 import mocha_server #mocha_server.insert_row(4, 'andrew') From 9f2cc875f9d1149eeaf111092c2a932a7da2c0c7 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:30:38 -0500 Subject: [PATCH 15/30] Add check for partial endpoints --- mocha_server.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mocha_server.py b/mocha_server.py index fbcd6ec..dac35b8 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -125,6 +125,9 @@ def process_request(uri): output = None + if len(parts) < 2: + return None + if parts[1] == 'users': output = fetch_user(parts[2]) elif parts[1] == 'top': From db1b48ee02c1c071902431103d12a4884d7403d3 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:35:29 -0500 Subject: [PATCH 16/30] Improve error handling --- mocha_server.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/mocha_server.py b/mocha_server.py index dac35b8..21a2c3a 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -106,10 +106,6 @@ def fetch_user(user_id): def fetch_top_n(n): output = get_top_N('score', True, n) - - if output == '[]': - output = None - return output @@ -126,12 +122,13 @@ def process_request(uri): output = None if len(parts) < 2: - return None - - if parts[1] == 'users': + output = None + elif parts[1] == 'users': output = fetch_user(parts[2]) elif parts[1] == 'top': output = fetch_top_n(parts[2]) + else: + output = None return output From 1df68994724f04c020f4e9c81269c6fca5e77a41 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:37:05 -0500 Subject: [PATCH 17/30] Improve documentation --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c741398..07876c4 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,9 @@ Base url = `https://corder.tech` -| Endpoint | Return | -| ------------------------ | ------------------------------ | -| `/mocha/users/{user_id}` | row with the given user | -| `/mocha/users/*` | all rows | -| `/mocha/top/{n}` | user with the n highest scores | +| Endpoint | Return | Implemented? | +| ---------------------------------- | ------------------------------ | ------------ | +| `/mocha/users/{user_id}` | row with the given user | Yes | +| `/mocha/users/*` | all rows | Yes | +| `/mocha/top/{n}` | user with the n highest scores | No | +| `/mocha/set/{user_id}/{num_steps}` | 200 OK if successful | No | From cc17637c993d2d28d272cb5f7a5d69db72fc3db3 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:38:59 -0500 Subject: [PATCH 18/30] Close cursor each time --- mocha_server.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/mocha_server.py b/mocha_server.py index 21a2c3a..e4bbcd1 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -6,17 +6,10 @@ import json import sqlite3 from os import path -# NOTE: closing a database makes the next query in a script not work - DATABASE = '/usr/local/www/mocha-server/mocha.db' if not path.exists(DATABASE): DATABASE = 'mocha.db' -conn = sqlite3.connect(DATABASE) -conn.row_factory = sqlite3.Row -cursor = conn.cursor() - - # TODO: Add fetching of list of users # TODO: Add fetching of top N users by score # TODO: Add ability to store and retrieve avatars (as image files?) @@ -26,6 +19,8 @@ def get_users(username_list): Gets a list of users searching by name. This can also easily be done by user_id. """ + conn = sqlite3.connect(DATABASE) + cursor = conn.cursor() output = [] for usr in username_list: cursor.execute("select * from users where username=(?)", (usr,)) @@ -33,7 +28,7 @@ def get_users(username_list): output = json.dumps([dict(row) for row in output]) conn.commit() - #conn.close() + conn.close() if output == '[]': output = None @@ -48,6 +43,8 @@ def get_top_N(search_parameter, desc, N): Store an orderd id list and put that in the json? """ + conn = sqlite3.connect(DATABASE) + cursor = conn.cursor() if desc == True: cursor.execute("select * from users order by ? desc limit ?", (search_parameter, N)) else: @@ -58,7 +55,7 @@ def get_top_N(search_parameter, desc, N): output = json.dumps([dict(row) for row in output]) conn.commit() - #conn.close() + conn.close() if output == '[]': output = None @@ -77,9 +74,11 @@ def insert_row(user_id, username): This may work better with AUTOINCREMENT to avoid arbitrary ids and duplicates """ + conn = sqlite3.connect(DATABASE) + cursor = conn.cursor() cursor.execute("insert into users values (?,?)", (user_id, username)) conn.commit() - #conn.close() + conn.close() def fetch_user(user_id): @@ -87,7 +86,8 @@ def fetch_user(user_id): Returns a JSON object containing the requested user Also can return a list of all users if user_id == "*" """ - + conn = sqlite3.connect(DATABASE) + cursor = conn.cursor() if user_id != '*': # must use (?), (item,) format cursor.execute("SELECT * FROM users WHERE user_id=(?)", (user_id,)) else: @@ -96,7 +96,7 @@ def fetch_user(user_id): output = cursor.fetchall() output = json.dumps([dict(row) for row in output]) conn.commit() - #conn.close() + conn.close() if output == '[]': output = None From 29774d6574717b60cdcfea87ca71329dc19dd66c Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:41:13 -0500 Subject: [PATCH 19/30] Add row_factory --- mocha_server.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mocha_server.py b/mocha_server.py index e4bbcd1..727ee61 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -20,6 +20,7 @@ def get_users(username_list): This can also easily be done by user_id. """ conn = sqlite3.connect(DATABASE) + conn.row_factory = sqlite3.Row cursor = conn.cursor() output = [] for usr in username_list: @@ -44,6 +45,7 @@ def get_top_N(search_parameter, desc, N): Store an orderd id list and put that in the json? """ conn = sqlite3.connect(DATABASE) + conn.row_factory = sqlite3.Row cursor = conn.cursor() if desc == True: cursor.execute("select * from users order by ? desc limit ?", (search_parameter, N)) @@ -75,6 +77,7 @@ def insert_row(user_id, username): """ conn = sqlite3.connect(DATABASE) + conn.row_factory = sqlite3.Row cursor = conn.cursor() cursor.execute("insert into users values (?,?)", (user_id, username)) conn.commit() @@ -87,6 +90,7 @@ def fetch_user(user_id): Also can return a list of all users if user_id == "*" """ conn = sqlite3.connect(DATABASE) + conn.row_factory = sqlite3.Row cursor = conn.cursor() if user_id != '*': # must use (?), (item,) format cursor.execute("SELECT * FROM users WHERE user_id=(?)", (user_id,)) From a38d1f4409aa41f82b91af048eb2aa125f08673c Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:50:46 -0500 Subject: [PATCH 20/30] Add vim modeline --- mocha_server.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/mocha_server.py b/mocha_server.py index 727ee61..7a5a47a 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -14,6 +14,7 @@ if not path.exists(DATABASE): # TODO: Add fetching of top N users by score # TODO: Add ability to store and retrieve avatars (as image files?) + def get_users(username_list): """ Gets a list of users searching by name. @@ -24,7 +25,7 @@ def get_users(username_list): cursor = conn.cursor() output = [] for usr in username_list: - cursor.execute("select * from users where username=(?)", (usr,)) + cursor.execute("select * from users where username=(?)", (usr, )) output += cursor.fetchall() output = json.dumps([dict(row) for row in output]) @@ -48,9 +49,11 @@ def get_top_N(search_parameter, desc, N): conn.row_factory = sqlite3.Row cursor = conn.cursor() if desc == True: - cursor.execute("select * from users order by ? desc limit ?", (search_parameter, N)) + cursor.execute("select * from users order by ? desc limit ?", + (search_parameter, N)) else: - cursor.execute("select * from users order by ? asc limit ?", (search_parameter, N)) + cursor.execute("select * from users order by ? asc limit ?", + (search_parameter, N)) output = cursor.fetchall() #print(output) @@ -72,9 +75,9 @@ def update_row(user_id, updated_username): def insert_row(user_id, username): """ - Inserts a row for a NEW user with given parameters - This may work better with AUTOINCREMENT to avoid arbitrary ids and duplicates - """ + Inserts a row for a NEW user with given parameters + This may work better with AUTOINCREMENT to avoid arbitrary ids and duplicates + """ conn = sqlite3.connect(DATABASE) conn.row_factory = sqlite3.Row @@ -92,8 +95,8 @@ def fetch_user(user_id): conn = sqlite3.connect(DATABASE) conn.row_factory = sqlite3.Row cursor = conn.cursor() - if user_id != '*': # must use (?), (item,) format - cursor.execute("SELECT * FROM users WHERE user_id=(?)", (user_id,)) + if user_id != '*': # must use (?), (item,) format + cursor.execute("SELECT * FROM users WHERE user_id=(?)", (user_id, )) else: cursor.execute("SELECT * FROM users") @@ -156,3 +159,5 @@ def application(environ, start_response): start_response(status, response_headers) return [output] + +# vim: tabstop=4 shiftwidth=4 softtabstop=4 expandtab From bd461c9be99eb12372867bd4af431b5893f660c9 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:57:52 -0500 Subject: [PATCH 21/30] Add testing functionality to main file --- mocha_server.py | 5 +++++ test.py | 13 ------------- 2 files changed, 5 insertions(+), 13 deletions(-) delete mode 100755 test.py diff --git a/mocha_server.py b/mocha_server.py index 7a5a47a..ee4b72a 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -160,4 +160,9 @@ def application(environ, start_response): return [output] +if __name__ == '__main__': + USERNAMES = ['andrew', 'Corder'] + print(get_users(USERNAMES)) + + # vim: tabstop=4 shiftwidth=4 softtabstop=4 expandtab diff --git a/test.py b/test.py deleted file mode 100755 index 45e32b4..0000000 --- a/test.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python3 -import mocha_server - -#mocha_server.insert_row(4, 'andrew') - -#print(mocha_server.fetch_user(3)) - -#print(mocha_server.get_top_N('user_id', False, 3)) - -username_list = ['andrew', 'Corder'] - -print(mocha_server.get_users(username_list)) - From c4a23c02f487f8c3b3ff0432feaf0e3ee777bb64 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 14:59:35 -0500 Subject: [PATCH 22/30] Make quotes consistent, misc. pylint --- mocha_server.py | 65 +++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/mocha_server.py b/mocha_server.py index ee4b72a..fb10108 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -""" +''' Handle API requests to the database -""" +''' import json import sqlite3 from os import path @@ -16,16 +16,16 @@ if not path.exists(DATABASE): def get_users(username_list): - """ + ''' Gets a list of users searching by name. This can also easily be done by user_id. - """ + ''' conn = sqlite3.connect(DATABASE) conn.row_factory = sqlite3.Row cursor = conn.cursor() output = [] for usr in username_list: - cursor.execute("select * from users where username=(?)", (usr, )) + cursor.execute('select * from users where username=(?)', (usr, )) output += cursor.fetchall() output = json.dumps([dict(row) for row in output]) @@ -38,22 +38,22 @@ def get_users(username_list): return output -def get_top_N(search_parameter, desc, N): - """ +def get_top_n(search_parameter, desc, num): + ''' In progress. Currently the query seems to work, but returning a dict does not preserve order. Store an orderd id list and put that in the json? - """ + ''' conn = sqlite3.connect(DATABASE) conn.row_factory = sqlite3.Row cursor = conn.cursor() - if desc == True: - cursor.execute("select * from users order by ? desc limit ?", - (search_parameter, N)) + if desc: + cursor.execute('select * from users order by ? desc limit ?', + (search_parameter, num)) else: - cursor.execute("select * from users order by ? asc limit ?", - (search_parameter, N)) + cursor.execute('select * from users order by ? asc limit ?', + (search_parameter, num)) output = cursor.fetchall() #print(output) @@ -69,36 +69,40 @@ def get_top_N(search_parameter, desc, N): # add new parameters as needed -def update_row(user_id, updated_username): +def update_row(_user_id, _updated_username): + ''' + WIP. + Will add a new user. + ''' print() def insert_row(user_id, username): - """ + ''' Inserts a row for a NEW user with given parameters This may work better with AUTOINCREMENT to avoid arbitrary ids and duplicates - """ + ''' conn = sqlite3.connect(DATABASE) conn.row_factory = sqlite3.Row cursor = conn.cursor() - cursor.execute("insert into users values (?,?)", (user_id, username)) + cursor.execute('insert into users values (?,?)', (user_id, username)) conn.commit() conn.close() def fetch_user(user_id): - """ + ''' Returns a JSON object containing the requested user - Also can return a list of all users if user_id == "*" - """ + Also can return a list of all users if user_id == '*' + ''' conn = sqlite3.connect(DATABASE) conn.row_factory = sqlite3.Row cursor = conn.cursor() if user_id != '*': # must use (?), (item,) format - cursor.execute("SELECT * FROM users WHERE user_id=(?)", (user_id, )) + cursor.execute('SELECT * FROM users WHERE user_id=(?)', (user_id, )) else: - cursor.execute("SELECT * FROM users") + cursor.execute('SELECT * FROM users') output = cursor.fetchall() output = json.dumps([dict(row) for row in output]) @@ -111,18 +115,21 @@ def fetch_user(user_id): return output -def fetch_top_n(n): - output = get_top_N('score', True, n) +def fetch_top_n(num): + ''' + Retrieves the top n users by score. + ''' + output = get_top_n('score', True, num) return output def process_request(uri): - """ + ''' Handles the API endpoint. Currently supports: - - /mocha/users/"user_id" Returns JSON of the specified user + - /mocha/users/'user_id' Returns JSON of the specified user - /mocha/users/* Returns JSON list of all users - """ + ''' parts = uri.split('/')[1:] assert parts[0] == 'mocha' @@ -141,9 +148,9 @@ def process_request(uri): def application(environ, start_response): - """ + ''' mod_wsgi entry point - """ + ''' status = '200 OK' output = process_request(environ['REQUEST_URI']) From 0502bbdea35bbf2db79a55fcf362dd08e5560c00 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 15:01:09 -0500 Subject: [PATCH 23/30] Remove completed TODO --- mocha_server.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mocha_server.py b/mocha_server.py index fb10108..fe0a695 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -10,11 +10,9 @@ DATABASE = '/usr/local/www/mocha-server/mocha.db' if not path.exists(DATABASE): DATABASE = 'mocha.db' -# TODO: Add fetching of list of users # TODO: Add fetching of top N users by score # TODO: Add ability to store and retrieve avatars (as image files?) - def get_users(username_list): ''' Gets a list of users searching by name. From 3230aba34516c21dcb4b04eb8c074d712f045b59 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 15:03:35 -0500 Subject: [PATCH 24/30] Add additional error checks --- mocha_server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mocha_server.py b/mocha_server.py index fe0a695..ddef071 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -135,9 +135,9 @@ def process_request(uri): if len(parts) < 2: output = None - elif parts[1] == 'users': + elif parts[1] == 'users' and len(parts) > 2: output = fetch_user(parts[2]) - elif parts[1] == 'top': + elif parts[1] == 'top' and len(parts) > 2: output = fetch_top_n(parts[2]) else: output = None From fd7e3a0628366acd2abeec853087ccaf48d493a6 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 15:14:52 -0500 Subject: [PATCH 25/30] Add fetch_users --- mocha_server.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/mocha_server.py b/mocha_server.py index ddef071..a4dfab0 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -13,7 +13,7 @@ if not path.exists(DATABASE): # TODO: Add fetching of top N users by score # TODO: Add ability to store and retrieve avatars (as image files?) -def get_users(username_list): +def fetch_users(user_ids): ''' Gets a list of users searching by name. This can also easily be done by user_id. @@ -22,8 +22,8 @@ def get_users(username_list): conn.row_factory = sqlite3.Row cursor = conn.cursor() output = [] - for usr in username_list: - cursor.execute('select * from users where username=(?)', (usr, )) + for user_id in user_ids: + cursor.execute('select * from users where user_id=(?)', (user_id, )) output += cursor.fetchall() output = json.dumps([dict(row) for row in output]) @@ -136,7 +136,10 @@ def process_request(uri): if len(parts) < 2: output = None elif parts[1] == 'users' and len(parts) > 2: - output = fetch_user(parts[2]) + if ',' in parts[2]: + output = fetch_users(parts[2].split(',')) + else: + output = fetch_user(parts[2]) elif parts[1] == 'top' and len(parts) > 2: output = fetch_top_n(parts[2]) else: @@ -165,9 +168,10 @@ def application(environ, start_response): return [output] + if __name__ == '__main__': USERNAMES = ['andrew', 'Corder'] - print(get_users(USERNAMES)) + print(fetch_users(USERNAMES)) # vim: tabstop=4 shiftwidth=4 softtabstop=4 expandtab From cd040dfd89b9d16af1512cb95bacba224f9e233a Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 15:15:16 -0500 Subject: [PATCH 26/30] Change minor docstring --- mocha_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mocha_server.py b/mocha_server.py index a4dfab0..4f2a2b8 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -125,7 +125,7 @@ def process_request(uri): ''' Handles the API endpoint. Currently supports: - - /mocha/users/'user_id' Returns JSON of the specified user + - /mocha/users/{user_id} Returns JSON of the specified user - /mocha/users/* Returns JSON list of all users ''' parts = uri.split('/')[1:] From 2502d9c7f4e3aa6427f7474761087658cff9d4d7 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 15:40:51 -0500 Subject: [PATCH 27/30] Improve PEP8 compliance --- mocha_server.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/mocha_server.py b/mocha_server.py index 4f2a2b8..7f0283b 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -2,6 +2,7 @@ ''' Handle API requests to the database ''' + import json import sqlite3 from os import path @@ -13,11 +14,12 @@ if not path.exists(DATABASE): # TODO: Add fetching of top N users by score # TODO: Add ability to store and retrieve avatars (as image files?) + def fetch_users(user_ids): ''' - Gets a list of users searching by name. - This can also easily be done by user_id. - ''' + Gets a list of users searching by name. + This can also easily be done by user_id. + ''' conn = sqlite3.connect(DATABASE) conn.row_factory = sqlite3.Row cursor = conn.cursor() @@ -39,9 +41,12 @@ def fetch_users(user_ids): def get_top_n(search_parameter, desc, num): ''' In progress. - Currently the query seems to work, but returning a dict does not preserve order. + Currently the query seems to work, but returning a dict does not + preserve order. - Store an orderd id list and put that in the json? + Store an orderd id list and put that in the json? + + Alternatively, sort on the client-side. ''' conn = sqlite3.connect(DATABASE) conn.row_factory = sqlite3.Row @@ -54,7 +59,7 @@ def get_top_n(search_parameter, desc, num): (search_parameter, num)) output = cursor.fetchall() - #print(output) + # print(output) output = json.dumps([dict(row) for row in output]) conn.commit() @@ -78,7 +83,10 @@ def update_row(_user_id, _updated_username): def insert_row(user_id, username): ''' Inserts a row for a NEW user with given parameters - This may work better with AUTOINCREMENT to avoid arbitrary ids and duplicates + This may work better with AUTOINCREMENT to avoid arbitrary ids and + duplicates. + + Alternatively, use randomized unique IDs. ''' conn = sqlite3.connect(DATABASE) From 86fdf429c269530a95895cad33a7c127ffad42b0 Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Tue, 17 Apr 2018 15:41:09 -0500 Subject: [PATCH 28/30] Remove extraneous check --- mocha_server.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mocha_server.py b/mocha_server.py index 7f0283b..61cac9d 100755 --- a/mocha_server.py +++ b/mocha_server.py @@ -141,9 +141,7 @@ def process_request(uri): output = None - if len(parts) < 2: - output = None - elif parts[1] == 'users' and len(parts) > 2: + if parts[1] == 'users' and len(parts) > 2: if ',' in parts[2]: output = fetch_users(parts[2].split(',')) else: From 0c36551382d0452a85a6e854fcc49cf043bdc26a Mon Sep 17 00:00:00 2001 From: Corder Guy Date: Wed, 18 Apr 2018 19:28:20 -0500 Subject: [PATCH 29/30] Add additional test users --- mocha.db | Bin 8192 -> 8192 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/mocha.db b/mocha.db index 6f7a339b0a4f01510957f10929682547f76e68c6..b38e53fe7730b6f3d5fa0fcb24f322cc165824c3 100644 GIT binary patch delta 226 zcmZp0XmFSyEy&Bjz`z8=Fu*)f$C#g&K~G^ONQRNWmw~@`W1&1hXCWiIxVSiDqvPcD z`~hY<3dPC!MX3s&ej%>zu0aYeu5ON@J|PMQnjmdl{5cH#-}&G0-{!x-f0%zO|7!ke z{EhrMn*{{|_)~;t>9r}VUT7lNGvJJOwLZ>W@TZJWK2mc NDoV}antVat000^~Hr4_ klc(_q2x@|)S@ Date: Wed, 18 Apr 2018 19:35:42 -0500 Subject: [PATCH 30/30] Fix typo --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 07876c4..3fe0930 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ Base url = `https://corder.tech` -| Endpoint | Return | Implemented? | -| ---------------------------------- | ------------------------------ | ------------ | -| `/mocha/users/{user_id}` | row with the given user | Yes | -| `/mocha/users/*` | all rows | Yes | -| `/mocha/top/{n}` | user with the n highest scores | No | -| `/mocha/set/{user_id}/{num_steps}` | 200 OK if successful | No | +| Endpoint | Return | Implemented? | +| ---------------------------------- | ------------------------------- | ------------ | +| `/mocha/users/{user_id}` | row with the given user | Yes | +| `/mocha/users/*` | all rows | Yes | +| `/mocha/top/{n}` | users with the n highest scores | No | +| `/mocha/set/{user_id}/{num_steps}` | 200 OK if successful | No |