☰
832401304 陈颀桢 Software Engineering Practice: Front-End and Back-End Separation Calculator System
2026/10/7 2:56:07 网站建设 项目流程

832401304陈颀桢 First Assignment: A Front-End/Back-End Separated Calculator System

This article documents CalcFlow, a calculator built for the first Software Engineering Practice assignment. It covers the source repositories, live demo, feature screenshots, design decisions, implementation, and deployment checks.

Table of Contents

  • Course Information and Assignment Requirements
  • GitHub Repositories and Code Standards
  • Live Demo and Testing Instructions
  • PSP Table
  • Finished Product
  • Design and Implementation
  • Key Code and Explanations
  • Deployment and Verification
  • Additional Features and Limitations
  • Personal Reflection

Course Information and Assignment Requirements

ItemDescription
CourseSoftware Engineering Practice
AssignmentBuild a calculator with separate front and back ends. The back end calculates results and stores history in a database.
ObjectivesUnderstand HTTP APIs, expression processing, database persistence, and cooperation between the front end and back end.
DeadlineOctober 7, 2026, 23:59 (China Standard Time)
Other referencesFirst assignment requirements published by teaching assistant Zhuo Genghua

GitHub Repositories and Code Standards

  • Front-end repository: grrrced/calculator_frontend
  • Back-end repository: grrrced/calculator_backend
  • Front-end code standard: codestyle.md
  • Back-end code standard: codestyle.md

Live Demo and Testing Instructions

  • Public calculator: Open CalcFlow
  • Back-end health endpoint: Railway API health

Open the calculator, enter an expression, and click “=” or press Enter. The following cases can be checked:12+8→ 20,18-7→ 11,6*7→ 42,14/2→ 7,0.1+0.2→ 0.3,1+2*3→ 7,(1+2)*3→ 9, and3*-2 + +4→ -2. Entering1++*2or1/0displays an error. After a successful calculation, the result appears in history. Refresh the page to check that the history is loaded again; use the search field to filter loaded records and the “×” button beside a record to delete that specific entry. The upper-right button changes the theme. When the expression field has focus, Enter calculates and Esc clears the input.

The expressions, error messages, history after a browser refresh, search, deletion followed by another refresh, theme switching, and keyboard shortcuts were checked on the public page on October 5, 2026. Persistence after a back-end restart has not yet been tested. The back-end service and its database volume must remain available during grading.

PSP Table

The figures below are an illustrative, retrospective workload estimate for a third-year student with basic programming experience who is building a separated front-end/back-end application for the first time. Neither column is an original pre-development plan or a measured record of time actually spent. The last column describes work covered by the estimate; it does not prove that the work took the stated time. Without original time logs, an actual plan-versus-result deviation cannot be calculated.

PhaseReference plan (minutes; drafted afterward)Reference time with debugging (minutes; not measured)Work covered by the estimate
Requirements analysis4050Read the assignment and identify the four required feature groups and acceptance checks.
System design6070Define front-end/back-end responsibilities, API responses, and project structure.
Front-end development150180Build input, buttons, result display, history, and error feedback.
Back-end API development120140Implement routes, input checks, JSON responses, and CORS.
Expression parser120170Handle precedence, parentheses, unary signs, and invalid input.
Database design4050Create the SQLite table and initialize the database connection.
History features6080Save successful calculations, query records, and delete by ID.
Integration60100Check API URLs, request bodies, and response fields.
Testing and fixes90120Check arithmetic, compound expressions, errors, and history behavior.
Deployment60140Publish GitHub Pages, deploy Railway, and configure the database path.
Blog writing90120Prepare screenshots, API and code explanations, and project review.
Total8901220Approximately 14 hours 50 minutes and 20 hours 20 minutes, respectively.

Finished Product

The following 16 screenshots were captured from the deployed application. They cover the main calculator functions, compound expressions, error handling, history, and additional features. Some screenshots include records left by earlier test cases.

Figure 1: Main Interface

The page has a calculator area and a history area. It shows the back-end connection status, expression input, buttons, and saved records.

Figure 2: Addition

Entering12+8displays 20. The same expression and result appear in history.

Figure 3: Subtraction

Entering18-7returns 11, checking both the calculation endpoint and the page display.

Figure 4: Multiplication

Entering6*7returns 42. The interface may show a multiplication sign, while the request uses “*”.

Figure 5: Division

Entering14/2returns 7, matching the result returned by the back end.

Figure 6: Decimal Numbers

Entering0.1+0.2displays 0.3, demonstrating decimal expression handling.

Figure 7: Operator Precedence

Entering1+2*3returns 7 because multiplication is evaluated before addition.

Figure 8: Parentheses

Entering(1+2)*3returns 9. Compared with Figure 7, this demonstrates how parentheses change the calculation order.

Figure 9: Unary Plus and Minus

Entering3*-2 + +4returns -2. This checks a negative multiplication operand and a unary plus sign.

Figure 10: Invalid Expression

Entering1++*2displays an expression error from the back end. It is not stored as a successful calculation.

Figure 11: Division by Zero

Entering1/0displays a division-by-zero error and produces no valid result.

Figure 12: History After a Browser Refresh

After refreshing the browser, the input returns to its initial state while history is loaded again from the back end. The earlier record12345+55 = 12400is still present. This checks a browser refresh, not a back-end restart.

Figure 13: History Search

Typing12345+55into the history search box filters the loaded list to the record with result 12400. Search covers loaded records, not the entire database.

Figure 14: Delete a Specific Record

With the same search filter active, clicking the record’s delete button causes the page to display “No matching records.” The front end sends a deletion request to the back end.

Figure 15: History After Deletion and Another Refresh

After the deletion, a second browser refresh reloads history from the back end. The12345+55record no longer appears, while other records remain. The item was deleted from the database rather than hidden only on the page.

Figure 16: Dark Theme

Clicking the theme button in the upper-right corner switches the page to a dark appearance. The selection lasts only for the current page visit.

Design and Implementation

Requirements and Separation of Responsibilities

The assignment requires four arithmetic operations, compound expressions, decimals, parentheses, unary signs, error handling, database-backed history, and deletion of a specific record. Two central constraints are that the final result must be calculated by the back end and that history must be stored in a back-end database.

The front end uses HTML, CSS, and vanilla JavaScript. It collects expressions, sends HTTP requests, and displays results and history. The back end uses the Python standard library to provide a JSON API, while SQLite stores calculation records. The two parts are kept in separate GitHub repositories, each with its own README and code standard.

Functional structure:

CalcFlow ├── Front end: expression input -> HTTP request -> result/error/history display ├── HTTP API: validation, routing, JSON responses, CORS ├── Calculator: tokens, precedence, parentheses, unary signs, division-by-zero checks └── Data layer: SQLite insert, query, delete one record, clear all records

A successful request follows this sequence: the front end sends an expression; the back end validates, parses, and calculates it; the back end saves the result; the back end returns the result; and the front end updates the display and reloads history. The browser has no implementation that independently calculates the final answer.

Front-End and Back-End Design

The interface separates calculation from history. Users can type in the expression field or use calculator buttons. Multiplication and division buttons may display “×” and “÷”, but add “*” and “/” to the expression sent to the API. While a request is pending, the interface shows a waiting state. On success it displays the response’s result; on failure it displays the error message. History expressions are escaped before being inserted into HTML so that input is not interpreted as page markup.

The back end separates HTTP handling in server.py, expression processing in calculator.py, and SQLite operations in database.py. This keeps UI changes independent of parsing rules and separates API behavior from database access.

API Design and Interaction

Method and pathPurposeMain response
GET /api/healthService health check200 with a service name and success flag
POST /api/calculateSubmit an expression201 on success; 400 for an invalid expression
GET /api/history?limit=100Retrieve recent history200 with an items array
DELETE /api/history/{id}Delete one record200 on success; 404 if no such record exists
DELETE /api/historyClear all records200 with the deleted-record count

For example, a request body of {“expression”:“(1+2)*3”} returns success, id, expression, result, and createdAt after a successful calculation; result is 9, while the ID and timestamp are generated by the back end. Division by zero returns HTTP 400 with {“success”:false,“message”:“Division by zero is not allowed”}. The front end checks both the HTTP status and the success field.

The two services have different origins, so the back end returns CORS headers and handles OPTIONS preflight requests. The current allowed origin is “*”, suitable for this account-free course demo. The application does not provide user accounts or separate histories for different visitors.

Database and History

The calculation_history table contains:

FieldTypeMeaning
idINTEGER primary key, autoincrementUnique ID used to delete a record
expressionTEXT, not nullSubmitted expression with surrounding whitespace removed
resultREAL, not nullResult calculated by the back end
created_atTEXT, not nullCreation time in UTC ISO format

On first startup, the back end creates the data directory, database, and table. Only successful calculations are saved. History is returned in descending ID order; the default is the 100 most recent entries, and the limit parameter is restricted to 1–500. Front-end search filters only the records already loaded.

Deletion by ID uses a parameterized SQL statement. After deletion, the front end requests the latest history again. Clearing history deletes all records in the back-end database. A browser refresh triggers a new history API request rather than relying on browser storage.

Expression Parsing and Error Handling

The parser first turns the input into numeric, operator, and parenthesis tokens. It then processes expression -> term -> unary -> primary recursively. The expression level handles addition and subtraction, term handles multiplication and division, unary handles positive and negative signs, and primary handles numbers and parentheses. Therefore1+2*3evaluates multiplication first,(1+2)*3evaluates the parentheses first, and3*-2treats “-2” as a negative operand. Operators at the same level are processed from left to right.

The implementation does not call eval or exec. Inputs are limited to 200 characters and 100 tokens. Unknown characters, missing operands, mismatched parentheses, and division by zero raise ExpressionError. The back end rejects non-finite results. It uses floating-point arithmetic and rounds results to 12 decimal places, so it is a standard calculator rather than an arbitrary-precision system.

Empty input, API configuration, and network-error messages in the front end improve usability but do not replace server-side validation. Database and infrastructure errors are not all mapped to one standardized business error; this remains an area for improvement.

Key Code and Explanations

The following short excerpts come from the project; some line breaks have been adjusted for readability.

1. Send the Expression and Use the Back-End Result

constdata=awaitrequest('/calculate',{method:'POST',body:JSON.stringify({expression})});resultOutput.textContent=String(data.result);

Only the expression is sent. The displayed result comes directly from the response. The request() helper uses fetch and parses JSON; after calculation, loadHistory() retrieves the saved record instead of constructing a fake history item in the browser.

2. Calculate, Save, and Respond in That Order

expression=expression.strip()result=calculate(expression)record=repository.add(expression,result)self._send_json({"success":True,**record},HTTPStatus.CREATED)

This order ensures that the history result comes from the same back-end calculation. If calculation raises an expression error, repository.add() is not called.

3. Handle Unary Signs Separately

defparse_unary(self)->float:ifself.current()isnotNoneandself.current().kindin("+","-"):operator=self.consume(self.current().kind).kind value=self.parse_unary()returnvalueifoperator=="+"else-valuereturnself.parse_primary()

The multiplication and division layer calls the unary layer to read its operands. Thus the minus sign in3*-2is understood as part of the operand, rather than a binary subtraction with a missing left operand.

4. Insert History with SQL Parameters

cursor=connection.execute("INSERT INTO calculation_history(expression, result, created_at) VALUES (?, ?, ?)",(expression,result,timestamp),)

The SQL statement and expression data remain separate, preventing user input from being concatenated into SQL. Queries and deletions also use parameters, and the connection context commits a successful transaction.

Deployment and Verification

The front end is published with GitHub Pages, and the back end runs on Railway. A Dockerfile builds the back-end Python 3.12 container. The service listens on 0.0.0.0 and reads Railway’s PORT environment variable. A persistent volume is mounted at /data, the SQLite path is /data/calculator.sqlite3, and the health endpoint is /api/health.

The front-end config.js chooses the API URL according to the environment: local development uses http://127.0.0.1:8000/api, while the public site uses https://calculatorbackend-production-1bcb.up.railway.app/api. The address 127.0.0.1 refers to the visitor’s own device and cannot serve as a public back-end URL for the teaching assistant.

On October 5, 2026, 14 public API checks passed, covering the health endpoint, CORS preflight, eight types of expressions, invalid input, division by zero, history retrieval, and deletion by ID. The page, scripts, and stylesheet also returned HTTP 200, and the online configuration contained the correct back-end URL. Browser checks covered arithmetic, compound expressions, error feedback, history after refresh, search, deletion followed by refresh, theme switching, and Enter/Esc. History persistence after a back-end restart has not yet been tested; a configured volume is not presented as proof of a successful restart test.

Deployment required three separate pieces: static page hosting, a running back-end process, and database storage. A GitHub repository is a source-code address, while GitHub Pages serves only the static front end. The back end must run separately and respect the platform-assigned port. SQLite stored only in a temporary container directory could be lost, so the deployment uses a persistent volume. These configuration decisions do not prove every long-running failure scenario has been tested.

At the time of deployment, the Railway account showed a 30-day or USD 5 trial credit and no paid plan. Service status and remaining credit should be checked during the grading period.

Additional Features and Limitations

  • History search filters the most recent 100 records loaded by default. It does not search the entire database, and there is no pagination interface.
  • The theme button switches between light and dark styles for the current page visit. The choice is not saved across refreshes.
  • When the expression field has focus, Enter calculates and Esc clears the input. The calculation still takes place on the back end.
  • “Clear history” deletes all records from the current database. All visitors currently share one history table because the application has no accounts.

The history-search and theme-switching screenshots demonstrate the corresponding features. Enter and Esc were checked in the browser. The clear-all operation is implemented in code but was not covered by this set of screenshots or the online interaction checks. Possible improvements include paginated history, per-user records, saved theme preferences, and more consistent handling of service failures.

Personal Reflection

The most useful lesson from this project is that the front-end repository, public page, and back-end API have different roles and addresses. During deployment, the public page once returned 404. After it became accessible, the back-end health endpoint and the public API URL still needed separate checks. A page loading successfully does not prove that calculation, error handling, and history retrieval work, so testing must use specific input cases.

Compound expressions are easier to mishandle than a single arithmetic operation. The implementation separates addition and subtraction, multiplication and division, unary signs, and parentheses in a recursive-descent parser. Comparing1+2*3with(1+2)*3makes precedence visible, while3*-2, decimals, invalid expressions, and division by zero exercise important edge cases. A useful next learning step is to trace these examples through the parser functions and explain each call.

History requires checking both the visible page and the database state. The back end saves successful calculations to SQLite, and the page queries history again after a refresh. Deleting a specific record and refreshing once more verifies that the record was removed from the database rather than merely hidden in the UI. Persistence after a back-end restart still needs a separate test. Shared history across visitors and unsaved theme preferences are also limitations to address later.

There are no original time logs for this project, so the PSP figures above are a workload example, not evidence of actual efficiency. For the next assignment, the tasks should be broken down and estimated before implementation, with time recorded after each phase. Another goal is to reproduce the local startup and essential tests independently, so that the project can be explained, debugged, and modified rather than merely run.

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

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

立即咨询