832401302 蔡耀华 Software Engineering Practice Assignment 1: A Frontend–Backend Separated Calculator
1. Course and Assignment Information
| Item | Details |
|---|---|
| Student ID | 832401302 |
| Name | Yaohua Cai |
| Course | EE308FZ Software Engineering Practice |
| Assignment requirements | Assignment 1: Personal Programming Project |
| Assignment objective | Build a frontend–backend separated calculator with backend-side computation, persistent history, selected-record deletion, local deployment, and engineering documentation. |
| References | Official documentation of Flask, Python, SQLite, pytest, and the course assignment specification. |
This project decomposes the calculator into three independent layers: the frontend handles user input and result display, the backend parses expressions and performs all arithmetic, and the database persistently stores successful calculation records. The architecture strictly follows the frontend-backend separation principle.
2. Project Repositories and Coding Standards
| Item | Description |
|---|---|
| Frontend repository | 832401302_calculator_frontend |
| Backend repository | 832401302_calculator_backend |
| Frontend coding standard | codestyle.md (follows Google JavaScript Style Guide) |
| Backend coding standard | codestyle.md (follows PEP 8 Python Style Guide) |
| Demonstration addresses | Frontend: http://127.0.0.1:5500 (local static server) ; Backend: http://127.0.0.1:5000 |
The frontend repository contains page structure, styles, and interaction logic. The backend repository contains the Flask API service, expression parser, SQLite operations, and unit tests. The database file is generated at runtime and is not committed to source control.
3. PSP Time Record and Analysis
The table below records estimated and actual time spent on each stage, and explains the main deviations.
| Stage | Estimated time (min) | Actual time (min) | Reason for deviation |
|---|---|---|---|
| Requirements reading & analysis | 30 | 40 | Repeatedly confirmed the responsibility boundary between frontend and backend, and all mandatory features. |
| System architecture design | 45 | 60 | Supplemented interface field definitions, database schema, and error code specifications. |
| Frontend page & interaction | 120 | 160 | Optimized history panel style, added keyboard shortcuts, and adapted dark mode. |
| Backend API development | 100 | 130 | Improved calculation, history query, single delete, clear-all interfaces and exception handling. |
| Expression parser development | 80 | 110 | Debugged edge cases: operator precedence, nested parentheses, unary signs, and percentage suffix. |
| Database & history features | 50 | 70 | Tuned parameterized SQL for insert, query, single delete and clear operations. |
| Frontend-backend integration | 60 | 90 | Troubleshooted CORS issues, interface field alignment, and unified request/response formats. |
| Testing & bug fixes | 70 | 95 | Added test cases for illegal input, division by zero, persistence, and fixed corresponding bugs. |
| Deployment & verification | 20 | 30 | Configured local dual-port services and verified the full workflow. |
| Blog writing | 100 | 150 | Organized structure, prepared screenshots, typesetting and technical description. |
| Total | 675 | 935 | — |
The actual total time is higher than estimated. The main deviations come from edge case debugging of expression parsing, frontend-backend interface integration, and document typesetting.
4. Product Demonstration
4.1 Main Interface
The calculator has an expression input area and keypad on the left, and a history record panel on the right. The top shows the backend connection status, and the bottom shows keyboard shortcut hints. All history records are loaded from the backend database and persist after page refresh.

Figure 1: Main interface of the calculator.
4.2 Basic Arithmetic
The system supports four basic arithmetic operations: addition, subtraction, multiplication and division.
- Input
12+8, backend returns20 - Input
10-3, backend returns7 - Input
6*7, backend returns42 - Input
10/4, backend returns2.5
Figure 2: Basic addition calculation result
Figure 3: Basic subtraction calculation result
Figure 4: Basic multiplication calculation result
Figure 5: Basic division calculation result.
4.3 Compound Expressions and Operator Precedence
The parser strictly follows mathematical precedence rules, and supports nested parentheses and unary plus/minus signs.
- Input
1+2*3: multiplication is evaluated first, result is7 - Input
(1+2)*3: parentheses change evaluation order, result is9 - Input
-5+8: supports unary minus at the start of expression, result is3
Figure 6: Operator precedence verification.
Figure 7: Parentheses verification.
Figure 8: Unary sign verification.
4.4 Error Handling
The backend uniformly validates all illegal inputs and returns error messages. Invalid requests are never written to the database.
- Input
1/0: returns "Division by zero is not allowed" - Mismatched parentheses and incomplete expressions also return corresponding error prompts
Figure 9: Division-by-zero error handling.
4.5 History Persistence
After each successful calculation, the expression, result and timestamp are automatically saved into the backend SQLite database. After refreshing the browser page, the history list is reloaded from the GET /api/history interface, proving data persistence.
Figure 10: History list loaded from backend database.
4.6 Selected Record Deletion
The system supports deleting a single history record by ID. Click the Delete button next to a record, the frontend calls DELETE /api/history/{id}, the backend removes the corresponding row from the database, and the list reloads. Other records remain unchanged.
Figure 14: Single record deletion operation
5. Requirements Analysis
| Assignment requirement | Implementation status | Main evidence |
|---|---|---|
| Basic arithmetic operations | Implemented | Backend parses + - * / and returns results via POST /api/calculate |
| Compound expressions | Implemented | Supports precedence, parentheses, decimals, unary signs, percentages |
| Error handling | Implemented | Rejects illegal characters, malformed expressions, unmatched parentheses, division by zero |
| History records | Implemented | Successful calculations are written to SQLite and loaded via API |
| Selected record deletion | Implemented | Delete a single record by ID via DELETE /api/history/{id} |
| Frontend–backend separation | Implemented | Frontend only submits raw expressions; all computation is done by backend |
6. Design and Implementation6.1 Feature Structure
6.1 Feature Structure

6.2 Overall Architecture

Complete flow of one calculation:
- Browser reads the raw expression from the input field
- Frontend sends a
POST /api/calculaterequest viafetchAPI - Flask backend receives the expression, performs syntax validation and numerical calculation
- If calculation succeeds, the record is inserted into the SQLite database
- Backend returns the calculation result and record ID in JSON format
- Frontend displays the result and reloads the history list
6.3 Frontend Design
The frontend is implemented with native HTML, CSS and JavaScript, without third-party frameworks. When the equals button is clicked, the event handler reads the expression and sends the request. After receiving the response, it updates the page. History loading, deletion and clearing are all done through API calls.
Search, favorites, theme switching and display precision are interaction-layer features. The precision setting only changes the display format on the page; it never modifies the original calculation results in the database.
6.4 Backend Design
The backend provides RESTful APIs based on Flask, and is divided into three layers:
| Layer | Responsibility |
|---|---|
| Routing layer | Receives JSON requests, calls business logic, builds unified responses |
| Expression parser | Uses Shunting-yard algorithm to handle numbers, parentheses, precedence, unary operators and percentages |
| Database layer | Implements history CRUD with SQLite and parameterized SQL |
The expression parser never uses eval(), exec() or any arbitrary code execution functions. It only accepts numbers and predefined operators, and returns errors for any other characters, ensuring security.
6.5 API Design
| Method | Path | Purpose |
|---|---|---|
GET | /api/health | Backend health check, verify if service is running |
POST | /api/calculate | Submit expression for calculation; save record on success |
GET | /api/history | Query all history records, ordered by time descending |
DELETE | /api/history/{id} | Delete a single history record by ID |
DELETE | /api/history | Clear all history records |
Calculation request example:
{
"expression": "(1+2)*3"
}
Successful response example:
{
"success": true,
"id": 1,
"expression": "(1+2)*3",
"result": 9,
"created_at": "2026-10-07T17:00:00+08:00"
}
Failed response example:
{
"success": false,
"message": "Division by zero is not allowed"
}
6.6 Database Design
The backend automatically creates the calculation_history table on startup:
| Field | Type | Purpose |
|---|---|---|
id | INTEGER PRIMARY KEY AUTOINCREMENT | Unique record identifier |
expression | TEXT NOT NULL | Raw expression submitted by user |
result | TEXT NOT NULL | Result calculated by backend |
created_at | TEXT NOT NULL | Timestamp when calculation finished |
A record is inserted only after the expression is successfully parsed. Invalid expressions and division-by-zero requests return errors before insertion. Delete operations use parameterized SQL and locate the target row by record ID.
6.7 Expression Parsing Principle
The Shunting-yard algorithm converts infix expressions to Reverse Polish Notation (RPN), then evaluates using a stack. This natively supports operator precedence and parentheses.Grammar hierarchy:
Percentage is processed as a postfix operator: 50% is converted to 0.5; in 200*5%, 5% becomes 0.05, so the final result is 10. During division, the parser raises an error if the right-hand operand is 0.
7. Key Code Notes
7.1 Frontend Calculation Request
const response = await fetch(`${API_BASE}/calculate`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ expression })
});
The frontend only transmits the raw expression string. All computation is performed by the backend. After receiving a successful response, the frontend updates the result area and refreshes the history list.
7.2 Save Record After Successful Calculation
value = evaluate_expression(expression)
result_str = format_result(value)
created_at = datetime.now().isoformat()
cursor.execute(
"INSERT INTO calculation_history(expression, result, created_at) VALUES (?, ?, ?)",
(expression, result_str, created_at)
)
Database insertion happens only after parsing succeeds. Values are passed through ? placeholders to avoid SQL injection risks.
7.3 Operator Precedence Handling
Multiplication and division are processed first in parse_term(), while addition and subtraction are handled in parse_expression(). Therefore 1+2*3 first computes 2*3=6, then 1+6=7. Parentheses and unary signs are handled recursively in parse_primary() and parse_unary().
8. Test Results and Limitations
8.1 Automated Tests
The backend uses pytest for expression parser and API tests. All tests pass locally:
| Test category | Coverage |
|---|---|
| Expression tests | Precedence, parentheses, decimals, unary signs, percentages |
| Error tests | Empty input, division by zero, incomplete expressions, illegal characters, unmatched parentheses |
| API tests |
Calculation, history query, single delete, clear history, persistence after restart |
8.2 Known Limitations
- Favorite status is only stored in the current browser's
localStorage, not synchronized across browsers. - Current deployment uses local Flask and SQLite, with the database file in the backend project directory. Cloud deployment would require a separate persistence design.
- Display precision is a frontend formatting option and cannot change the numeric precision of JavaScript or the database.
- History currently has no pagination or user isolation, intended for local single-user demonstration.
9. Deployment and Run Instructions
9.1 Start the Backend
Execute in the backend project root directory:
pip install -r requirements.txt
cd src
python app.py
The backend listens on http://127.0.0.1:5000 by default.
9.2 Start the Frontend Static Server
Execute in the frontend project root directory:
python -m http.server 5500
Open http://127.0.0.1:5500 in the browser. The page calls the backend through http://127.0.0.1:5000/api for health checks, calculations, history refresh and deletion operations.
10. Personal Experience and Reflection
At the beginning of this assignment, I used to think "the page displays a result" and "the backend completes the calculation" were the same thing. I built the frontend interactions first, and only discovered during integration that the API request and response formats must be strictly agreed. I then sorted out the complete flow: frontend submits expression → backend parses, calculates and saves → frontend receives result and refreshes page. This made the logic boundary much clearer.
Most debugging time went into expression parsing, illegal input handling, and history refresh. Seemingly simple inputs like 1+2*3, (1+2)*3, -5+8, 1/0, 1+abc cover precedence, parentheses, unary operations, mathematical errors, and illegal characters. Testing and fixing them one by one gave me a clearer understanding of the relationship between backend calculation and database persistence.
Beyond the required features, I added Ans result reuse, history search, favorite filtering, result copying, theme switching, keyboard shortcuts, percentages, and display precision. These features focus on input and viewing experience optimization, without changing the backend calculation flow.
11. Conclusion
This project implements a frontend–backend separated calculator system. The frontend handles input, interaction and display; the Flask backend handles expression parsing, calculation and error reporting; SQLite stores successful history records. The system supports arithmetic, parentheses, decimals, unary signs, percentages, history queries, selected deletion and record clearing, and passes all local automated tests.
https://github.com/Betty-bear86/calculator-separated-project
相关推荐
基于MATLAB的复合弹性模量分析,采用混合法则,结合纤维体积分数变化和图形结果。.zip
1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
基于MATLAB的自适应纯追踪曲线跟踪机器人仿真,采用多路径、自适应速度控制和图形界面可视化。.zip
1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
【数据库优化】通信行业B/O域查询性能提升:基于分区裁剪与覆盖索引的秒级溯源系统实现
内容概要:本文围绕通信行业中海量话单与信令数据的查询性能问题,系统阐述了从全表扫描到秒级响应的数据库优化路径。文章以运营商 cdr_detail 表为案例,揭示了因使用函数操作索引列(如DATE(call_start_time))和深分页(LIMIT offset, size)导致的全表扫描问题,并提出完整的优化方案:通过时间范围RANGE分区实现分区裁剪、构建msisdn与时间的联合覆盖索引、消除索引列函数运算、采用游标分页替代OFFSET等方式,使查询性能从P95 8.3秒降至290毫秒。同时介绍了执行计划分析(EXPLAIN)在调优中的关键作用,并展望了HTAP架构、向量化执行与AI自治优化器等未来方向。; 适合人群:具备一定数据库基础知识,从事通信行业数据开发、DBA或后端研发的技术人员,尤其是面对大规模时序数据查询性能瓶颈的工程师;; 使用场景及目标:①解决营业厅用户详单查询、网间结算对账、VoNR信令分析等高并发、低延迟查询场景下的性能问题;②掌握如何通过分区策略、索引设计和SQL改写实现从全表扫描到精准索引访问的优化落地; 阅读建议:建议结合实际生产环境中的大表查询案例,对照本文的反例与正例进行执行计划对比分析,并在测试环境中实践分区表创建、覆盖索引构建及游标分页改写,深入理解每项优化技巧的实际效果。
石墨提纯全球市场结构与产业发展趋势分析报告(QYResearch最新研究).docx
石墨提纯全球市场结构与产业发展趋势分析报告(QYResearch最新研究)
政府科技管理者如何提升科技成果转化政策的精准匹配与执行效率?.docx
政府科技管理者如何提升科技成果转化政策的精准匹配与执行效率?












