about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authormounderfod <mounderfod@gmail.com>2023-07-20 17:11:14 +0200
committermounderfod <mounderfod@gmail.com>2023-07-20 17:11:14 +0200
commit44d18b0ddd81736f938ac75a96ae519195c01c20 (patch)
treecca1ab702986f465576f4fa31d0b0b7e404aabf0
downloadpyMathEngine-44d18b0ddd81736f938ac75a96ae519195c01c20.tar.gz
Initial commit
-rw-r--r--.gitignore1
-rw-r--r--.idea/.gitignore8
-rw-r--r--.idea/flaskProject.iml21
-rw-r--r--.idea/inspectionProfiles/Project_Default.xml12
-rw-r--r--.idea/inspectionProfiles/profiles_settings.xml6
-rw-r--r--.idea/misc.xml4
-rw-r--r--.idea/modules.xml8
-rw-r--r--Discovery.md12
-rw-r--r--README.md8
-rw-r--r--Spacefile12
-rw-r--r--app.py46
-rw-r--r--icon.pngbin0 -> 33676 bytes
-rw-r--r--requirements.txt3
-rw-r--r--templates/index.html146
14 files changed, 287 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..97861c9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.space
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/flaskProject.iml b/.idea/flaskProject.iml
new file mode 100644
index 0000000..688ba73
--- /dev/null
+++ b/.idea/flaskProject.iml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PYTHON_MODULE" version="4">
+  <component name="Flask">
+    <option name="enabled" value="true" />
+  </component>
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/venv" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+  <component name="TemplatesService">
+    <option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
+    <option name="TEMPLATE_FOLDERS">
+      <list>
+        <option value="$MODULE_DIR$/../flaskProject\templates" />
+      </list>
+    </option>
+  </component>
+</module>
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..d4a00d6
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,12 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
+      <option name="ignoredErrors">
+        <list>
+          <option value="E302" />
+        </list>
+      </option>
+    </inspection_tool>
+  </profile>
+</component>
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="USE_PROJECT_PROFILE" value="false" />
+    <version value="1.0" />
+  </settings>
+</component>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..7e09807
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (flaskProject)" project-jdk-type="Python SDK" />
+</project>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..2c2d842
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/flaskProject.iml" filepath="$PROJECT_DIR$/.idea/flaskProject.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/Discovery.md b/Discovery.md
new file mode 100644
index 0000000..7d3c460
--- /dev/null
+++ b/Discovery.md
@@ -0,0 +1,12 @@
+---
+app_name: "pyMathEngine"
+title: "pyMathEngine"
+tagline: "A mathematical engine in the style of Wolfram Alpha"
+git: <https://github.com/mounderfod/pyMathEngine>
+---
+
+A mathematical engine in the style of Wolfram Alpha.
+Very useful for homework, assignments, etc.
+Currently, the engine can only take univariate expressions, but additional features are being added over time.
+
+[(view screenshot)](https://cdn.discordapp.com/attachments/838048982873538572/1131593838285295637/image.png)
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e699297
--- /dev/null
+++ b/README.md
@@ -0,0 +1,8 @@
+# pyMathEngine
+[<img width="200" src="https://deta.space/buttons/dark.svg">](https://deta.space/discovery/@mounderfod/pymathengine)
+
+A mathematical engine in the style of Wolfram Alpha.
+Very useful for homework, assignments, etc.
+Currently, the engine can only take univariate expressions, but additional features are being added over time.
+
+![(A screenshot of the app)](https://cdn.discordapp.com/attachments/838048982873538572/1131593838285295637/image.png)
\ No newline at end of file
diff --git a/Spacefile b/Spacefile
new file mode 100644
index 0000000..9b8a42e
--- /dev/null
+++ b/Spacefile
@@ -0,0 +1,12 @@
+# Spacefile Docs: https://go.deta.dev/docs/spacefile/v0
+v: 0
+
+micros:
+  - name: python-app
+    src: .
+    engine: python3.9
+    primary: true
+    run: flask run
+    public: true
+
+icon: ./icon.png
diff --git a/app.py b/app.py
new file mode 100644
index 0000000..51b158b
--- /dev/null
+++ b/app.py
@@ -0,0 +1,46 @@
+import base64
+import os
+
+from sympy import diff, integrate, print_latex, latex, pretty, solve, Eq
+from sympy.parsing.sympy_parser import *
+from sympy.plotting import plot
+from io import BytesIO
+
+from flask import Flask, request, render_template
+
+app = Flask(__name__)
+
+
+@app.route('/')
+def hello_world():  # put application's code here
+    return render_template("index.html", HOSTNAME=os.getenv("DETA_SPACE_APP_HOSTNAME"))
+
+@app.route('/api/univariate')
+def plotter():
+    args = request.args
+
+    try:
+        exp = parse_expr(args['func'], transformations=standard_transformations + (split_symbols, implicit_multiplication, function_exponentiation, convert_xor))
+
+        buf = BytesIO()
+
+        p = plot(exp, show=False)
+        p._backend = p.backend(p)
+        p._backend.process_series()
+        p._backend.fig.savefig(buf, format="png")
+
+        data = base64.b64encode(buf.getbuffer()).decode("ascii")
+        return {
+            "variable": pretty(exp.atoms(Symbol).pop()),
+            "expression": latex(exp),
+            "plot": data,
+            "diff": latex(diff(exp)),
+            "integral": latex(integrate(exp)),
+            "solution": [latex(i) for i in solve(Eq(exp, 0))]
+        }
+    except Exception as e:
+        print(e)
+        return f"Bad Request", 400
+
+if __name__ == '__main__':
+    app.run()
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000..54ac929
--- /dev/null
+++ b/icon.png
Binary files differdiff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..7b99323
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,3 @@
+sympy~=1.12
+Flask~=2.3.2
+matplotlib~=3.7.2
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..e87e7e4
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>pyMathEngine</title>
+    <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
+    <script
+      id="MathJax-script"
+      async
+      src="https://cdn.jsdelivr.net/npm/mathjax@3.0.1/es5/tex-mml-chtml.js"
+    ></script>
+    <link
+      href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
+      rel="stylesheet"
+      integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
+      crossorigin="anonymous"
+    />
+    <script
+      src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
+      integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
+      crossorigin="anonymous"
+    ></script>
+    <link
+      rel="icon"
+      type="image/png"
+      href="https://cdn.discordapp.com/attachments/838048982873538572/1131593508759814287/favicon.png"
+    />
+  </head>
+  <body>
+    <div class="m-3">
+      <div class="alert alert-info mb-2" role="alert">
+        Welcome to <b>pyMathEngine</b>, a mathematical engine in the style of
+        Wolfram Alpha. <br />
+        Currently the engine can only take univariate expressions (e.g. \(
+        x^2+2x+3 \)), but additional features are being added over time.<br />
+        Some notes:
+        <ul>
+          <li>Euler's number (\(e\)) is written as "E" in the box</li>
+          <li>Pi (\(\pi\)) is written as "pi" in the box</li>
+        </ul>
+      </div>
+      <div class="input-group mb-3">
+        <input
+          id="expression"
+          type="text"
+          class="form-control"
+          placeholder="Enter a univariate expression..."
+          aria-label="Enter a univariate expression..."
+          aria-describedby="button-addon"
+        />
+        <button
+          class="btn btn-secondary"
+          type="button"
+          onclick="get()"
+          id="button-addon"
+        >
+          Search
+        </button>
+      </div>
+    </div>
+    <div id="response" class="m-5"></div>
+  </body>
+  <script>
+    function get() {
+      let exp = document.getElementById("expression").value;
+      fetch(
+        `https://${"{{ HOSTNAME }}"}/api/univariate?func=${encodeURIComponent(
+          exp
+        )}`
+      )
+        .then((response) => {
+          if (!response.ok) {
+            // create error object and reject if not a 2xx response code
+            let err = new Error("HTTP status code: " + response.status);
+            err.response = response;
+            err.status = response.status;
+            throw err;
+          } else {
+            response.json().then((data) => {
+              console.log(data);
+              let responseBox = document.getElementById("response");
+              responseBox.innerHTML = "";
+              responseBox.innerHTML += `
+              <div class="card mt-2">
+                <div class="card-header">Input</div>
+                <div class="card-body">
+                    <p>$$ f(${data.variable}) = ${data.expression} $$</p>
+                </div>
+              </div>
+
+              <div class="card mt-2">
+                <div class="card-header">Solutions (where \\(f(${data.variable}) = 0\\))</div>
+                <div class="card-body overflow-auto"><p>
+                    $$ ${data.solution} $$ 
+                    </p></div>
+              </div>
+
+              <div class="card mt-2">
+                <div class="card-header">Plot</div>
+                <div class="card-body text-center">
+                    <img src="data:image/png;base64,${data.plot}" />
+                </div>
+              </div>
+
+              <div class="card mt-2">
+                <div class="card-header">Differential</div>
+                <div class="card-body">
+                    <p>$$ \\frac{\\mathrm{d} f(${data.variable}) }{\\mathrm{d} ${data.variable}} = ${data.diff} $$</p>
+                </div>
+              </div>
+
+              <div class="card mt-2">
+                <div class="card-header">Integral</div>
+                <div class="card-body">
+                    <p>$$ \\int f(${data.variable})\\, \\mathrm{d}${data.variable} = ${data.integral} + c$$</p>
+                </div>
+              </div>
+
+              `;
+              MathJax.typeset();
+            });
+          }
+        })
+        .catch((err) => {
+          let responseBox = document.getElementById("response");
+
+          responseBox.innerHTML = "";
+          responseBox.innerHTML += `
+          <div class="card mt-2">
+            <div class="card-header bg-danger text-white">Error</div>
+            <div class="card-body">
+                <p>${err.message}</p>
+                <p>This is likely because you have entered an invalid expression.</p>
+            </div>
+          </div>
+          `;
+        });
+    }
+  </script>
+  <script type="text/x-mathjax-config">
+    MathJax.Hub.Config({
+        "CommonHTML": { linebreaks: { automatic: true, width: "container" } },
+        "HTML-CSS": { linebreaks: { automatic: true, width: "container" } },
+        "SVG": { linebreaks: { automatic: true, width: "container" } }
+    });
+  </script>
+</html>