☰
前后端分离计算器系统(Flask + SQLite + HTML/CSS/JS)
2026/9/26 4:15:48 网站建设 项目流程

Frontend/Backend Separated Calculator System — Assignment Blog

Table of Contents

  • Frontend/Backend Separated Calculator System — Assignment Blog
    • 1. Course Information
    • 2. Git Repository Link and Code Standards Link
    • 3. PSP Table
    • 4. Presentation of the Finished Product
      • 4.1 Main Interface
      • 4.2 Addition
      • 4.3 Subtraction
      • 4.4 Multiplication
      • 4.5 Division
      • 4.6 Decimal Arithmetic
      • 4.7 Operator Precedence
      • 4.8 Parentheses
      • 4.9 Unary Minus
      • 4.10 Division by Zero
      • 4.11 Invalid Expression
      • 4.12 History Records
      • 4.13 Persistence
      • 4.14 Delete a Single Record
      • 4.15 Clear All
      • 4.16 Keyboard Input (Extended Feature)
    • 5. Design and Implementation Process
      • 5.1 Requirements Analysis
      • 5.2 Overall System Architecture
      • 5.3 Front-End Design
      • 5.4 Back-End Design
      • 5.5 API Design
      • 5.6 Database Design
      • 5.7 Expression Evaluation Algorithm Design
      • 5.8 Exception Handling
      • 5.9 Frontend/Backend Interaction Process
      • 5.10 Deployment Process
    • 6. Function Structure Diagram
    • 7. Code Explanation (Key Code + Design Rationale)
      • 7.1 Backend: Expression Parsing (calc.py)
      • 7.2 Backend: REST Interface (app.py)
      • 7.3 Backend: History CRUD (app.py)
      • 7.4 Frontend: Unified Input for Keyboard and Buttons (app.js)
      • 7.5 Frontend: History Records (Proof of Persistence)
    • 8. Personal Journey and Learnings
    • 9. Extended Features (Extra Credit)
    • 10. Deployment / Access Information
      • Backend (public address)
      • Frontend (public address)
      • How to test

1. Course Information

Course for This AssignmentWeb Development Technology(Web 开发技术)
Assignment RequirementsImplement a calculator system with a frontend/backend separated architecture; the backend must do all calculation and persist history in a database; deploy the project and submit the blog, the two GitHub repositories and a publicly accessible address.
Objectives of This AssignmentUnderstand the frontend/backend separation architecture, REST API design, database persistence, expression parsing, deployment, and write a complete assignment blog.
Other ReferencesAssignment notice: https://bbs.csdn.net/topics/620530837
ItemContent
Student王少杰
Student ID832401219
PlatformWindows + Python 3.14 + Flask + SQLite + HTML/CSS/JS
Date2026-09-24

2. Git Repository Link and Code Standards Link

ItemLink
Frontend repositoryhttps://github.com/060725/calculator_frontend
Frontend code standardhttps://github.com/060725/calculator_frontend/blob/main/codestyle.md
Backend repositoryhttps://github.com/060725/calculator_backend
Backend code standardhttps://github.com/060725/calculator_backend/blob/main/codestyle.md

3. PSP Table

PhaseEstimated (hours)Actual (hours)
Requirements analysis0.50.5
System design (architecture / API / database)0.50.5
Backend: expression parsing & calculation module1.52.0
Backend: calculation history & database module0.50.5
Frontend: UI, interaction, keyboard shortcuts2.02.0
Frontend/backend integration & testing1.01.0
Deployment (PythonAnywhere + GitHub Pages)0.50.5
Blog writing1.01.5
Total7.58.5

4. Presentation of the Finished Product

4.1 Main Interface

The page shows a dark-themed calculator: the button area is on the left and the history panel is on the right.

4.2 Addition

Click12+8=in sequence; the result shows20.

4.3 Subtraction

After clearing, enter15-7=; the result shows8.

4.4 Multiplication

After clearing, enter6×7=; the result shows42.

4.5 Division

After clearing, enter20÷4=; the result shows5.

4.6 Decimal Arithmetic

After clearing, enter3.14+2.86=; the result shows6.

4.7 Operator Precedence

After clearing, enter1+2×3=; the result shows7instead of9, proving that multiplication/division have higher precedence than addition/subtraction.

4.8 Parentheses

After clearing, enter(1+2)×3=; the result shows9, and parentheses have the correct precedence.

4.9 Unary Minus

After clearing, enter3×±2=; the result shows-6. You can also type3*-2with the keyboard.

4.10 Division by Zero

After clearing, enter5÷0=; the UI shows a red message: “除数不能为零” (Division by zero is not allowed).

4.11 Invalid Expression

After clearing, type only+and press=; the UI shows “表达式无效” (Invalid expression).

4.12 History Records

After several calculations, the right panel lists the expressions, results and timestamps in reverse chronological order.

4.13 Persistence

PressF5to refresh the page; the history records still exist, which proves the data is persisted in the backend database.

4.14 Delete a Single Record

Click the×at the top-right of a record; that record is removed from both the list and the database.

4.15 Clear All

Click “清空全部” (Clear All) at the top of the panel and confirm; all records are removed.

4.16 Keyboard Input (Extended Feature)

Type1+2*3directly with the keyboard and pressEnter; it computes7correctly.
Full keyboard support (digits,+ - * /,( ),Enter,Backspace,Escape) is an
extended featurebeyond the basic requirements (see Section 9).


5. Design and Implementation Process

5.1 Requirements Analysis

The assignment requires afrontend/backend separatedcalculator system:

  • The frontend provides a graphical interface (dark theme) and supports both mouse clicks and keyboard input;
  • The backend provides an expression-evaluation API and persists history records in a database (SQLite);
  • History records support deleting a single record and clearing all; records must survive page refresh;
  • It must support the four basic operations, decimals, parentheses with precedence, and unary plus/minus;
  • Invalid expressions (e.g., division by zero, a bare operator) must produce friendly error messages;
  • The project must be deployed to a publicly accessible address so that the teaching assistant can verify it.

5.2 Overall System Architecture

┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │ Browser (Frontend) │ │ Backend Service │ │ calculator_frontend │ HTTP │ calculator_backend │ │ │ ──────► │ │ │ index.html / style.css │ JSON │ app.py REST API │ │ app.js (Fetch calls the API) │ ◄────── │ calc.py Expression parser │ │ · Calculator buttons / keys │ │ SQLite History persistence │ │ · History panel / error msg │ │ (calculator.db) │ └─────────────────────────────────┘ └─────────────────────────────────┘

The frontend and backend communicate through a REST API with JSON. The frontend never
touches the database directly and never computes the result itself — the calculation is
always done on the backend. This is the essence of “frontend/backend separation”.
A simple way to verify this: if the backend service is stopped, the frontend can still
accept input but can no longer obtain any new valid calculation result.

5.3 Front-End Design

  • Single-page UI: left panel is the calculator button grid, right panel is the history list.
  • Every button carries adata-keyattribute; clicks and keyboard events share one input channel.
  • The display area has three lines: the input expression, the result, and a red error message.
  • After a successful calculation the frontend re-queries the history API to refresh the panel.

5.4 Back-End Design

  • Flask app exposing a small REST API (calculation + history CRUD).
  • A hand-writtenrecursive descent parser(calc.py) — noeval/execis used,
    which satisfies the assignment’s security requirement.
  • CORS is enabled so an independently hosted frontend can call the API cross-origin.
  • SQLite for persistence; awsgi.pyentry is provided for production deployment.

5.5 API Design

MethodPathRequest Body / ParamsResponse
POST/api/calculate{"expression":"1+2×3"}201:{id, expression, result, created_at}
GET/api/history—{items:[{id, expression, result, created_at}]}
DELETE/api/history/<id>path param{ok:true}
DELETE/api/history—{ok:true, deleted:n}(optional “clear all”)
  • On success:201with the result, and the record is written to history;
  • On failure (division by zero, invalid expression, etc.):400with a Chinese message in theerrorfield.

5.6 Database Design

SQLite database filecalculator.dbwith a single tablehistory:

FieldTypeDescription
idINTEGER PRIMARY KEY AUTOINCREMENTPrimary key
expressionTEXT NOT NULLThe expression evaluated
resultTEXT NOT NULLThe evaluation result
created_atTEXT NOT NULLRecord timeYYYY-MM-DD HH:MM:SS

The table is created automatically on first startup (init_db()), so no manual
database initialization is required. History is queried withORDER BY id DESC LIMIT 100,
so the records remain visible after a page refresh.

5.7 Expression Evaluation Algorithm Design

The backend uses arecursive descent parser(calc.py):

expr := term (('+' | '-') term)* term := factor (('*' | '/' | '×' | '÷') factor)* factor := ('+' | '-') factor | '(' expr ')' | number
  • The grammar naturally handles “multiplication/division before addition/subtraction”, parentheses, and unary plus/minus;
  • Division by zero raisesValueError('除数不能为零')and a parse failure raisesValueError('表达式无效');
  • Results are formatted uniformly:6.0 → 6,0.30000000000000004 → 0.3.
  • Input is tokenised with a whitelist regex, so arbitrary code can never be executed.

5.8 Exception Handling

  • Backend: business errors raiseValueErrorwith Chinese messages; the API layer maps
    them to400+errorfield. Unexpected internal errors are caught and returned as
    500with a generic message.
  • Frontend: on a non-2xx response, the red message area shows the backend’serror
    text; if the backend is unreachable the UI shows “无法连接到后端服务”
    (Cannot connect to the backend service) instead of a wrong result.

5.9 Frontend/Backend Interaction Process

User clicks a button / presses a key ↓ Frontend builds the expression string ↓ POST /api/calculate { "expression": "1+2×3" } ↓ Backend validates → parses → calculates → saves to SQLite ↓ 201 { id, expression, result, created_at } ↓ Frontend shows the result and refreshes the history panel (GET /api/history)

5.10 Deployment Process

  • Backend: deployed withPythonAnywhere(free tier) using thewsgi.pyentry
    point; the Flask app runs behind PythonAnywhere’s web server.
  • Frontend: deployed withGitHub Pages(free static hosting);
    chooses the production backend address when opened from the deployed domain.
  • Online addresses and test instructions are listed inSection 10.

6. Function Structure Diagram

Frontend/Backend Separated Calculator System ├── Frontend calculator_frontend │ ├── Calculator UI (dark theme) │ │ ├── Digit / decimal point input │ │ ├── Four basic operator input │ │ ├── Parenthesis input │ │ ├── Sign toggle (±) │ │ ├── Clear (AC) / backspace │ │ └── Evaluate (=) │ ├── Keyboard shortcuts (extended) │ ├── Error messages (red, division by zero / invalid expression) │ └── History panel │ ├── Shows expression / result / timestamp │ ├── Delete a single record (×) │ └── Clear all (with confirmation) └── Backend calculator_backend ├── POST /api/calculate evaluate expression + write history ├── GET /api/history read history ├── DELETE /api/history/<id> delete one history record ├── DELETE /api/history clear all history └── SQLite persistence

7. Code Explanation (Key Code + Design Rationale)

7.1 Backend: Expression Parsing (calc.py)

defparse_term(self):value=self.parse_factor()whileself.peek()in('*','/','×','÷'):op=self.take()rhs=self.parse_factor()ifopin('/','÷'):ifrhs==0:raiseValueError('除数不能为零')# business error -> HTTP 400value/=rhselse:value*=rhsreturnvalue

Design rationale: The grammar is a three-level recursionexpr → term → factor.
Thetermlevel parses afactorfirst and then handles multiplication/division,
so the multiplication/division “binds” tighter and naturally has higher precedence
than the addition/subtraction handled by theexprlevel. Thefactorlevel also
handles parentheses and unary minus, covering cases like(1+2)×3and3×-2.
Noeval/execis used anywhere — the input is parsed with a whitelist token regex.

7.2 Backend: REST Interface (app.py)

@app.route('/api/calculate',methods=['POST'])defcalculate():expression=(request.get_json(silent=True)or{}).get('expression','').strip()try:result=evaluate(expression)exceptValueErrorasexc:returnjsonify({'error':str(exc)}),400cur=conn.execute('INSERT INTO history (expression, result, created_at) VALUES (?, ?, ?)',(expression,result,created_at))conn.commit()returnjsonify({'id':cur.lastrowid,'expression':expression,'result':result,'created_at':created_at}),201

Design rationale: The endpoint only does “receive expression → validate → evaluate →
save to DB → return”. Business errors are uniformly mapped to400 + error message, which the
frontend renders as a red message. Parameterized SQL (?placeholders) prevents injection attacks.

7.3 Backend: History CRUD (app.py)

@app.route('/api/history/<int:rid>',methods=['DELETE'])defdelete_record(rid):conn=get_conn()try:conn.execute('DELETE FROM history WHERE id = ?',(rid,))conn.commit()finally:conn.close()returnjsonify({'ok':True})

Design rationale: Deletion goes through the backend API and removes the row from the
database for real; the frontend then re-queriesGET /api/historyto refresh the list,
so the displayed data always reflects the latest state of the backend database.

7.4 Frontend: Unified Input for Keyboard and Buttons (app.js)

document.addEventListener('keydown',(event)=>{constkey=event.key;if(/[0-9]/.test(key))insert(key);elseif(key==='*')insert('×');elseif(key==='/'){event.preventDefault();insert('÷');}elseif(key==='Enter'){event.preventDefault();evaluate();}elseif(key==='Backspace')backspace();elseif(key==='Escape')clearAllInput();});

Design rationale: Every button carries adata-keyattribute, and both button
clicks and keyboard events go through the sameinsert()input channel.*//are
automatically converted to×/÷before being sent to the backend, so clicking with
the mouse and typing with the keyboard behave identically (see screenshot 4.16).
This keyboard support is one of the extended features.

7.5 Frontend: History Records (Proof of Persistence)

asyncfunctionloadHistory(){constres=awaitfetch(`${API_BASE_URL}/history`);constdata=awaitres.json();renderHistory(data.items||[]);}

Design rationale: History is not stored inlocalStorage; it is read from the
backend SQLite database every time. Therefore the records survive a page refresh
(F5), which demonstrates the frontend/backend separation idea that “data is
persisted by the backend” (see screenshot 4.13).


8. Personal Journey and Learnings

  1. I truly understood frontend/backend separation: the frontend is only
    responsible for display and interaction, while all calculation and data storage
    are delegated to the API. The two sides communicate via JSON with clear
    responsibilities, so they can be developed and deployed independently.
  2. The recursive descent parsergave me a concrete understanding of the
    relationship between grammar and precedence. Before, I only knew the precedence
    rules; this time I implemented them with a grammar myself and realized that the
    layering ofterm/factoris exactly where precedence comes from.
  3. Error handling must reach the frontend: aValueErrorraised in the backend
    has to be converted into an HTTP status code plus a user-friendly message, and the
    frontend renders it as a red error message — the full chain must be complete.
  4. SQLite made persistence painless: no separate database service needed, a
    single file does the job. It fits course assignments perfectly and is more than
    enough for the CRUD operations on history records.
  5. Deployment taught me the difference between local and online environments:
    the frontend and the backend live on different domains, so CORS must be enabled
    and the frontend must switch its API address automatically.
  6. Through the unified input channel of keyboard shortcuts anddata-key, I merged
    “clicking” and “typing” into one logic flow and learned the value of abstraction.

9. Extended Features (Extra Credit)

The following features go beyond the basic requirements and have been implemented and
demonstrated:

FeatureDescriptionDemonstration
Keyboard shortcutsFull keyboard input: digits,+ - * /,( ),Enter,Backspace,EscapeScreenshot 4.16
Delete a single history recordEach record has a×button; deletion is executed through the backend APIScreenshot 4.14
Clear all history“清空全部” button with a confirmation dialogScreenshot 4.15
Red error messagesDivision by zero / invalid expression shown in red, coming from the backendScreenshots 4.10, 4.11

10. Deployment / Access Information

Backend (public address)

https://060725.pythonanywhere.com

Frontend (public address)

https://060725.github.io/calculator_frontend/

How to test

  1. Open the frontend address in a browser (desktop or mobile).
  2. Click or type any expression, e.g.12+8, then press=— the result20is returned by the backend.
  3. Check the history panel: records (expression / result / time) are stored in the backend database and surviveF5.
  4. Try5÷0— the red message “除数不能为零” appears; try+then=— “表达式无效”.
  5. To verify separation: stop the backend and press=— no new valid result can be computed.

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询