merge attempt

This commit is contained in:
asleal2 2018-04-19 13:13:43 -05:00
commit beedeaab73
19 changed files with 107 additions and 347 deletions

0
.env
View file

2
.gitignore vendored
View file

@ -1 +1,3 @@
venv/ venv/
__pycache__/
pip-selfcheck.json

11
README.md Normal file
View file

@ -0,0 +1,11 @@
# API endpoints:
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}` | users with the n highest scores | No |
| `/mocha/set/{user_id}/{num_steps}` | 200 OK if successful | No |

View file

@ -1,5 +0,0 @@
#!/usr/bin/bash
set -e
source "./venv/bin/activate"
pip install -Ur ./requirements.txt

104
\
View file

@ -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]

Binary file not shown.

View file

@ -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)

BIN
mocha.db

Binary file not shown.

View file

@ -1,46 +1,35 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" '''
Handle API requests to the database Handle API requests to the database
""" '''
import json import json
import sqlite3 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'
# 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 fetching of top N users by score
# TODO: Add ability to store and retrieve avatars (as image files?) # TODO: Add ability to store and retrieve avatars (as image files?)
def update_steps(user_id, steps): def fetch_users(user_ids):
""" '''
Overwrites the old raw_steps value.
This can be changed to increment it if needed.
"""
cursor.execute("update users set raw_steps=? where user_id=?", (steps, user_id))
conn.commit()
def get_users(username_list):
"""
Gets a list of users searching by name. Gets a list of users searching by name.
This can also easily be done by user_id. This can also easily be done by user_id.
""" '''
conn = sqlite3.connect(DATABASE)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
output = [] output = []
for usr in username_list: for user_id in user_ids:
cursor.execute("select * from users where username=(?)", (usr,)) cursor.execute('select * from users where user_id=(?)', (user_id, ))
output += cursor.fetchall() output += cursor.fetchall()
output = json.dumps([dict(row) for row in output]) output = json.dumps([dict(row) for row in output])
conn.commit() conn.commit()
#conn.close() conn.close()
if output == '[]': if output == '[]':
output = None output = None
@ -48,24 +37,32 @@ def get_users(username_list):
return output return output
def get_top_N(search_parameter, desc, N): def get_top_n(search_parameter, desc, num):
""" '''
In progress. 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?
"""
if desc == True: Alternatively, sort on the client-side.
cursor.execute("select * from users order by ? desc limit ?", (search_parameter, N)) '''
conn = sqlite3.connect(DATABASE)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
if desc:
cursor.execute('select * from users order by ? desc limit ?',
(search_parameter, num))
else: 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() output = cursor.fetchall()
#print(output) # print(output)
output = json.dumps([dict(row) for row in output]) output = json.dumps([dict(row) for row in output])
conn.commit() conn.commit()
#conn.close() conn.close()
if output == '[]': if output == '[]':
output = None output = None
@ -74,35 +71,48 @@ def get_top_N(search_parameter, desc, N):
# add new parameters as needed # 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() print()
def insert_row(user_id, username): def insert_row(user_id, username):
""" '''
Inserts a row for a NEW user with given parameters 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.
cursor.execute("insert into users values (?,?)", (user_id, username))
Alternatively, use randomized unique IDs.
'''
conn = sqlite3.connect(DATABASE)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute('insert into users values (?,?)', (user_id, username))
conn.commit() conn.commit()
#conn.close() conn.close()
def fetch_user(user_id): def fetch_user(user_id):
""" '''
Returns a JSON object containing the requested user 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 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: else:
cursor.execute("SELECT * FROM users") cursor.execute('SELECT * FROM users')
output = cursor.fetchall() output = cursor.fetchall()
output = json.dumps([dict(row) for row in output]) output = json.dumps([dict(row) for row in output])
conn.commit() conn.commit()
#conn.close() conn.close()
if output == '[]': if output == '[]':
output = None output = None
@ -110,28 +120,43 @@ def fetch_user(user_id):
return output return output
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): def process_request(uri):
""" '''
Handles the API endpoint. Handles the API endpoint.
Currently supports: 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 - /mocha/users/* Returns JSON list of all users
""" '''
parts = uri.split('/')[1:] parts = uri.split('/')[1:]
assert parts[0] == 'mocha' assert parts[0] == 'mocha'
output = None output = None
if parts[1] == 'users': if parts[1] == 'users' and len(parts) > 2:
if ',' in parts[2]:
output = fetch_users(parts[2].split(','))
else:
output = fetch_user(parts[2]) output = fetch_user(parts[2])
elif parts[1] == 'top' and len(parts) > 2:
output = fetch_top_n(parts[2])
else:
output = None
return output return output
def application(environ, start_response): def application(environ, start_response):
""" '''
mod_wsgi entry point mod_wsgi entry point
""" '''
status = '200 OK' status = '200 OK'
output = process_request(environ['REQUEST_URI']) output = process_request(environ['REQUEST_URI'])
@ -147,3 +172,11 @@ def application(environ, start_response):
start_response(status, response_headers) start_response(status, response_headers)
return [output] return [output]
if __name__ == '__main__':
USERNAMES = ['andrew', 'Corder']
print(fetch_users(USERNAMES))
# vim: tabstop=4 shiftwidth=4 softtabstop=4 expandtab

View file

@ -1,120 +0,0 @@
"""
Django settings for mochaserver project.
Generated by 'django-admin startproject' using Django 2.0.4.
For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '(vy6o!asl(q@de1ee9#cy7fdw@4$!cn^j84pg97gai8nj$4h4i'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'mochaserver.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'mochaserver.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/
STATIC_URL = '/static/'

View file

@ -1,21 +0,0 @@
"""mochaserver URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]

View file

@ -1,16 +0,0 @@
"""
WSGI config for mochaserver project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mochaserver.settings")
application = get_wsgi_application()

View file

@ -1 +0,0 @@
{"last_check":"2018-04-06T05:49:45Z","pypi_version":"9.0.3"}

View file

@ -1,4 +0,0 @@
Django==2.0.4
django-environ==0.4.4
pytz==2018.3
six==1.11.0