├── README.md
├── react-frontend
├── .gitignore
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.css
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── components
│ │ ├── code-editor.tsx
│ │ ├── codemirror.css
│ │ ├── controls.css
│ │ ├── controls.tsx
│ │ ├── placeholder.tsx
│ │ ├── userinput.tsx
│ │ ├── visualizer.css
│ │ └── visualizer.js
│ ├── index.css
│ ├── index.tsx
│ ├── logo.svg
│ ├── react-app-env.d.ts
│ ├── serviceWorker.ts
│ ├── setupTests.ts
│ └── util
│ │ ├── graph-util.js
│ │ └── utility-functions.js
└── tsconfig.json
└── remote-code-runner
├── app.py
├── hasImport.py
├── injectCode.py
├── judge.py
├── parseinput.py
├── remoteCodeRunner.py
├── requirements.txt
└── tryget.py
/README.md:
--------------------------------------------------------------------------------
1 | # This project is no longer being maintained!
2 |
3 | As Server hosting fees started racking up due to many many infinite recursive loops being submitted to what is more or less a hackathon project,
4 | I decided it was better to just pull the plug. Sorry :(
5 |
6 | # Recursion Visualizer
7 |
8 | Ever wondered what the recursive structure of your Leetcode algorithm looks like?
9 | Then you are in luck! This project allows you to see recursion in your code.
10 | Whoa! What does that mean? Basically, write a single recursive function, and this app will show you step by step how your algorithm reached it's solution. And at each stage of the recursion, you're able to navigate the call graph and see the return output value at each stage. How does that sound?
11 |
12 | ## Build it locally
13 | Ok, so what you need is Node.js v12.18.3, Python 3.8.5, and a lot of patience. Wait, no, I meant you need Flask. So, just install everything in requirements.txt in the remoteCodeRunner, run npm install in the react-frontend directory, start the Flask server, and you should be good! (also, you need to adjust your localhost flask URL inside the react directory.)
14 |
15 | ## DEMO
16 | Visit: https://www.youtube.com/watch?v=Zb0EJkHHxgo
17 |
--------------------------------------------------------------------------------
/react-frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | firebase-debug.log*
8 | firebase-debug.*.log*
9 |
10 | # Firebase cache
11 | .firebase/
12 |
13 | # Firebase config
14 |
15 | # Uncomment this if you'd like others to create their own Firebase project.
16 | # For a team working on the same Firebase project(s), it is recommended to leave
17 | # it commented so all members can deploy to the same project(s) in .firebaserc.
18 | # .firebaserc
19 |
20 | # Runtime data
21 | pids
22 | *.pid
23 | *.seed
24 | *.pid.lock
25 |
26 | # Directory for instrumented libs generated by jscoverage/JSCover
27 | lib-cov
28 |
29 | # Coverage directory used by tools like istanbul
30 | coverage
31 |
32 | # nyc test coverage
33 | .nyc_output
34 |
35 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
36 | .grunt
37 |
38 | # Bower dependency directory (https://bower.io/)
39 | bower_components
40 |
41 | # node-waf configuration
42 | .lock-wscript
43 |
44 | # Compiled binary addons (http://nodejs.org/api/addons.html)
45 | build/Release
46 |
47 | # Dependency directories
48 | node_modules/
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Optional REPL history
57 | .node_repl_history
58 |
59 | # Output of 'npm pack'
60 | *.tgz
61 |
62 | # Yarn Integrity file
63 | .yarn-integrity
64 |
65 | # dotenv environment variables file
66 | .env
67 |
--------------------------------------------------------------------------------
/react-frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ts-rec",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@material-ui/core": "^4.11.0",
7 | "@testing-library/jest-dom": "^4.2.4",
8 | "@testing-library/react": "^9.5.0",
9 | "@testing-library/user-event": "^7.2.1",
10 | "@types/jest": "^24.9.1",
11 | "@types/node": "^12.12.58",
12 | "@types/react": "^16.9.49",
13 | "@types/react-dom": "^16.9.8",
14 | "codemirror": "^5.57.0",
15 | "d3": "^5.16.0",
16 | "react": "^16.13.1",
17 | "react-codemirror2": "^7.2.1",
18 | "react-d3-graph": "^2.5.0",
19 | "react-dom": "^16.13.1",
20 | "react-scripts": "3.4.3",
21 | "react-spring": "^8.0.27",
22 | "typescript": "^3.7.5"
23 | },
24 | "scripts": {
25 | "start": "react-scripts start",
26 | "build": "react-scripts build",
27 | "test": "react-scripts test",
28 | "eject": "react-scripts eject"
29 | },
30 | "eslintConfig": {
31 | "extends": "react-app"
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/react-frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProjectsByJackHe/recursion-visualizer/9bb19e8722735a9f45b342b2649a4c00c09bca0a/react-frontend/public/favicon.ico
--------------------------------------------------------------------------------
/react-frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | Recursion Visualizer
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/react-frontend/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProjectsByJackHe/recursion-visualizer/9bb19e8722735a9f45b342b2649a4c00c09bca0a/react-frontend/public/logo192.png
--------------------------------------------------------------------------------
/react-frontend/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProjectsByJackHe/recursion-visualizer/9bb19e8722735a9f45b342b2649a4c00c09bca0a/react-frontend/public/logo512.png
--------------------------------------------------------------------------------
/react-frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/react-frontend/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/react-frontend/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | body {
11 | background-color: #15121d;
12 | }
13 |
14 | .App-header {
15 | background-color: #282c34;
16 | min-height: 100vh;
17 | display: flex;
18 | flex-direction: column;
19 | align-items: center;
20 | justify-content: center;
21 | font-size: calc(10px + 2vmin);
22 | color: white;
23 | }
24 |
25 | .App-link {
26 | color: #61dafb;
27 | }
--------------------------------------------------------------------------------
/react-frontend/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | const { getByText } = render();
7 | const linkElement = getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/react-frontend/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, {Fragment, useState} from 'react';
2 | import './App.css';
3 | import UserInput from './components/userinput'
4 | import Visualizer from './components/visualizer'
5 |
6 | function App() {
7 | const [arrOfCalls, setArrOfCalls] = useState([])
8 | const [funcName, setFuncName] = useState("")
9 | const [renderSpeed, setRenderSpeed] = useState(50)
10 | const [isRunning, setIsRunning] = useState(false)
11 | const [isLoading, setIsLoading] = useState(false)
12 | const [jelly, setJelly] = useState(false)
13 |
14 | return (
15 |
16 |
26 |
34 |
35 | );
36 | }
37 |
38 | export default App;
39 |
--------------------------------------------------------------------------------
/react-frontend/src/components/code-editor.tsx:
--------------------------------------------------------------------------------
1 | import React, {Fragment} from 'react'
2 | import {Controlled as CodeMirror} from 'react-codemirror2'
3 | import './codemirror.css'
4 |
5 | require("codemirror/theme/material.css");
6 | require("codemirror/theme/dracula.css");
7 | require("codemirror/mode/python/python");
8 | require("codemirror/lib/codemirror.css");
9 |
10 | const DEFAULT_PYTHON_OPTIONS = {
11 | autoCloseBrackets: true,
12 | mode: "python",
13 | lineNumbers: true,
14 | class: "CodeMirror"
15 | };
16 |
17 |
18 | const CodeEditor = (props: any) => {
19 | return
20 |
21 | {
30 | // Execute anything before onChange
31 | props.setCode(value)
32 | }}
33 | />
34 |
35 |
36 | }
37 |
38 | export default CodeEditor
--------------------------------------------------------------------------------
/react-frontend/src/components/codemirror.css:
--------------------------------------------------------------------------------
1 | .CodeMirror {
2 | font-size: 20px;
3 | resize: vertical;
4 | overflow: auto;
5 | border: 4px solid #b6b4b4;
6 | }
--------------------------------------------------------------------------------
/react-frontend/src/components/controls.css:
--------------------------------------------------------------------------------
1 | .ControlPanel {
2 | width: 100%;
3 | background-color: #b6b4b4;
4 | height: 60px;
5 | }
--------------------------------------------------------------------------------
/react-frontend/src/components/controls.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import React from "react";
3 | import { Button, Slider } from "@material-ui/core";
4 | import Switch from '@material-ui/core/Switch';
5 | import FormControlLabel from '@material-ui/core/FormControlLabel';
6 | import "./controls.css";
7 |
8 | const Controls = (props: any) => {
9 |
10 | const handleChange = () => {
11 | props.setJelly(!props.jelly)
12 | }
13 |
14 | const onSlide = (e: any, newValue: number | number[]) => {
15 | props.setRenderSpeed(newValue)
16 | }
17 |
18 | return (
19 |
20 |
30 |
40 |
41 | }
48 | label="Jelly"
49 | />
50 |
51 |
59 |
60 | );
61 | };
62 |
63 | export default Controls;
--------------------------------------------------------------------------------
/react-frontend/src/components/placeholder.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import Card from "@material-ui/core/Card";
4 | import CardContent from "@material-ui/core/CardContent";
5 | import Typography from "@material-ui/core/Typography";
6 |
7 | const useStyles = makeStyles({
8 | root: {
9 | maxWidth: 593,
10 | },
11 | bullet: {
12 | display: "inline-block",
13 | margin: "0 2px",
14 | transform: "scale(0.8)",
15 | },
16 | title: {
17 | fontSize: 14,
18 | },
19 | pos: {
20 | marginBottom: 12,
21 | },
22 | });
23 |
24 | export default function OutlinedCard() {
25 | const classes = useStyles();
26 |
27 | return (
28 |
33 |
34 |
35 | Instructions
36 |
37 |
38 |
39 | Made with ❤️ by Jack He
40 | Please consider subscribing to my{" "}
41 |
42 | YouTube channel.
43 |
44 |
45 |
46 |
47 | 1. Write Code
48 |
49 |
54 | - You MUST define EXACTLY 1 recursive function
55 |
56 | - NO empty return statements. You gotta return something.
57 |
58 | - DO NOT include any 'print(...)' statements!
59 |
60 | - Be sure to call your function in the end.
61 |
62 | - Make sure the code you write follows correct syntax and indentation
63 | (Python 3.8)
64 |
65 |
66 | 2. Run Code
67 |
68 |
73 | - If all goes well, you will see a nice recursive visualization of
74 | your function
75 | - Green node == initial call. Red node == recursive call. Blue node ==
76 | base case.
77 | - Turn 'Jelly' off for a static graph
- Click on any Node and
78 | view it's return value
79 |
80 |
81 |
82 |
90 |
91 |
92 | );
93 | }
94 |
--------------------------------------------------------------------------------
/react-frontend/src/components/userinput.tsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState } from "react";
2 | import Controls from "./controls";
3 | import CodeEditor from "./code-editor";
4 | import {
5 | getFuncName,
6 | getFuncCall,
7 | truncateFuncCall,
8 | } from "../util/utility-functions";
9 |
10 | const UserInput = (props: any) => {
11 | const [submittedCode, setCode] = useState(
12 | "# DO NOT include any 'print(...)' statements! \n\ndef fib(x):\n if x == 1 or x == 2:\n return 1\n return fib(x - 1) + fib(x - 2)\n\nfib(10)"
13 | );
14 |
15 | const runCode = async () => {
16 | let code = "";
17 | for (let i = 0; i < submittedCode.length; i++) {
18 | if (submittedCode[i] === "\t") {
19 | code += " ";
20 | } else {
21 | code += submittedCode[i];
22 | }
23 | }
24 | // extract function name
25 | const functionName = getFuncName(code);
26 | if (functionName === -1) {
27 | alert(
28 | "Make sure you have at least one function defined, and your function name is <= 8 characters."
29 | );
30 | return;
31 | }
32 | console.log("function name: " + functionName);
33 | // extract function call. Result will be ['call', [start index, end index]]
34 | const functionCall = getFuncCall(code, functionName);
35 | if (functionCall === -1) {
36 | alert("Make sure you call your function");
37 | return;
38 | }
39 | console.log("function call: " + functionCall);
40 | // truncate function call
41 | const codeToSend = truncateFuncCall(code, functionCall);
42 | console.log("code to send: " + codeToSend);
43 | props.setIsLoading(true)
44 | let callTrace;
45 | let noError = true;
46 | try {
47 | // send HTTP request to flask server and store
48 | // response inside callTrace
49 | const domain = "https://recursion0r94s8df984.herokuapp.com";
50 |
51 | const options = `/execute?funcName=${functionName}&funcCall=${functionCall[0]}`;
52 | const fetchConfig = {
53 | method: "POST",
54 | body: codeToSend,
55 | };
56 | let response = await fetch(domain + options, fetchConfig);
57 | callTrace = await response.text();
58 | if (!response.ok) {
59 | throw new Error(callTrace);
60 | }
61 | } catch (e) {
62 | // if and when there is some error executing the python code on the server,
63 | // the flask server will send back some sort of error response. We will
64 | // catch that case here and alert the user accordingly
65 | alert("There was an error executing your code. Details: " + e);
66 | noError = false;
67 | }
68 | props.setIsLoading(false)
69 | console.log(callTrace)
70 | if (callTrace && noError) {
71 | // render callTrace
72 | props.setIsRunning(true)
73 | callTrace = callTrace.replace("'", "");
74 | const arrOfStr = callTrace.split("|");
75 | const arrOfCalls: string[][] = [];
76 | console.log(arrOfCalls);
77 | for (let i = arrOfStr.length - 1; i >= 0; i--) {
78 | arrOfCalls.push(arrOfStr[i].split(":"));
79 | }
80 | props.setArrOfCalls(arrOfCalls);
81 | props.setFuncName(functionName);
82 | }
83 | };
84 |
85 | return (
86 |
87 |
88 |
97 |
98 | );
99 | };
100 |
101 | export default UserInput;
102 |
--------------------------------------------------------------------------------
/react-frontend/src/components/visualizer.css:
--------------------------------------------------------------------------------
1 |
2 | .circle {
3 | margin: auto;
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | margin-top: 100px;
8 | }
9 |
10 | .line {
11 | display: flex;
12 | justify-content: center;
13 | align-items: center;
14 | }
15 |
16 | .circleLine {
17 | position: relative;
18 | display: flex;
19 | justify-content: center;
20 | align-items: center;
21 | border: 4px solid white;
22 | border-radius: 50%;
23 | }
24 |
25 | .circleBig {
26 | height: 80px;
27 | width: 80px;
28 |
29 | }
30 |
31 | .circleSmall {
32 | height: 20px;
33 | width: 20px;
34 | }
35 |
36 | .twoQuarterBig {
37 | width: 60px;
38 | height: 65px;
39 | border: 4px solid white;
40 | border-top: 4px solid transparent;
41 | border-bottom: 4px solid transparent;
42 | border-radius: 50%;
43 | animation: spin 3s infinite;
44 |
45 | }
46 |
47 | .twoQuarterSmall {
48 | width: 40px;
49 | height: 40px;
50 | border: 4px solid white;
51 | border-left: 4px solid transparent;
52 | border-right: 4px solid transparent;
53 | border-radius: 50%;
54 | animation: spin 6s infinite;
55 |
56 | }
57 |
58 | .lineSmallLeft {
59 | width: 40px;
60 | height: 0px;
61 | margin-left: 100px;
62 | border-bottom: 4px solid white;
63 | position: absolute;
64 |
65 |
66 | }
67 |
68 | .lineSmallRight{
69 | width: 40px;
70 | height: 0px;
71 | margin-right: 100px;
72 | border-bottom: 4px solid white;
73 | position: absolute;
74 | }
75 |
76 | .lineBigUp {
77 | width: 4px;
78 | height: 210px;
79 | margin-top: 120px;
80 | border-top: 50px solid white;
81 | position: absolute;
82 | }
83 |
84 | .lineBigDown {
85 | width: 4px;
86 | height: 210px;
87 | margin-bottom: 120px;
88 | border-bottom: 50px solid white;
89 | position: absolute;
90 | }
91 |
92 |
93 | @keyframes spin {
94 | 100% {
95 | transform: rotateZ(360deg);
96 | }
97 | }
--------------------------------------------------------------------------------
/react-frontend/src/components/visualizer.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, Fragment } from 'react'
2 | import { Graph } from 'react-d3-graph'
3 | import { parseNodesFromCalls, parseEdgesFromNodes } from '../util/graph-util'
4 | import OutlinedCard from './placeholder'
5 | import './visualizer.css'
6 |
7 |
8 | const Visualizer = (props) => {
9 | /*
10 | code here to always run whenever callTrace changes.
11 | incrementally add values from callTrace to renderTrace
12 | */
13 | const funcName = props.name
14 | const callTrace = props.callTrace
15 | const ANIMATION_SPEED = 1100 - props.renderSpeed * 10; // milliseconds per interval
16 | const [graphState, setGraphState] = useState({
17 | nodes: [],
18 | links: []
19 | })
20 |
21 | useEffect(() => {
22 | /**
23 | * Call utility functions here to transform callTrace into a list of nodes.
24 | * Also transform the list of nodes into a list of edges.
25 | * Set nodes to list of nodes.
26 | * Incrementally add edge by edge to links from list of edges.
27 | */
28 | setGraphState({
29 | nodes: [],
30 | links: []
31 | })
32 | const listOfNodes = parseNodesFromCalls(callTrace, funcName)
33 | const listOfEdges = parseEdgesFromNodes(listOfNodes)
34 | let interval
35 | let nodes = []
36 | let links = []
37 | if (listOfNodes.length > 0) {
38 | nodes.push(listOfNodes.shift())
39 | setGraphState({
40 | nodes: nodes,
41 | links: []
42 | })
43 | } else {
44 | setGraphState({
45 | nodes: [],
46 | links: []
47 | })
48 | }
49 | interval = setInterval(() => {
50 | if (listOfEdges.length > 0) {
51 | // incrementally takes an item from listOfEdges and adds them to graphState.nodes
52 | nodes.push(listOfNodes.shift())
53 | links.push(listOfEdges.shift())
54 | setGraphState({
55 | nodes: nodes,
56 | links: links
57 | })
58 | } else {
59 | // eslint-disable-next-line
60 | props.setIsRunning(false)
61 | clearInterval(interval)
62 | }
63 | }, ANIMATION_SPEED);
64 | return () => clearInterval(interval);
65 | // eslint-disable-next-line
66 | }, [callTrace, funcName, ANIMATION_SPEED]);
67 | const myConfig = {
68 | nodeHighlightBehavior: true,
69 | node: {
70 | color: "red",
71 | size: 500,
72 | highlightStrokeColor: "blue",
73 | fontSize: 30,
74 | highlightFontSize: 50,
75 | labelPosition: "top",
76 | labelProperty: "label",
77 | fontColor: "white"
78 | },
79 | d3: {
80 | gravity: -1000
81 | },
82 | link: {
83 | highlightColor: "yellow",
84 | },
85 | directed: true,
86 | width: window.innerWidth * 2,
87 | height: window.innerHeight,
88 | staticGraph: !props.jelly
89 | };
90 | // include methods to manipulate the nodes
91 | const onClickNode = (nodeId) => {
92 | let nodesSoFar = graphState.nodes
93 | for (let i = 0; i < nodesSoFar.length; i++) {
94 | if (nodesSoFar[i].id === nodeId) {
95 | const originalColor = nodesSoFar[i].color
96 | nodesSoFar[i] = {
97 | id: nodesSoFar[i].id,
98 | caller: nodesSoFar[i].caller,
99 | result: nodesSoFar[i].result,
100 | label: nodesSoFar[i].result,
101 | color: "cyan",
102 | size: nodesSoFar[i].size
103 | }
104 | setGraphState({
105 | nodes: nodesSoFar,
106 | links: graphState.links
107 | })
108 | setTimeout(() => {
109 | nodesSoFar[i] = {
110 | id: nodesSoFar[i].id,
111 | caller: nodesSoFar[i].caller,
112 | result: nodesSoFar[i].result,
113 | label: nodesSoFar[i].id,
114 | color: originalColor,
115 | size: nodesSoFar[i].size
116 | }
117 | setGraphState({
118 | nodes: nodesSoFar,
119 | links: graphState.links
120 | })
121 | }, 3000);
122 | break
123 | }
124 | }
125 | }
126 |
127 | let placeHolder;
128 |
129 | if (props.isLoading) {
130 | console.log(props.isLoading)
131 | placeHolder = (
132 |
145 | )
146 | } else {
147 | // put promo stuff and instructions here
148 | placeHolder = (
149 |
150 | )
151 | }
152 |
153 | return
154 | {
155 | graphState.nodes.length > 0 && !props.isLoading ?
156 | :
162 | placeHolder
163 | }
164 |
165 | }
166 |
167 | export default Visualizer
--------------------------------------------------------------------------------
/react-frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/react-frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want your app to work offline and load faster, you can change
15 | // unregister() to register() below. Note this comes with some pitfalls.
16 | // Learn more about service workers: https://bit.ly/CRA-PWA
17 | serviceWorker.unregister();
18 |
--------------------------------------------------------------------------------
/react-frontend/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/react-frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/react-frontend/src/serviceWorker.ts:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | type Config = {
24 | onSuccess?: (registration: ServiceWorkerRegistration) => void;
25 | onUpdate?: (registration: ServiceWorkerRegistration) => void;
26 | };
27 |
28 | export function register(config?: Config) {
29 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
30 | // The URL constructor is available in all browsers that support SW.
31 | const publicUrl = new URL(
32 | process.env.PUBLIC_URL,
33 | window.location.href
34 | );
35 | if (publicUrl.origin !== window.location.origin) {
36 | // Our service worker won't work if PUBLIC_URL is on a different origin
37 | // from what our page is served on. This might happen if a CDN is used to
38 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
39 | return;
40 | }
41 |
42 | window.addEventListener('load', () => {
43 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
44 |
45 | if (isLocalhost) {
46 | // This is running on localhost. Let's check if a service worker still exists or not.
47 | checkValidServiceWorker(swUrl, config);
48 |
49 | // Add some additional logging to localhost, pointing developers to the
50 | // service worker/PWA documentation.
51 | navigator.serviceWorker.ready.then(() => {
52 | console.log(
53 | 'This web app is being served cache-first by a service ' +
54 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
55 | );
56 | });
57 | } else {
58 | // Is not localhost. Just register service worker
59 | registerValidSW(swUrl, config);
60 | }
61 | });
62 | }
63 | }
64 |
65 | function registerValidSW(swUrl: string, config?: Config) {
66 | navigator.serviceWorker
67 | .register(swUrl)
68 | .then(registration => {
69 | registration.onupdatefound = () => {
70 | const installingWorker = registration.installing;
71 | if (installingWorker == null) {
72 | return;
73 | }
74 | installingWorker.onstatechange = () => {
75 | if (installingWorker.state === 'installed') {
76 | if (navigator.serviceWorker.controller) {
77 | // At this point, the updated precached content has been fetched,
78 | // but the previous service worker will still serve the older
79 | // content until all client tabs are closed.
80 | console.log(
81 | 'New content is available and will be used when all ' +
82 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
83 | );
84 |
85 | // Execute callback
86 | if (config && config.onUpdate) {
87 | config.onUpdate(registration);
88 | }
89 | } else {
90 | // At this point, everything has been precached.
91 | // It's the perfect time to display a
92 | // "Content is cached for offline use." message.
93 | console.log('Content is cached for offline use.');
94 |
95 | // Execute callback
96 | if (config && config.onSuccess) {
97 | config.onSuccess(registration);
98 | }
99 | }
100 | }
101 | };
102 | };
103 | })
104 | .catch(error => {
105 | console.error('Error during service worker registration:', error);
106 | });
107 | }
108 |
109 | function checkValidServiceWorker(swUrl: string, config?: Config) {
110 | // Check if the service worker can be found. If it can't reload the page.
111 | fetch(swUrl, {
112 | headers: { 'Service-Worker': 'script' }
113 | })
114 | .then(response => {
115 | // Ensure service worker exists, and that we really are getting a JS file.
116 | const contentType = response.headers.get('content-type');
117 | if (
118 | response.status === 404 ||
119 | (contentType != null && contentType.indexOf('javascript') === -1)
120 | ) {
121 | // No service worker found. Probably a different app. Reload the page.
122 | navigator.serviceWorker.ready.then(registration => {
123 | registration.unregister().then(() => {
124 | window.location.reload();
125 | });
126 | });
127 | } else {
128 | // Service worker found. Proceed as normal.
129 | registerValidSW(swUrl, config);
130 | }
131 | })
132 | .catch(() => {
133 | console.log(
134 | 'No internet connection found. App is running in offline mode.'
135 | );
136 | });
137 | }
138 |
139 | export function unregister() {
140 | if ('serviceWorker' in navigator) {
141 | navigator.serviceWorker.ready
142 | .then(registration => {
143 | registration.unregister();
144 | })
145 | .catch(error => {
146 | console.error(error.message);
147 | });
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/react-frontend/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/react-frontend/src/util/graph-util.js:
--------------------------------------------------------------------------------
1 | export function parseNodesFromCalls(calls, funcName) {
2 | /**
3 | * given an array of ["[parameter]", "result", "[caller]"],
4 | * return a list of nodes with a caller where each node is unique.
5 | * So if there are duplicate [parameter] or [caller], then add a space " ".
6 | *
7 | * example: [
8 | * {id: [1], caller: 0, result: 0},
9 | * {id: [2], caller: [1], result: 0},
10 | * {id: [3], caller: [2], result: 0}
11 | * ]
12 | *
13 | */
14 | let spaces = {}
15 | let heights = {}
16 |
17 | for (let call of calls) {
18 | spaces[call[0]] = ""
19 | }
20 | // "0" is the root caller
21 | spaces["0"] = ""
22 |
23 | let nodes = []
24 | for (let i = 0; i < calls.length; i++) {
25 | console.log(calls[i])
26 | const param = calls[i][0]
27 | const result = calls[i][1]
28 | const caller = calls[i][2]
29 | // shift a new " " with param
30 | let callerToAdd = "(" + caller.substring(1, caller.length - 1) + ")" + spaces[caller]
31 | callerToAdd = funcName + callerToAdd.substring(0, callerToAdd.length - 1)
32 | const paramToAdd = funcName + "(" + param.substring(1, param.length - 1) + ")" + spaces[param]
33 | spaces[param] += " "
34 | /**
35 | * Nodes with the same caller should have the same height.
36 | */
37 | let heightToAdd = i * 100
38 | if (callerToAdd in heights) {
39 | heightToAdd = heights[callerToAdd]
40 | } else {
41 | heights[callerToAdd] = heightToAdd
42 | }
43 | nodes.push({
44 | id: paramToAdd,
45 | caller: callerToAdd,
46 | result: result,
47 | label: paramToAdd,
48 | x: i * 100,
49 | y: heightToAdd,
50 | size: 500,
51 | color: "red"
52 | })
53 | }
54 | // give the first node extra size and change its color
55 | if (nodes.length > 0) {
56 | nodes[0].size = 800
57 | nodes[0].color = "green"
58 | }
59 | // look for all base cases and set their color to blue
60 | for (let i = 0; i < nodes.length; i++) {
61 | if (checkIsBaseCase(nodes[i], nodes)) {
62 | nodes[i].color = "blue"
63 | }
64 | }
65 | return nodes
66 | }
67 |
68 |
69 | export function parseEdgesFromNodes(listOfNodes) {
70 | /**
71 | * Assume each node in listOfNodes has:
72 | * - param
73 | * - caller
74 | * - result
75 | * link source: caller, target: param.
76 | *
77 | * PROBLEM: each object in the list must be unique.
78 | */
79 | let listOfEdges = []
80 | for (let i = 1; i < listOfNodes.length; i++) {
81 | listOfEdges.push({
82 | source: listOfNodes[i].caller,
83 | target: listOfNodes[i].id
84 | })
85 | }
86 | return listOfEdges
87 | }
88 |
89 | function checkIsBaseCase(node, nodes) {
90 | /**
91 | * given a list of nodes:
92 | * - check every other node in nodes and see if their caller is that node.
93 | * - if so, then it's not a base case.
94 | */
95 | for (let i = 0; i < nodes.length; i++) {
96 | if (node.id === nodes[i].caller) {
97 | return false
98 | }
99 | }
100 | return true
101 | }
--------------------------------------------------------------------------------
/react-frontend/src/util/utility-functions.js:
--------------------------------------------------------------------------------
1 | // getFuncName helper
2 | const findDef = (s, code) => {
3 | const lookFor = "fed"
4 | for (let i = 0; i < lookFor.length; i++) {
5 | if (!(s - i >= 0 && lookFor[i] === code[s - i])) {
6 | return false
7 | }
8 | }
9 | // we know now that s is the starting point for 'f' in 'def'.
10 | let functionName = ""
11 | for (let j = s + 1; j < code.length; j++) {
12 | if (code[j] === "(") {
13 | break
14 | }
15 | if (code[j] !== " ") {
16 | functionName += code[j]
17 | }
18 | }
19 | return functionName
20 | }
21 | /**
22 | * @param {string} code
23 | */
24 | export function getFuncName(code) {
25 | // search for 'def' and get whatever text comes after ' ' and ends at '('
26 | // if no def was found or if functionName is > 8 characters long, then return undefined.
27 | for (let i = code.length - 1; i >= 0; i--) {
28 | // parse backwards to find the word 'def'
29 | let functionName = findDef(i, code)
30 | if (functionName !== false) {
31 | return functionName
32 | }
33 | }
34 | return -1
35 | }
36 |
37 |
38 | // getFuncCall helper
39 | const reverseString = (str) => {
40 | let ans = ""
41 | for (let c of str) {
42 | ans = c + ans
43 | }
44 | return ans
45 | }
46 |
47 | // getFuncCall helper
48 | const findCall = (s, code, functionName) => {
49 | const lookFor = functionName
50 | for (let i = 0; i < lookFor.length; i++) {
51 | if (!(s - i >= 0 && lookFor[i] === code[s - i])) {
52 | return false
53 | }
54 | }
55 | // we know that at position s is the last character for functionName
56 | // check that there is no 'def' a few characters later.
57 | let check1 = s - lookFor.length - 1 >= 0 && code[s - lookFor.length - 1] === "f"
58 | let check2 = s - lookFor.length - 2 >= 0 && code[s - lookFor.length - 2] === "e"
59 | let check3 = s - lookFor.length - 3 >= 0 && code[s - lookFor.length - 3] === "d"
60 |
61 | if (check1 && check2 && check3) {
62 | return false
63 | }
64 | let functionCall = ""
65 | for (let k = s - lookFor.length; k < code.length; k++) {
66 | if (code[k] !== ")") {
67 | functionCall += code[k]
68 | } else {
69 | functionCall += ")"
70 | return functionCall
71 | }
72 | }
73 | return false
74 | }
75 | /**
76 | * @param {string} code
77 | * @param {string} functionName
78 | */
79 | export function getFuncCall(code, functionName) {
80 | // parse code in reverse, and actively search for functionName.
81 | // if found, remember index i, and go backwards some more to look for 'fed',
82 | // not counting spaces. If we encounter 3 foreign characters that are not 'fed', we
83 | // are good.
84 | // parse forward from i and collect the entire function call, ending at ")".
85 | // if no ")" was found, then return -1.
86 | // once entire function call is collected, store i, and store i + functionCall.length
87 | // return ['call', [i, i + functionCall.length]]
88 | const lookFor = reverseString(functionName)
89 | for (let i = code.length - 1; i >= 0; i--) {
90 | let call = findCall(i, code, lookFor)
91 | if (call) {
92 | return [call, [i - functionName.length, i + (call.length - functionName.length)]]
93 | }
94 | }
95 | return -1
96 | }
97 |
98 |
99 |
100 | /**
101 | *
102 | * @param {string} code
103 | * @param { -1 | (string | number[])[]} callLocation
104 | */
105 | export function truncateFuncCall(code, functionCall) {
106 | // completely remove functionCall from code via it's callLocation.
107 | // split the entire code string from [s] to [s + functionCall.length]
108 | // recombine the split parts.
109 | const callLocation = functionCall[1]
110 | let left = ""
111 | let right = ""
112 | for (let i = 0; i < callLocation[0]; i++) {
113 | left += code[i]
114 | }
115 | for (let j = callLocation[1] + 1; j < code.length; j++) {
116 | right += code[j]
117 | }
118 | return left + right
119 | }
--------------------------------------------------------------------------------
/react-frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "react"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/remote-code-runner/app.py:
--------------------------------------------------------------------------------
1 | import flask
2 | from flask import request
3 | from flask_cors import CORS
4 | import parseinput as pi
5 | import remoteCodeRunner as rc
6 | import hasImport as hi
7 |
8 | app = flask.Flask(__name__)
9 | CORS(app)
10 |
11 | # for each flask request, we need:
12 | # - data in the body to represent function logic
13 | # - data in the parameters to represent function name and function call
14 | # if either is missing, we send back an error message. If the remote
15 | # code execution raises an error, we send back that error message as well.
16 |
17 | @app.route('/execute', methods=['POST'])
18 | def execute():
19 | if 'funcName' in request.args and 'funcCall' in request.args:
20 | funcName = request.args['funcName']
21 | funcCall = request.args['funcCall']
22 | if funcName != funcCall[:len(funcName)]:
23 | print(funcName)
24 | print(funcCall[:len(funcName)] != funcName)
25 | return "Check and make sure you defined EXACTLY one function.", 400
26 | body = str(request.data)
27 | inputCode = pi.parseInput(body)
28 |
29 | # check import statements
30 | if hi.hasImport(inputCode):
31 | return "Whatever you're trying to do, just don't.", 400
32 |
33 | functionTrace = rc.runCode(inputCode, funcName, funcCall)
34 | if functionTrace[0]:
35 | # success case
36 | # set response status to 200
37 | return functionTrace[1], 200
38 | else:
39 | # case of invalid value
40 | # set reponse status to 400
41 | return functionTrace[1], 400
42 | else:
43 | # case of an invalid request
44 | return "Please specify both the function name and the initial function call."
45 |
46 | if __name__ == "__main__":
47 | app.run(port=5000)
--------------------------------------------------------------------------------
/remote-code-runner/hasImport.py:
--------------------------------------------------------------------------------
1 | def isImport(s, inputCode):
2 | check = "import"
3 | for i in range(len(check)):
4 | if check[i] != inputCode[s + i]:
5 | return False
6 | return True
7 |
8 | def hasImport(inputCode):
9 | for k in range(len(inputCode)):
10 | if isImport(k, inputCode):
11 | return True
12 | return False
--------------------------------------------------------------------------------
/remote-code-runner/injectCode.py:
--------------------------------------------------------------------------------
1 | # alwaysInject = """
2 | # class Call:
3 | # def __init__(self, params, level):
4 | # self.params = params
5 | # self.level = level
6 | # self.result = None
7 | # functionCalls_ = []
8 | alwaysInject = """
9 | class JQGRg8XBnB4:
10 | def __init__(self, params, caller):
11 | self.params = params
12 | self.caller = caller
13 | self.result = None
14 |
15 | bwmSjveL3Lc = []
16 | """
17 |
18 | # goes through our list and adds the output.
19 | # alwaysInjectLast = """
20 | # response = "|"
21 | # for call in functionCalls_:
22 | # segment = str(call.params) + ":" + str(call.result) + ":" + str(call.level) + "|"
23 | # response += segment
24 | # print(response)
25 | # """
26 | alwaysInjectLast = """
27 | ePpPVE_GGJw = "|"
28 | for call in bwmSjveL3Lc:
29 | segment = str(call.params) + ":" + str(call.result) + ":" + str(call.caller) + "|"
30 | ePpPVE_GGJw += segment
31 | print(ePpPVE_GGJw)
32 | """
33 |
34 | # injectInBeginningOfFunc1 = """
35 | # c = Call(["""
36 | # injectInBeginningOfFunc2 = """], WZwr2a_lFWY)
37 |
38 | injectInBeginningOfFunc1 = """
39 | tFRcEOmkDM8 = JQGRg8XBnB4(["""
40 | injectInBeginningOfFunc2 = """], WZwr2a_lFWY)"""
41 |
42 | # we need to add indentation to every line here accordingly.
43 | # constantLinesToAdd = "c.result = r; functionCalls_.append(c); return r"
44 | constantLinesToAdd = "tFRcEOmkDM8.result = fE2h3lGlOsk; bwmSjveL3Lc.append(tFRcEOmkDM8); return fE2h3lGlOsk;"
45 |
46 |
47 |
48 | # adds custom return statements
49 | def checkReturnStatement(inputCode, s):
50 | # start from s and check to see if
51 | # s + n is a return statement with
52 | # return values. returns that return
53 | # value and start bound and end bound
54 | # representing where the return statement begins
55 | # and where it ends.
56 | # checks return validity
57 | lookFor = "return"
58 | for i in range(len(lookFor)):
59 | if not (i + s < len(inputCode) and lookFor[i] == inputCode[i + s]):
60 | return None
61 |
62 | retVal = ""
63 | e = s
64 | for j in range(s + 7, len(inputCode)):
65 | if inputCode[j] != "\n":
66 | retVal += inputCode[j]
67 | else:
68 | e = j
69 | break
70 | return (retVal, s, e)
71 | def findReturnOutValues(inputCode):
72 | global constantLinesToAdd
73 | # looks for every return statement and adds their corresponding
74 | # output values to a list. We also want to store the location of
75 | # those return values as startbound and endbound (s, e).
76 | i = 0
77 | while i < len(inputCode):
78 | returnStatement = checkReturnStatement(inputCode, i)
79 | if returnStatement:
80 | # modify inputCode here
81 | # split input code.
82 | left = inputCode[:returnStatement[1]]
83 | right = inputCode[returnStatement[2]:]
84 | linesToAdd = "fE2h3lGlOsk = " + returnStatement[0] + "; " + constantLinesToAdd
85 | inputCode = left + linesToAdd + right
86 | i += len(linesToAdd)
87 | i += 1
88 | return inputCode
89 | def addCustomReturnStatements(inputCode):
90 | # finds every return statement, and replaces it with our custom
91 | # code
92 | inputCode = findReturnOutValues(inputCode)
93 | return inputCode
94 |
95 | # adds the 'Call() class and level += 1, and dynamically changes the input parameters to match the function call.
96 | def checkIsFuncName(inputFunctionName, inputCode, s):
97 | for i in range(len(inputFunctionName)):
98 | if not (i + s < len(inputCode) and inputFunctionName[i] == inputCode[i + s]):
99 | return False
100 | return True
101 | def untilFuncEnd(inputCode, startbound):
102 | # get list of parameters here.
103 | # returns distance until function ends AND a string of parameters.
104 | parameters = ""
105 | dtc = 0
106 | for i in range(startbound, len(inputCode)):
107 | if inputCode[i] == ":":
108 | return (dtc + 1, parameters)
109 | else:
110 | if inputCode[i] != "(" and inputCode[i] != ")":
111 | parameters += inputCode[i]
112 | dtc += 1
113 | def injectCallFunction(inputCode, inputFunctionName):
114 | global injectInBeginningOfFunc
115 | # find first function name:
116 | for s in range(len(inputCode)):
117 | if checkIsFuncName(inputFunctionName, inputCode, s):
118 | # insert injectInBeginningFunc here
119 | collection = untilFuncEnd(inputCode, s + len(inputFunctionName))
120 | leftBound = s + len(inputFunctionName) + collection[0]
121 | left = inputCode[:leftBound]
122 | right = inputCode[leftBound:]
123 | inputParameters = collection[1]
124 | injectInBeginningOfFunc = injectInBeginningOfFunc1 + inputParameters + injectInBeginningOfFunc2
125 | return [left + injectInBeginningOfFunc + right, inputParameters]
126 |
127 |
128 | # adds the 'caller' parameter to every single instance of the function call
129 | def addLevelParameter(inputCode, inputFunctionName, caller):
130 | s = 0
131 | isFirstFunc = True
132 | while s < len(inputCode):
133 | if checkIsFuncName(inputFunctionName, inputCode, s):
134 | cut = s + len(inputFunctionName) + 1
135 | left = inputCode[:cut]
136 | right = inputCode[cut:]
137 | if isFirstFunc:
138 | inputCode = left + "WZwr2a_lFWY" + ", " + right
139 | isFirstFunc = False
140 | else:
141 | inputCode = left + "[" + caller + "]" + ", " + right
142 | s += 1
143 | return inputCode
144 | def addZero(inputFunctionCall):
145 | inputFunctionCall = "\n" + inputFunctionCall
146 | for i in range(len(inputFunctionCall)):
147 | if inputFunctionCall[i] == "(":
148 | left = inputFunctionCall[:i+1]
149 | right = inputFunctionCall[i+1:]
150 | inputFunctionCall = left + "0, " + right
151 | return inputFunctionCall
152 |
153 | def injectCode(inputCode, inputFunctionName, inputFunctionCall):
154 | finalOutput = ""
155 | finalOutput += alwaysInject
156 | paramsInjection = injectCallFunction(inputCode, inputFunctionName)
157 | inputCode = paramsInjection[0]
158 | inputCode = addLevelParameter(inputCode, inputFunctionName, paramsInjection[1])
159 | finalOutput += addCustomReturnStatements(inputCode)
160 | finalOutput += addZero(inputFunctionCall)
161 | finalOutput += alwaysInjectLast
162 | return finalOutput
--------------------------------------------------------------------------------
/remote-code-runner/judge.py:
--------------------------------------------------------------------------------
1 | import os
2 | import requests
3 | import json
4 | import time
5 |
6 | API_KEY = os.environ.get('API_KEY')
7 |
8 | def sendCodeToJudge(code):
9 | print(code)
10 | print('________________________________________\n\n')
11 | url = "https://judge0.p.rapidapi.com/submissions"
12 | payload = { "language_id": 71, "source_code": code}
13 | headers = {
14 | 'x-rapidapi-host': "judge0.p.rapidapi.com",
15 | 'x-rapidapi-key': API_KEY,
16 | 'content-type': "application/json",
17 | 'accept': "application/json"
18 | }
19 | response = requests.request("POST", url, data=json.dumps(payload), headers=headers)
20 | responseObj = json.loads(response.text)
21 | if 'error' in responseObj:
22 | return False
23 | if 'token' not in responseObj:
24 | return False
25 | token = responseObj['token']
26 | time.sleep(2) # wait 2 seconds before retrieving results
27 | URL = "https://judge0.p.rapidapi.com/submissions/" + token
28 | HEADERS = {
29 | 'x-rapidapi-host': "judge0.p.rapidapi.com",
30 | 'x-rapidapi-key': API_KEY
31 | }
32 | subResponse = requests.request("GET", URL, headers=HEADERS)
33 | codeSubmissionResponse = json.loads(subResponse.text)
34 | print(codeSubmissionResponse)
35 | return [codeSubmissionResponse, token]
--------------------------------------------------------------------------------
/remote-code-runner/parseinput.py:
--------------------------------------------------------------------------------
1 | def parseInput(inputCode):
2 | # remove the b" in the beginning and make sure \n is actually a new line.
3 | inputCode = inputCode[2:]
4 | inputCode = inputCode[:-1]
5 | inputCode = inputCode.replace('\\n', '\n').replace('\\t', '\t')
6 | return inputCode
--------------------------------------------------------------------------------
/remote-code-runner/remoteCodeRunner.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | import injectCode as ij
3 | import judge as j
4 | import tryget as tg
5 |
6 | def runCode(inputCode, inputFunctionName, inputFunctionCall):
7 | # On the frontend, we will parse the input to get the
8 | # - function name
9 | # - single function call
10 | # - we will truncate this function call line and add it as a seperate parameter
11 | # All the inputs we will collect from the frontend:
12 | # inputCode here is truncated to exclude the single function call.
13 | readyToExe = ij.injectCode(inputCode, inputFunctionName, inputFunctionCall)
14 | res = j.sendCodeToJudge(readyToExe)
15 | if res == False: # couldn't even POST the user's code
16 | return (False, "Be sure to follow all rules laid out in the instructions: Make sure there are no syntax errors, logic errors, and infinite recursions... and any funky business ;)")
17 |
18 | codeSubmissionResults = res[0] # output object with stdout and stderr
19 | submissionToken = res[1] # token reference ID
20 |
21 | # do processing checks first before doing error checks
22 | if codeSubmissionResults['status']['id'] < 3:
23 | # still processing...
24 | codeSubmissionResults = tg.tryGet(submissionToken)
25 |
26 | if codeSubmissionResults['status']['id'] == 3:
27 | # success case
28 | output = codeSubmissionResults['stdout'][1:len(codeSubmissionResults['stdout']) - 2]
29 | return (True, output)
30 | else:
31 | # error case
32 | if codeSubmissionResults['stderr']:
33 | return (False, codeSubmissionResults['stderr'])
34 | else:
35 | return (False, "Unknown error.")
--------------------------------------------------------------------------------
/remote-code-runner/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask==1.1.2
2 | Flask-Cors==3.0.9
3 | requests==2.24.0
4 | gunicorn==20.0.4
--------------------------------------------------------------------------------
/remote-code-runner/tryget.py:
--------------------------------------------------------------------------------
1 | import time
2 | import os
3 | import requests
4 | import json
5 |
6 | API_KEY = os.environ.get('API_KEY')
7 |
8 | def tryGet(token):
9 | URL = "https://judge0.p.rapidapi.com/submissions/" + token
10 | HEADERS = {
11 | 'x-rapidapi-host': "judge0.p.rapidapi.com",
12 | 'x-rapidapi-key': API_KEY
13 | }
14 | subResponse = requests.request("GET", URL, headers=HEADERS)
15 | codeSubmissionResponse = json.loads(subResponse.text)
16 | retryNumber = 1
17 | while codeSubmissionResponse['status']['id'] < 3:
18 | time.sleep(2) # wait 2 seconds before trying again
19 | codeSubmissionResponse = requests.request("GET", URL, headers=HEADERS)
20 | print("retryNumber " + str(i))
21 | print(codeSubmissionResponse)
22 | i += 1
23 | return codeSubmissionResponse
--------------------------------------------------------------------------------