Frontend-Backend Separation Calculator System
Assignment Information
|
Item |
Content |
|---|---|
|
Course |
Software Engineering |
|
Assignment Name |
Development of a Frontend-Backend Separation Calculator System |
|
Assignment Goal |
Master frontend-backend separation architecture, HTTP API communication, database persistence, exception handling, and project deployment workflow |
|
Tech Stack |
Frontend: HTML + CSS + Vanilla JavaScript Backend: Python Flask + Flask-SQLAlchemy Database: SQLite |
Table of Contents
-
GitHub Repository Links
-
Code Style Guidelines
-
PSP Personal Software Process Table
-
Project Overview and Requirements Analysis
-
System Overall Design
-
Core Feature Implementation
-
Key Code Analysis
-
Feature Testing and Demo
-
Deployment Instructions
-
Development Issues, Solutions, and Reflections
GitHub Repository Links
Replace with your own repository links before submission.
Frontend Repository: https://github.com/xuntachuan/calculator-frontend
Backend Repository: https://github.com/xuntachuan/calculator-backend
Frontend Code Style: https://github.com/xuntachuan/calculator-frontend/blob/main/codestyle.md
Backend Code Style: https://github.com/xuntachuan/calculator-backend/blob/main/codestyle.md
Code Style Guidelines
The backend follows the Python PEP 8 coding standard, and the frontend follows the Airbnb JavaScript Style Guide.
Backend code uses snake_case naming with semantically clear function and variable names. Frontend JavaScript uses camelCase naming, HTML is structured in layers, and CSS styles are uniformly placed in the page head. Each repository contains a codestyle.md document that uniformly constrains indentation, comments, and naming to ensure code readability and facilitate future maintenance.
PSP Personal Software Process Table
|
Phase |
Estimated (min) |
Actual (min) |
Description |
|---|---|---|---|
|
Requirements Analysis |
20 |
15 |
Clarified the hard requirements of frontend-backend separation: the frontend only handles UI interaction, all calculation logic is implemented on the backend; history persistence and exception handling are required |
|
System Architecture Design |
20 |
25 |
Designed REST-style API endpoints, SQLite table schema, Reverse Polish Notation calculation module, and CORS handling strategy |
|
Backend Development |
90 |
110 |
Implemented tokenization, infix-to-postfix conversion, postfix expression evaluation; wrote database models, three API endpoints, and exception handling |
|
Frontend Development |
60 |
55 |
Calculator UI layout, button click expression concatenation, fetch request encapsulation, history rendering and deletion interaction |
|
Integration Testing |
40 |
45 |
Tested arithmetic operations, nested parentheses, decimals, unary minus; tested division by zero, illegal characters, mismatched parentheses; tested history add/delete |
|
Deployment |
20 |
15 |
Installed dependencies, started Flask service, verified frontend-backend CORS communication, database auto-created tables |
|
Blog Writing and Organization |
60 |
70 |
Organized architecture, code, test screenshots, and development summary |
|
Total |
310 |
335 |
—— |
Project Overview and Requirements Analysis
Project Introduction
This project implements a frontend-backend separation online calculator based on Flask + vanilla frontend. The frontend performs no calculations — it only provides a visual interface, receives user input, and sends JSON requests to the backend. The backend receives expressions and uses the Reverse Polish Notation (postfix expression) algorithm to parse and evaluate expressions, without using eval, to avoid code injection risks. After a successful calculation, the expression, result, and timestamp are stored in a SQLite database for persistent history, supporting viewing and deleting history records.
Core Hard Requirements (All Implemented)
-
Strict frontend-backend separation with HTTP + JSON API communication
-
All mathematical calculation logic resides on the backend; the frontend only handles display and request sending
-
Supports arithmetic operations, decimals, multi-level nested parentheses, and unary minus (
-5,3*-2) -
No dangerous
evalfunction — manually implemented lexical analysis + Reverse Polish Notation calculation for security -
Calculation records are persistently stored; records survive page refresh and service restart
-
Supports viewing all history records and deleting individual history records
-
Complete exception handling: division by zero, illegal characters, mismatched parentheses, malformed expressions, network errors
-
Backend enables CORS so the frontend webpage can directly access backend APIs
System Overall Design
Architecture Diagram
Module Breakdown
-
Frontend Modules
-
UI Module: calculator buttons, input field, result display area, history panel
-
Interaction Module: button expression concatenation, clear function
-
Network Request Module: send calculation requests, fetch history list, delete history records
-
Rendering Module: render history data returned by the backend to the page, auto-refresh list after deletion
-
Backend Modules
-
Lexical Tokenization Module
tokenize: splits expressions into tokens, identifies numbers, operators, unary minus, and filters illegal characters -
Infix-to-Postfix Module
infix_to_postfix: uses a stack to convert infix math expressions to Reverse Polish Notation, handling operator precedence and parentheses -
Postfix Evaluation Module
calc_postfix: evaluates postfix expressions with a stack, catching division by zero and format errors -
Database Model Module: defines the history table using Flask-SQLAlchemy
-
API Module: calculation endpoint, history query endpoint, history deletion endpoint
-
Exception Handling Module: uniformly catches various calculation exceptions and returns standardized JSON error codes
Database Design
Table: CalcRecord
|
Field |
Type |
Description |
|---|---|---|
|
id |
Integer (primary key, auto-increment) |
Unique record ID |
|
expression |
String(256) |
Original expression entered by the user |
|
result |
String(256) |
Calculation result |
|
create_time |
DateTime |
Record creation time, defaults to current UTC time |
Core Feature Implementation
Feature 1: Basic Arithmetic and Compound Expressions
The backend manually implements the Reverse Polish Notation algorithm, following mathematical operator precedence (multiplication and division before addition and subtraction). Supports multi-level nested parentheses, decimal operations, and unary minus.
Examples: (1+2.5)*(-3+7), 10/2 both calculate correctly.
Feature 2: Safe Expression Parsing (No eval)
Does not call Python's built-in eval to execute user strings. Manually tokenizes and parses syntax, intercepts illegal characters, and prevents code injection security risks.
Feature 3: History Persistence
On every successful calculation, the backend automatically stores the expression, result, and timestamp in the SQLite database. The frontend automatically requests the backend API to read history on page load; data survives page refresh because it is stored in the backend database, not dependent on frontend localStorage caching.
Feature 4: Delete Individual History Record
The frontend clicks the delete button and sends a DELETE request with the record ID to the backend. The backend queries the corresponding record; if it exists, it deletes the database record and commits the transaction. After successful deletion, the frontend auto-refreshes the history list. If the record does not exist, a 404 error is returned.
Feature 5: Complete Exception Handling
-
Division by zero: catches
ZeroDivisionErrorwhen the divisor is near zero and returns a prompt -
Illegal characters: identifies non-digit/non-operator characters during tokenization and raises an exception
-
Mismatched parentheses: detects inconsistent parenthesis counts during infix-to-postfix conversion
-
Malformed expressions: e.g., consecutive operators, missing operands
-
Network errors: the frontend catches failed fetch requests and prompts to check the backend service
Key Code Analysis
Backend: Lexical Tokenization and Reverse Polish Notation Core (No eval)
# Operator precedence
priority = {'+': 1, '-': 1, '*': 2, '/': 2}
def tokenize(expr):
"""Tokenize: split numbers and operators, handle unary minus"""
tokens = []
i = 0
n = len(expr)
while i < n:
c = expr[i]
if c.isspace():
i += 1
continue
if c in '()+ -*/':
# Unary minus detection
if c == '-' and (i == 0 or expr[i-1] in '(*+-/'):
tokens.append('u-')
else:
tokens.append(c)
i += 1
elif c.isdigit() or c == '.':
# Read number (supports decimals)
j = i
while j < n and (expr[j].isdigit() or expr[j] == '.'):
j += 1
tokens.append(expr[i:j])
i = j
else:
raise ValueError("Contains illegal characters")
return tokens
def infix_to_postfix(tokens):
"""Convert infix to postfix (Reverse Polish Notation)"""
postfix = []
op_stack = []
for t in tokens:
if t.replace('.', '').isdigit():
postfix.append(t)
elif t == '(':
op_stack.append(t)
elif t == ')':
while op_stack and op_stack[-1] != '(':
postfix.append(op_stack.pop())
if not op_stack:
raise ValueError("Mismatched parentheses")
op_stack.pop()
else: # operators + - * / u-
while op_stack and op_stack[-1] != '(' and priority.get(op_stack[-1], 3) >= priority.get(t, 3):
postfix.append(op_stack.pop())
op_stack.append(t)
while op_stack:
top = op_stack.pop()
if top == '(':
raise ValueError("Mismatched parentheses")
postfix.append(top)
return postfix
def calc_postfix(postfix):
"""Evaluate postfix expression"""
stk = []
for token in postfix:
if token == 'u-':
if not stk:
raise ValueError("Expression error")
val = float(stk.pop())
stk.append(-val)
elif token in '+-*/':
if len(stk) < 2:
raise ValueError("Expression error")
b = float(stk.pop())
a = float(stk.pop())
if token == '+':
res = a + b
elif token == '-':
res = a - b
elif token == '*':
res = a * b
elif token == '/':
if abs(b) < 1e-10:
raise ZeroDivisionError("Division by zero")
res = a / b
stk.append(res)
else:
stk.append(float(token))
if len(stk) != 1:
raise ValueError("Malformed expression")
return stk[0]
def calculate_expression(expr):
tokens = tokenize(expr)
post = infix_to_postfix(tokens)
res = calc_postfix(post)
return res
Backend API Endpoint Code
@app.post("/api/calc")
def api_calc():
data = request.get_json()
if not data or "expression" not in data:
return jsonify({"code": 400, "msg": "Missing expression parameter"}), 400
expr = data["expression"].strip()
try:
result = calculate_expression(expr)
res_str = f"{result:.6f}".rstrip('0').rstrip('.') if '.' in f"{result:.6f}" else f"{result:.6f}"
# Save to database
rec = CalcRecord(expression=expr, result=res_str)
db.session.add(rec)
db.session.commit()
return jsonify({"code": 200, "data": {"result": res_str}})
except ZeroDivisionError:
return jsonify({"code": 400, "msg": "Division by zero"}), 400
except ValueError as e:
return jsonify({"code": 400, "msg": f"Illegal expression: {str(e)}"}), 400
except Exception as e:
return jsonify({"code": 400, "msg": f"Calculation error: {str(e)}"}), 400
# Query history endpoint
@app.get("/api/history")
def api_get_history():
records = CalcRecord.query.order_by(CalcRecord.create_time.desc()).all()
return jsonify({"code": 200, "data": [r.to_dict() for r in records]})
# Delete history endpoint
@app.delete("/api/history/<int:rid>")
def api_del_history(rid):
rec = CalcRecord.query.get(rid)
if not rec:
return jsonify({"code": 404, "msg": "Record not found"}), 404
db.session.delete(rec)
db.session.commit()
return jsonify({"code": 200, "msg": "Deleted successfully"})
Frontend Core JavaScript Code
// Backend URL, modify according to actual situation
const BACKEND_URL = "http://127.0.0.1:5000";
const exprInput = document.getElementById("input-exp");
const resultDiv = document.getElementById("result");
const historyWrap = document.getElementById("history-list");
window.onload = () => {
loadHistory();
}
function appendChar(c) {
exprInput.value += c;
}
function clearAll() {
exprInput.value = "";
resultDiv.innerText = "Result";
}
// Click equals, request backend to calculate
async function calcEqual() {
const expr = exprInput.value.trim();
if (!expr) {
resultDiv.innerText = "Please enter an expression";
return;
}
try {
const resp = await fetch(`${BACKEND_URL}/api/calc`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({expression: expr})
})
const json = await resp.json();
if (json.code === 200) {
resultDiv.innerText = json.data.result;
loadHistory(); // Refresh history after successful calculation
} else {
resultDiv.innerText = json.msg;
}
} catch (err) {
resultDiv.innerText = "Network request failed, please check the backend";
console.error(err);
}
}
// Fetch history list
async function loadHistory() {
try {
const resp = await fetch(`${BACKEND_URL}/api/history`);
const json = await resp.json();
if (json.code === 200) {
renderHistory(json.data);
}
} catch (e) {
historyWrap.innerHTML = "<p>Failed to load history</p>";
}
}
// Delete a single record
async function delRecord(id) {
if (!confirm("Are you sure you want to delete this record?")) return;
try {
await fetch(`${BACKEND_URL}/api/history/${id}`, {method: "DELETE"});
loadHistory();
} catch (e) {
alert("Delete failed");
}
}
function renderHistory(list) {
if (list.length === 0) {
historyWrap.innerHTML = "<p>No records yet</p>";
return;
}
let html = "";
list.forEach(item => {
html += `
<div class="history-item">
<div>${item.expression} = ${item.result}<br><small>${item.create_time}</small></div>
<button class="del-btn" onclick="delRecord(${item.id})">Delete</button>
</div>
`
})
historyWrap.innerHTML = html;
}
Feature Testing and Demo








-
Calculator main interface: left-right layout, calculator button panel on the left, history panel on the right; supports clicking buttons to input expressions and manual input.
-
Basic arithmetic test:
1+2,10-3.5,4*5,9/2all produce correct results. -
Compound expression and parentheses test:
(1+2)*3,1+2*3— precedence follows mathematical rules; supports multi-level parentheses. -
Unary minus test:
-5+9,4*-2— minus sign is correctly recognized. -
Division by zero test: entering
10/0, the backend catchesZeroDivisionErrorand the frontend displays a division by zero error prompt. -
Illegal expression test: mismatched parentheses, illegal characters — the backend returns corresponding error messages.
-
History persistence test: calculations are automatically saved to the database; after page refresh, history records remain.
-
Delete record test: clicking the delete button, after confirmation the database record is deleted and the page history list auto-updates; deleting a non-existent ID returns 404.
-
Exception scenario test: when the backend is not running, the frontend prompts network request failure.
Deployment Instructions
-
Install backend dependencies
pip install flask flask-sqlalchemy flask-cors
-
Run the backend program:
python main.py. The backend starts athttp://127.0.0.1:5000and automatically creates thecalc_history.dbdatabase file. -
Open the frontend HTML file directly in a browser. Ensure
BACKEND_URLin the frontend JavaScript matches the backend address. -
The backend enables CORS, so local webpages can directly access the API without additional proxy configuration.
Development Issues, Solutions, and Reflections
Issues Encountered
-
Difficulty in recognizing unary minus
Initially, the tokenization module could not distinguish between the subtraction operator and the negative sign, e.g., 3*-2 was parsed incorrectly. Solution: added detection logic — when - appears at the beginning of the expression or after (, *, +, it is judged as a unary minus, marked as u-, and handled separately during postfix evaluation.
-
Frontend-backend CORS request error
When the frontend opened the HTML and accessed the backend API, the browser reported a CORS restriction. Solution: the backend introduced flask-cors and executed CORS(app) to enable global CORS, allowing the frontend webpage to access the API.
-
Handling mismatched parentheses in Reverse Polish Notation
When a user entered (1+2 (missing closing parenthesis), the program crashed directly. Solution: during the infix-to-postfix stack operation, detect leftover opening parentheses and actively raise a ValueError with "Mismatched parentheses", which is uniformly caught and returned to the frontend.
-
Database time formatting
The database stores UTC time. The to_dict function formats the time, converting datetime to a readable string that the frontend directly displays in YYYY-MM-DD HH:MM:SS format.
-
Trailing zeros in floating-point results
Calculation results like 4.0 were desired to be simplified to 4. Solution: format the result string to automatically strip trailing zeros and decimal points for better display.
ReflectionsThis assignment used Flask to build the backend and manually implemented a Reverse Polish Notation calculator, fully experiencing the frontend-backend separation architecture. The frontend is only responsible for view display, while all business logic, security validation, and data persistence are handed to the backend, achieving separation of concerns.
Compared to directly using eval, manually implementing lexical analysis and stack-based expression evaluation helped me understand the underlying principles of expression parsing while avoiding the security risk of code injection. Through REST-style APIs and JSON data interaction, I became familiar with the engineering practices of interface design, CORS, database ORM operations, and exception handling.
During the integration process, I also learned frontend-backend joint debugging and troubleshooting, distinguishing between frontend page errors, network errors, and backend business exceptions. The entire project followed the software engineering process — from requirements analysis, architecture design, coding, testing to documentation writing — with standardized development that enhanced my engineering development mindset.
相关推荐
高校技术转移办公室人员如何提升技术成果评价的客观性与效率?.docx
科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。
高校技术转移办公室人员如何高效完成科技成果评价与转化推荐?.docx
高校技术转移办公室人员如何高效完成科技成果评价与转化推荐?
Delphi 13.2控件之扫描大师PRO-推广介绍.zip
Delphi 13.2控件之扫描大师PRO-推广介绍.zip
基于MIT-BIH数据评估的FIR与IIR带通滤波在心电图信号去噪中的比较研究.zip
1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
网络存储技术应用项目教程(微课视频版)教学大纲+课程标准+PPT课件+教案+习题
网络存储技术应用项目教程(微课视频版)教学大纲+课程标准+PPT课件+教案+习题


