832401306陈子洋 Front-End and Back-End Separation Calculator System
Front-End and Back-End Separation Calculator System
| Item | Current Information |
|---|---|
| Student ID | 832401306(FZU) 24125245(MU) |
| Student Name | Chen Ziyang 陈子洋 |
| Course | EE308FZ Software Engineering |
| Assignment | First Assignment-Front-End and Backend Separation Calculator System |
| Blog Platform | CSDN |
Table of Contents
- Course Information
- Project Links
- PSP Table
- Assignment Description
- Requirement Analysis
- Overall Architecture
- Frontend Design
- Backend Design
- API Design
- Database Design
- Expression Parser Design
- Calculation History Design
- Exception Handling
- Frontend and Backend Communication
- Deployment
- Functional Structure Diagram
- Presentation of Finished Product
- Code Explanation
- Personal Journey and Learnings
1. Course Information
| Field | Content |
|---|---|
| Assignment Requirements | Develop a separated frontend and backend calculator, then provide code, deployment, a blog and presention evidence |
| Objectives | Implement basic and compound calculations, exceptions, exception handling, persistent SQLite history, deletion by ID and public deployment |
| Other References | Teacher provided assignment blog and class announcements |
2. Project Links
Frontend Repository: https://github.com/chenzy3034-ux/832401306_calculator_frontend
Backend Repository: https://github.com/chenzy3034-ux/832401306_calculator_backend
Frontend Code Standard: https://github.com/chenzy3034ux/832401306_calculator_frontend/blob/main/codestyle.md
Backend Code Standard: https://github.com/chenzy3034ux/832401306_calculator_backend/blob/main/codestyle.md
3. PSP Table
The table below lists the typical development activities planned for this project, along with the estimated time for each activity.
| Activity | Estimated min | Actual min |
|---|---|---|
| Requirement analysis | 90 | 75 |
| System design | 120 | 125 |
| Frontend development | 240 | 255 |
| Backend development | 240 | 260 |
| Expression calculation module | 180 | 200 |
| Database design | 120 | 120 |
| Calculation history module | 120 | 140 |
| Frontend and backend integration | 120 | 135 |
| Testing | 180 | 180 |
| Deployment | 180 | 180 |
| Blog writing | 240 | 215 |
| Other work | 90 | 90 |
4. Assignment Description
This assignment requires a calculator system with fully separated frontend and backend applications. The frontend handles expression input, button interaction, result and error display, and history presentation. The backend validates and parses expressions, performs calculations, handles exceptions, and accesses the database.
The project supports addition, subtraction, multiplication, division, decimals, parentheses, operator precedence, unary plus and minus, invalid expression handling, and division by zero handling. Successful calculations are stored in SQLite, and records can be queried and deleted. The frontend neither stores history in localStorage nor calculates final results independently.
5. Requirement Analysis
In this assignment, the calculator system should cover the following requirements:
· Basic calculation. Backend supports +,-,* and /.
· Compound expressions. Precedence, parentheses, decimals, unary signs.
· Invalid input. Restricted tokenizer and parser return controlled errors.
· Division by zero. Evaluator raises a dedicated domain error.
· Persistence history. SQLite file stores every successful calculation.
· History retrieval from the backend.
· Delete the specified records by ID.
6. Overall Architecture
The system is divided into the browser client, HTTP API, business services, and database layers. The browser handles interaction and presentation. FastAPI receives JSON requests and invokes calculation or history services. The SQLite repository provides persistence.
User
↓
React + Vite Frontend
↓ HTTPS / JSON
FastAPI API Routes
↓
Service Layer
├── Expression Calculation Service
│ ↓
│ Tokenizer → Parser → Evaluator
│
└── Calculation History Service
↓
SQLite Database
Module boundaries keep routes, calculation logic, and database access separate. This supports focused testing and keeps parsing and SQL out of API route functions.
7. Frontend Design
The frontend uses React19, Vite8, Javascript and CSS. The calculator component provides the expression field, number button, operators parentheses, decimal point, clear button, and equal button. The HistoryList component presents stored records and handles refresh and delete actions.
· When the expression is empty, the field displays 0.
· Multiplication and division buttons display familiar symbols and are converted to * and / before sending a request.
· Pressing equals calls the backend; React does not calculate the final result.
· The page calls GET/api/history on load and DELETE/api/history/{id} for deletion.
· Failed requests display the safe message returned by the backend.
· When the backend is unavailable, users can still edit the input but cannnot obtain a new valid result.
8. Backend Design
The backend uses Python, FastAPI, Pydantic, Uvicorn and the standard sqlite 3 library. app/api contains HTTP routes, app/services coordinates use cases, app/calculator safely precesses expressions and app/database contains the connection, schema and repository layers.
At application startup, the lifespan handler initializes the database table. Global exception handlers convert request validation errors and unexpected exceptions into a consistent JSON response without exposing a Python stack trace. Allowed CORS origins are configured through an environment variable.
9. API Design
| Method | Endpoint | Purpose |
|---|---|---|
| POST | api/calculate | Validate, calculate, save and return one record |
| GET | api/history | Return stored calculations, newest first |
| DELETE | api/history/{id} | Delete one record by database ID |
| GET | health | Deployment health check |
Example calculation request:
{
"expression": "(1+2)*3"
}
Successful responses place the payload in data. Failed responses place a stable error code and safe message in error.
{
"success": false,
"data": null,
"error": {"code": "INVALID_EXPRESSION", "message": "The expression is invalid"}
}
10. Database Design
The project uses a file based SQLite database rather than browser storage or an in process list.
| Column | Type | Constraint | Meaning |
|---|---|---|---|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT | History record identifier |
| expression | TEXT | NOT NULL | Original API expression |
| result | TEXT | NOT NULL | Decimal calculation result |
| created_at | TEXT | NOT NULL | UTC ISO timestamp |
11. Expression Parser Design
Expression processing follows three explicit stages: tokenizer, parser and evaluator. The input is never executed as Python code.
11.1 Tokenizer
The tokenizer accepts only digits, decimal points, whitespace, +, -, *, /, and parentheses. It constructs numbers with Decimal. Multiple decimal points, an isolated decimal point, or an unknown character raises Invalid expression error.
11.2 Recursive Descent Parser
The parser converts tokens into NumberNode, UnaryNode, and BinaryNode objects. Its grammar is:
expression -> term ((+ | -) term)*
term -> unary ((* | /) unary)*
unary -> (+ | -) unary | primary
primary -> NUMBER | (expression)
11.3 Precedence and Evaluation
The expression level handles addition and subtraction while the term level handles multiplication and division, giving multiplication and division higher precedence. The primary level recursively parses parentheses, and the unary level handles unary plus and minus. The evaluator traverses the AST recursively and checks the right operand before division.
12. Calculation History Design
A successful calculation follows this order: parse the expression, evaluate the result, insert the record into SQLite, and return the stored record. Insertion happens only after evaluation succeeds, so invalid expressions and division by zero do not create history records.
1.POST /api/calculate returns and stores a successful record.
2.GET /api/history returns records in descending created_at and id order.
3.The frontend reloads history on page load and after successful calculations or deletions.
4.DELETE /api/history/{id} removes the row with the specified database ID.
5.Deleting a missing ID returns 404 rather than reporting false success.
13. Exception Handling
| Situation | HTTP | Code | Frontend Message |
|---|---|---|---|
| Invalid expression | 400 | INVALID_EXPRESSION | The expression is invalid |
| Division by zero | 422 | DIVISION_BY_ZERO | Division by is not allowed |
| Missing history ID | 404 | HISTORY_NOT_FOUND | The calculation history record was not found |
| Invalid request body | 422 | INVALID_REQUEST | The request data is invalid |
| Unexpected backend error | 500 | INTERNAL_ERROR | An internal server error occurred |
14. Frontend and Backend Communication
A successful calculation uses the following communication sequence:
User presses =
-> React converts × and ÷ to * and /
-> POST /api/calculate
-> FastAPI validates the request
-> Tokenizer -> Parser -> Evaluator
-> SQLite INSERT
-> JSON response
-> React displays the result
-> React reloads GET /api/history
In development, VITE_API_BASE_URL points to the local backend. The production build sets it to the PythonAnywhere HTTPS address. The backend uses CALCULATOR_CORS_ORIGINS to allow only configured frontend origins.
15. Deployment
15.1 Frontend
The frontend is deployed on GitHub Pages. When the main branch changes, GitHub Actions runs npm ci and npm run build, then publishes dist as the Pages artifact. The Vite base is /832401306_calculator_frontend/.
Public URL: https://chenzy3034-ux.github.io/832401306_calculator_frontend/
15.2 Backend
The backend is deployed on PythonAnywhere as a Uvicorn ASGI web application. Its start command uses environment variables to specify the persistent SQLite file and CORS origin. The platform provides a public HTTPS subdomain.
Backend URL: https://chenzy.pythonanywhere.com/
The public environment has been checked for health, CORS, calculation, history retrieval, deletion, and persistence after a backend reload. The production frontend build and GitHub Pages workflow succeeded, and all 41 backend tests passed.
16. Function Structure Diagram
Calculator System
|-- Frontend
| |-- Expression input and keypad
| |-- Result and error display
| |-- History list and delete action
| `-- Backend API client
|
|-- Backend
| |-- Calculate API
| |-- History APIs
| |-- Calculation service
| |-- History service
| `-- Unified responses and exception handling
|
|-- Expression Processing
| |-- Tokenizer
| |-- Recursive descent parser
| `-- Decimal evaluator
|
`-- Persistence
|-- SQLite connection and initialization
`-- Insert, query, and delete repository
This structure diagram matches the modules in the current repositories. It excludes unimplemented features such as scientific calculation, login, and bulk history deletion.
17. Presentation of Finished Product
17.1 Main calculator page

Figure1. Main calculator page
The initial expression shows 0, the result shows a dash, and the history section is visible.
17.2 Addition

Figure2. Addition: 1+2=3
The backend returns 3 and the successful record appears in history.
17.3 Subtraction

Figure3. Subtractiion: 10-3=7
The backend returns 7.
17.4 Multiplication

Figure4. Multiplication: 4x5=20
The interface shows the multiplication symbol while the API receives an asterisk.
17.5 Division

Figure5. Division: 10/2=5
The interface shows the division symbol while the API receives a slash.
17.6 Decimal

Figure6. Decimal: 3.5+1.2=4.7
This demonstrates decimal parsing and calculation.
17.7 Compound expression

Figure7. Compound expression: 1+2x3=7
This demonstrates multiplication taking precedence over addition.
17.8 Parentheses

Figure8. Parentheses: (1+2)x3=9
This demonstrates parentheses changing the evaluation order.
17.9 Unary minus

Figure9. Unary minus: -5+8=3
This demonstrates unary minus.
17.10 Invalid expression

Figure10. Invalid expression: 1+x2
The page shows "The expression is invalid" and does not add a history record.
17.11 Division by zero

Figure11. Division by zero: 10/0
The page shows "Division by zero is not allowed" and does not add a history record.
17.12 Calculation history

Figure12. Calculation history
The list shows each expression, result, time and Delete button.
17.13 History persistence

Figure13. History persistence
Database records remain after refreshing or reopening the page.
17.14 Delete history

Figure14. Delete history
The selected record -5+8=3 disappears, and the deleted record does not return after refreshing.
18. Code Explanation
18.1 Frontend API Request
The interface uses x and ÷ to make input more intuitive. After the user clicks the equals button, the frontend first converts these symbols into * and /, which are supported by the backend, and then sends the expression.
function toApiExpression(value) {
return value.replaceAll('×', '*').replaceAll('÷', '/')
}
const record = await calculateExpression(toApiExpression(expression))
setResult(record.result)
await loadHistory()
}
calculateExpression sends JSON through an HTTP POST request. The frontend only displays the result returned by the backend and does not calculate the final result by itself.
export function calculateExpression(expression) {
return request('/api/calculate', {
method: 'POST',
body: JSON.stringify({ expression }),
})
}
18.2 Backend Calculate API
POST /api/calculate receives the expression, calls the service layer, and returns the saved history record after a successful calculation. A successful response uses HTTP 201.
history = calculate_and_store(request.expression, repository)
return CalculateSuccessResponse(data=history)
The service layer performs the evaluation before writing the result to the database. Therefore, a failed calculation does not create a history record.
def calculate_and_store(
expression: str,
repository: HistoryRepository,
) -> CalculationHistory:
result = evaluate_expression(expression)
return repository.insert(expression=expression, result=result)
18.3 Expression Parsing
The expression passes through the tokenizer, parser, and evaluator in sequence. The tokenizer converts the input into restricted mathematical tokens, the parser constructs an expression tree, and the evaluator calculates the value of that tree.
def evaluate_expression(expression: str) -> Decimal:
return evaluate(Parser(tokenize(expression)).parse())
This process only handles the numbers and operators defined by the project. It does not execute the user input as Python code.
18.4 Operator Precedence
The parser uses four levels: expression, term, unary, and primary. expression combines only addition and subtraction, while term combines only multiplication and division. Therefore 1+23 becomes 1+(23). Parentheses start a nested expression parse and can override the default order.
18.5 Input Validation
The tokenizer rejects empty expressions and only recognizes numbers, decimal points, whitespace, the four arithmetic operators, and parentheses. Invalid numbers and unknown characters trigger InvalidExpressionError.
if not expression or expression.isspace():
raise InvalidExpressionError("Expression cannot be empty")
The parser also checks whether the expression is complete. For example, if extra tokens remain after parsing, the expression is treated as invalid.
def parse(self) -> ExpressionNode:
expression = self._parse_expression()
if self._current.type is not TokenType.END:
raise InvalidExpressionError("Unexpected token after expression")
return expression
18.6 Error Handling
The calculation API converts invalid expressions and division by zero into clear HTTP status codes and consistent JSON error messages.
except InvalidExpressionError:
return error_response(
status.HTTP_400_BAD_REQUEST,
"INVALID_EXPRESSION",
"The expression is invalid",
)
except DivisionByZeroExpressionError:
return error_response(
status.HTTP_422_UNPROCESSABLE_CONTENT,
"DIVISION_BY_ZERO",
"Division by zero is not allowed",
)
In addition, the global exception handler returns a general message for unexpected backend errors, preventing Python exception stack traces from being exposed to the browser. The shared frontend request function checks both the HTTP status and the success field in the response before displaying the corresponding error message.
18.7 Database Insert
After each successful calculation, the repository uses parameter binding to insert the expression, result, and creation time. The result is stored as text to preserve the representation of the Decimal result.
INSERT INTO calculation_history (expression, result, created_at)
VALUES (?, ?, ?)
After the insertion, the code reads cursor.lastrowid and returns the new record’s id, expression, result, and created_at to the calculation API.
18.8 Database Query
GET /api/history reads records from SQLite. The query sorts the records by creation time and ID in descending order so that newer records appear first.
SELECT id, expression, result, created_at
FROM calculation_history
ORDER BY created_at DESC, id DESC
The frontend requests the history again when the page loads and after a successful calculation or deletion. Therefore, the history is still obtained from the database after the page is refreshed.
18.9 Delete a Specific History Record
The delete operation uses the database record ID and parameter binding to locate the specified row.
cursor = connection.execute(
"DELETE FROM calculation_history WHERE id = ?",
(history_id,),
)
return cursor.rowcount > 0
If rowcount is zero, the service layer raises HistoryNotFoundError, and the API returns HTTP 404. After a successful deletion, the frontend reloads the history list so that the interface remains synchronized with the database.
19. Personal Journey and Learnings
I began this assignment by reviewing the Blog on society and converting its requirements into a checklist. This helped me understand that the task included frontend and backend separation, testing, database persistence, documentation, and deployment.
The most important technical challenge was implementing expression calculation without using eval or exec. I created a tokenizer, recursive descent parser, and evaluator to support operator precedence, parentheses, unary operators, decimals, invalid expressions, and division by zero. This improved my understanding of safe input processing and expression parsing.
I also learned how to separate responsibilities between FastAPI routes, services, calculation modules, and the SQLite repository. Successful calculations are stored in SQLite, while failed calculations do not create history records. The frontend only handles interaction and displays results returned by the backend.
Finally, deploying the frontend to GitHub Pages and the backend to PythonAnywhere helped me understand CORS, environment variables, HTTPS communication, and persistent database storage. Through this project,although there are so many tricky issues, I gained practical experience in developing, testing, integrating, and deploying a complete frontend and backend application.
相关推荐
基于Spring Boot 3 + Vue 3 + Element Plus的现代化宠物救助管理系统设计与实现
本文将详细阐述系统的需求分析、技术选型、架构设计、数据库设计、核心功能模块的实现(包括用户认证、宠物信息管理、救助申请、领养流程等),并提供关键代码片段,最后对系统进行总结与展望。.requestMatchers("/api/auth/login", "/api/auth/register", "/api/public/**").permitAll() // 公开接口。.requestMatchers("/api/admin/**").hasRole("ADMIN") // 管理员接口。
电子商务案例分析(第七版)课程标准+课件+教案+试卷及答案
电子商务案例分析(第七版)课程标准+课件+教案+试卷及答案
政府科技管理者在审批科技成果转化项目时,如何高效核实技术成果的知识产权归属?.docx
政府科技管理者在审批科技成果转化项目时,如何高效核实技术成果的知识产权归属?
技术经理人上岗后成长慢?AI工具打造从上手到出活的全链路支撑.docx
技术经理人上岗后成长慢?AI工具打造从上手到出活的全链路支撑
数媒大赛ai工具使用word说明
数媒大赛ai工具使用word说明


