repository reformat (remove python)

This commit is contained in:
Xevion
2020-12-15 10:43:26 -06:00
parent c95760b64f
commit 6fdfa5ef94
25 changed files with 8974 additions and 9760 deletions

147
.gitignore vendored
View File

@@ -1,147 +0,0 @@
# Repository specific
.vscode/**
.idea/**
migrations/**
app.db
keys.json
app.db-journal
test.html
app/data/raw/*.html
app/data/preprocess/*.json
flaskenv
algolia.json
*.scss.css
*.scss.css.map
node_modules/**
server/data/algolia/**
server/data/html/**
server/data/processed/**
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

View File

@@ -1 +0,0 @@
VUE_APP_API_URL=http://localhost:5000

23
client/.gitignore vendored
View File

@@ -1,23 +0,0 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -1,5 +0,0 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

View File

@@ -1,47 +0,0 @@
{
"name": "power-math",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@babel/parser": "^7.11.5",
"axios": "^0.20.0",
"core-js": "^3.6.5",
"gsap": "^3.5.1",
"vue": "^2.6.11"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"node-sass": "^4.14.1",
"sass-loader": "^10.0.1",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,19 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:400,500,700,400italic|Material+Icons">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript
enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@@ -1,130 +0,0 @@
<template>
<div id="app">
<div id="expression" v-katex="expression"></div>
<div class="container">
<div class="columns is-centered">
<div class="column is-three-fifths">
<b-field id="input" @keyup.native.enter="checkAnswer()">
<b-input v-model="answer"></b-input>
</b-field>
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
html, body, #app {
height: 100%;
min-height: 100%;
background: hsl(0, 0%, 6%)
}
#input {
input {
text-align: center;
background-color: transparent;
border-color: hsl(0, 0%, 20%);
border-left-width: 0;
border-right-width: 0;
border-radius: 0;
color: hsl(0, 0%, 80%);
font-family: 'KaTeX_Main', serif;
padding: 0;
height: 1.3em;
line-height: 0;
font-size: 8em;
&:active, &:focus {
box-shadow: none;
}
}
}
a, p, span {
color: hsl(0, 0%, 91%);
text-shadow: hsl(0, 0%, 5%) 5px 5px;
}
#question-text {
font-family: "Computer Modern", serif;
}
#expression {
width: 75%;
margin: 0 auto;
padding-bottom: 2em;
text-align: center;
.katex {
font-size: 16em !important;
white-space: nowrap;
user-select: none;
}
}
</style>
<script>
import arithmetic from './arithmetic.js';
export default {
name: 'App',
data() {
return {
answer: null,
currentQuestion: null,
correctTimeout: false
}
},
computed: {
expression() {
return this.currentQuestion != null ? this.currentQuestion.text : "error";
},
},
created() {
window.addEventListener('keyup', (e) => {
if (e.keyCode === 39) {
this.nextQuestion();
}
});
},
mounted: function () {
this.$nextTick(() => {
this.nextQuestion();
})
},
methods: {
nextQuestion() {
this.currentQuestion = arithmetic.methods.getProblem();
},
checkAnswer() {
// Check answer
let correct;
// Number parsing if the answer is a specific number
if (typeof this.currentQuestion.answer === "number")
correct = this.currentQuestion.answer === Number.parseInt(this.answer)
else
// String based answer (like a fraction)
correct = this.currentQuestion.answer === this.answer
if (correct) {
// Correct answer toast, new question & reset answer box
this.$buefy.toast.open({
message: 'Correct!',
type: 'is-success',
duration: 6000
})
this.nextQuestion();
this.answer = "";
} else {
// Incorrect answer toast
this.$buefy.toast.open({
message: 'Incorrect.',
type: 'is-danger',
duration: 5000
})
}
}
}
}
</script>

View File

@@ -1,51 +0,0 @@
import utils from './utils.js';
export default {
methods: {
// Generate a addition problem
addition(mult = 1) {
let a = utils.methods.getRandomInt(5 * mult, 100 * mult);
let b = utils.methods.getRandomInt(5 * mult, 100 * mult);
return {
text: `${a} + ${b}`,
answer: a + b
}
},
subtraction(mult = 1) {
let a = utils.methods.getRandomInt(5 * mult, 100 * mult);
let b = utils.methods.getRandomInt(5 * mult, 100 * mult);
if (Math.random() > 0.5) {
return {
text: `${a} - ${b}`,
answer: a - b,
}
}
else {
return {
text: `-${a} + ${b}`,
answer: -a + b
}
}
},
multiplication(mult = 1) {
let a = utils.methods.getRandomInt(3 * mult, 30 * mult);
let b = utils.methods.getRandomInt(3 * mult, 15 * mult);
return {
text: `${a} \\times ${b}`,
answer: a * b
}
},
square_root(mult = 1) {
let a = utils.methods.getRandomInt(2 * mult, 20 * mult);
return {
text: `\\sqrt{${a * a}}`,
answer: a
}
},
getProblem: function () {
let possible = [this.multiplication, this.square_root];
let index = utils.methods.getRandomInt(0, possible.length);
return possible[index]();
}
}
}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1,15 +0,0 @@
import Vue from 'vue'
import VueKatex from 'vue-katex';
import Buefy from 'buefy';
import App from './App.vue'
import 'katex/dist/katex.min.css';
import 'buefy/dist/buefy.css';
Vue.config.productionTip = false
Vue.use(Buefy)
Vue.use(VueKatex)
new Vue({
render: h => h(App),
}).$mount('#app')

View File

@@ -1,9 +0,0 @@
export default {
methods: {
getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min); //The maximum is exclusive and the minimum is inclusive
}
}
}

View File

@@ -1,3 +0,0 @@
module.exports = {
outputDir: './../dist/',
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,50 @@
{
"dependencies": {
"axios": "^0.21.0",
"katex": "^0.12.0",
"vue-katex": "^0.5.0"
}
"name": "power-math",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.21.0",
"buefy": "^0.9.4",
"katex": "^0.12.0",
"vue-katex": "^0.5.0",
"@babel/parser": "^7.11.5",
"core-js": "^3.6.5",
"gsap": "^3.5.1",
"vue": "^2.6.11"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"node-sass": "^4.14.1",
"sass-loader": "^10.0.1",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

View File

@@ -1,3 +0,0 @@
flask
flask-restful
flask-cors

View File

@@ -1,123 +0,0 @@
"""
api.py
Organizes all API routes for acquiring, checking, removing questions, as well as other important and miscellaneous
API interactions, static or dynamic.
"""
import copy
import random
from flask_restful import Resource, reqparse, abort
from server import questions, exceptions
from server.helpers import generate_id
# Stores active questions in memory for checking answers.
# Keys represent random question IDs. Values are Question objects.
active_questions = {}
# Question categories, key name, value function for acquiring a random question generator.
categories = {
'arithmetic': (
questions.get_arithmetic,
'The basic four mathematical functions, plus fractions, exponents and a little more.'
)
}
parser = reqparse.RequestParser()
parser.add_argument(
'category',
required=False, type=str, choices=tuple(categories.keys()), help='Invalid Category: {error_msg}'
)
class Question(Resource):
"""
Questions are small objects represented generated questions for users with a prompt and a answer.
They are identified by a String ID.
"""
def get(self, question_id=None):
"""Retrieve information about a given question, including the answer for it."""
if question_id is not None:
if question_id in active_questions.keys():
return active_questions[question_id]
else:
raise exceptions.InvalidQuestion(404, question=question_id)
else:
abort(404, message='Use PUT to create questions, otherwise specify a question ID')
def put(self, question_id=None):
"""
Request a new question.
A category can be specified in the query arguments in order to filter the question type.
Furthermore, a question type can be also specified .
The Question object is returned, although the answer is omitted.
"""
args = parser.parse_args()
# Generate new Question ID
q_id = None
while q_id in active_questions.keys() or q_id is None:
q_id = generate_id(5)
# Get category arg or choose one if not specified.
if args.get('category') is not None:
category = args.get('category')
# Check that the category is valid
if category not in categories.keys():
raise exceptions.InvalidCategory(404, category=category)
else:
category = random.choice(list(categories.keys()))
# Acquire a question generator and generate a function, then store the result.
active_questions[q_id] = categories[category][0]()()
# Make a shallow copy, hide 'answer' key.
question = copy.copy(active_questions[q_id])
del question['answer']
return question, 201
def delete(self, question_id):
"""Delete a question object from the running before it is automatically removed."""
if question_id in active_questions.keys():
del active_questions[question_id]
return {'message': f'Successfully deleted Question ID {question_id}'}
else:
raise exceptions.InvalidQuestion(404, question=question_id)
class Questions(Resource):
"""
Simple resource for listing all available question objects.
"""
def get(self):
return active_questions
class Category(Resource):
"""
A category is a static designation for a problem to be classified as.
Any question will only have one category it belongs to.
"""
def get(self, category_id):
"""Get all information about a category"""
if category_id in categories.keys():
return categories[category_id][1]
else:
raise exceptions.InvalidCategory(404, category=category_id)
class Categories(Resource):
"""
Categories are static identifiers for the different types of questions available to users.
They are identified with pre-chosen identifiers.
"""
def get(self):
"""Get a list of all categories."""
return list(categories.keys()), 200

View File

@@ -1,66 +0,0 @@
"""
arithmetic.py
Organizes all questions related to basic arithmetic, i.e. the four primary mathematical operations.
Also includes arithmetic related to exponents (positive and negative), fractions and more.
"""
import inspect
import random
def addition():
a, b = random.randint(6, 70), random.randint(6, 70)
return {
'type': inspect.stack()[0][3],
'question': f'{a} + {b}',
'answer': a + b
}
def subtraction():
a, b = random.randint(6, 70), random.randint(6, 70)
return {
'type': inspect.stack()[0][3],
'question': f'{a} - {b}',
'answer': a - b
}
def multiplication():
a = random.randint(2, 18)
b = random.randint(2, 26 - a)
return {
'type': inspect.stack()[0][3],
'question': f'{a}\\times {b}',
'answer': a * b
}
def division():
a = random.randint(2, 18)
b = random.randint(2, 26 - a)
return {
'type': inspect.stack()[0][3],
'question': f'{a * b}\\div {b}',
'answer': a
}
def simplify_fraction():
a = random.randint(1, 12)
b = random.randint(2, 20 - a)
# Multiply by even/odd to
c, d = a, b
while c + d <= 15:
multiplier = random.randint(2, 3)
c, d = c * multiplier, d * multiplier
return {
'type': inspect.stack()[0][3],
'question': f'\\frac{{{c}}}{{{d}}}',
'answer': f'{a}/{b}'
}
questions = [addition, subtraction, multiplication, division, simplify_fraction]

View File

@@ -1,28 +0,0 @@
"""config.py
Stores all configurations for this Flask app. Would be used for storing storing and access development
and production secret keys.
"""
import os
configs = {
'development': 'server.config.DevelopmentConfig',
'production': 'server.config.ProductionConfig',
'testing': 'server.config.TestingConfig'
}
class Config:
"""Base configuration for the app."""
class DevelopmentConfig(Config):
"""Development configuration for the app."""
class ProductionConfig(Config):
"""Production configuration for the app."""
class TestingConfig(Config):
"""Testing configuraiton for the app."""

View File

@@ -1,46 +0,0 @@
"""
create_app.py
The main app creation, registering extensions, API routes, the Vue.js catch all redirect and configs.
"""
from flask import Flask, render_template, jsonify
from flask_restful import Api
from flask_cors import CORS
from server import exceptions
from server.api import Question, Questions, Category, Categories
from server.config import configs
def create_app(env=None):
app = Flask(
__name__,
static_folder="./../dist/static",
template_folder="./../dist"
)
# Add CORS support
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
# Instantiate Flask-Restful API and register appropriate routes
api = Api(app, prefix='/api/')
api.add_resource(Question, '/question/', '/question/<string:question_id>')
api.add_resource(Category, '/category/<string:category_id>')
api.add_resource(Categories, '/categories/')
api.add_resource(Questions, '/questions/')
if not env:
env = app.config['ENV']
app.config.from_object(configs[env])
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
return render_template("index.html")
@app.errorhandler(exceptions.APIException)
def api_exceptions(e):
return jsonify(e.json()), e.status_code
return app

View File

@@ -1,90 +0,0 @@
"""
exceptions.py
Stores all API exceptions neatly for importing and usage elsewhere.
"""
from typing import Tuple
# TODO: Improve exception management to cut down needless class definitions.
# TODO: Add 'extra' message parameter to base APIException kwargs.
class APIException(Exception):
"""Exception from which all API-related exceptions are derived and formatted with"""
MESSAGE = "A generic unhandled API Exception has occurred."
def __init__(self, status_code: int = 500):
self.status_code = status_code
def json(self):
return {
'error': {
'code': self.status_code,
'message': self.MESSAGE
}
}
class UnspecifiedParam(APIException):
MESSAGE = 'This API Route requires a parameter that was not satisfied in the latest request.'
def __init__(self, status_code: int):
super().__init__(status_code)
def json(self):
return {
'error': {
'code': self.status_code,
'message': self.MESSAGE
}
}
class UnspecifiedQueryParam(APIException):
MESSAGE = 'This API Route requires a query parameter that was not satisfied in the latest request.'
def __init__(self, status_code: int):
super().__init__(status_code)
class UnspecifiedURIParam(APIException):
MESSAGE = 'This API Route requires a URI parameter that was not satisfied in the latest request.'
def __init__(self, status_code: int):
super().__init__(status_code)
class InvalidQueryParam(APIException):
def __init__(self, status_code: int, query_item: Tuple[str, str]):
super().__init__(status_code)
self.query_item = query_item
def json(self):
error = super().json()
error['error']['query'] = {'key': self.query_item[0], 'value': self.query_item[1]}
return error
class InvalidURIParam(APIException):
def __init__(self, status_code: int, route_param: str):
super().__init__(status_code)
self.route_param = route_param
def json(self):
error = super().json()
error['error']['param'] = self.route_param
return error
class InvalidQuestion(InvalidURIParam):
MESSAGE = "A invalid question was specified in the request URI and could not be resolved."
def __init__(self, *args, question):
super().__init__(*args, route_param=question)
class InvalidCategory(InvalidURIParam):
MESSAGE = "A invalid category was specified in the request URI and could not be resolved."
def __init__(self, status_code, category: str):
super().__init__(status_code, route_param=category)

View File

@@ -1,6 +0,0 @@
import random
import string
def generate_id(length: int):
return ''.join(random.choices(string.ascii_letters, k=length))

View File

@@ -1,6 +0,0 @@
import random
from server import arithmetic
def get_arithmetic():
return random.choice(arithmetic.questions)

View File

@@ -1,3 +0,0 @@
from server.create_app import create_app
app = create_app()

8928
yarn.lock
View File

File diff suppressed because it is too large Load Diff