mirror of
https://github.com/Xevion/power-math.git
synced 2025-12-12 18:12:28 -06:00
repository reformat (remove python)
This commit is contained in:
147
.gitignore
vendored
147
.gitignore
vendored
@@ -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/
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
VUE_APP_API_URL=http://localhost:5000
|
|
||||||
23
client/.gitignore
vendored
23
client/.gitignore
vendored
@@ -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?
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: [
|
|
||||||
'@vue/cli-plugin-babel/preset'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -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>
|
|
||||||
@@ -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>
|
|
||||||
@@ -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]();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.7 KiB |
@@ -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')
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
outputDir: './../dist/',
|
|
||||||
}
|
|
||||||
8932
client/yarn.lock
8932
client/yarn.lock
File diff suppressed because it is too large
Load Diff
53
package.json
53
package.json
@@ -1,7 +1,50 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"name": "power-math",
|
||||||
"axios": "^0.21.0",
|
"version": "0.1.0",
|
||||||
"katex": "^0.12.0",
|
"private": true,
|
||||||
"vue-katex": "^0.5.0"
|
"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"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
flask
|
|
||||||
flask-restful
|
|
||||||
flask-cors
|
|
||||||
123
server/api.py
123
server/api.py
@@ -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
|
|
||||||
@@ -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]
|
|
||||||
@@ -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."""
|
|
||||||
@@ -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
|
|
||||||
@@ -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)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import random
|
|
||||||
import string
|
|
||||||
|
|
||||||
|
|
||||||
def generate_id(length: int):
|
|
||||||
return ''.join(random.choices(string.ascii_letters, k=length))
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import random
|
|
||||||
from server import arithmetic
|
|
||||||
|
|
||||||
|
|
||||||
def get_arithmetic():
|
|
||||||
return random.choice(arithmetic.questions)
|
|
||||||
Reference in New Issue
Block a user