173
社区成员
发帖
与我相关
我的任务
分享Fuzhou University Software Engineering Assignment 1
Calcura: A Separated Front-End and Back-End Calculator
Chen Linghan Student ID 832401201 Completed 3 October 2026
Course: Software Engineering, Fuzhou University. Assignment: the first project, a calculator with a separated front end and back end. The goal is a calculator that anyone can open on the public internet, together with a report that covers the repositories, code standards, PSP table, finished product, design, code, and personal summary.
This report implements that calculator. The browser only collects input, shows results, and handles interaction. Parsing, arithmetic, error checks, and history storage all happen on the server. A successful result is written to PostgreSQL and can still be read back after the page is refreshed.
3.1 Required functions
3.2 Extensions
7.1 Four operations
7.2 Precedence, parentheses, signs, and decimals
7.3 Invalid input and division by zero
7.4 Scientific functions and explanation steps
7.5 History, favorites, search, copy, CSV export, and refresh
8.1 Parsing rules
8.2 API
8.3 How one calculation runs
8.4 Why the front end sends only the expression
8.5 When the back end writes to the database
8.6 Parsing, precedence, validation, and errors
8.7 History operations and response format
Appendix A. Assignment blog checklist
| Item | Location |
|---|---|
| Name / student ID | Chen Linghan / 832401201 |
| Deadline | 7 October 2026, 23:59 |
| Front-end website | https://calcura-frontend.vercel.app/ |
| Back-end API | https://calcura-backend.vercel.app/ |
| API documentation | https://calcura-backend.vercel.app/docs |
| Front-end repository | https://github.com/chenlinghan82-debug/calcura-frontend |
| Back-end repository | https://github.com/chenlinghan82-debug/calcura-backend |
| Front-end code style | https://github.com/chenlinghan82-debug/calcura-frontend/blob/main/codestyle.md |
| Back-end code style | https://github.com/chenlinghan82-debug/calcura-backend/blob/main/codestyle.md |
Both main branches contain codestyle.md. The standard follows Google style for names, formatting, comments, and commit messages. This report quotes only the pieces needed to explain a design choice. The complete source is the two GitHub repositories.
The assignment asks for a calculator that other people can open, not a page that works only on one computer. The required behavior is addition, subtraction, multiplication, division, a separated front end and back end, operator precedence, parentheses, unary plus and minus, decimals, invalid input, division by zero, and history that is stored in a database, displayed, and deleted.
Calcura keeps that boundary strict. The browser does not call eval, and it does not calculate the answer before sending it. The front end submits the original expression. The back end returns the result, the explanation steps, and the database record. History is not stored in localStorage.
| Requirement | Implementation | Evidence |
|---|---|---|
| Four operations | + - * / ; buttons show + - x and division | Figures 3 to 6 |
| Separated services | The React page calls the FastAPI JSON API | Figures 1, 2, and 28 |
| Precedence | Recursive descent does multiplication and division before addition | Figure 8 |
| Parentheses | Parentheses change the order and may be nested | Figure 9 |
| Unary signs | -5 + 8, 3 x -2, and --5 | Figures 10 and 11 |
| Decimals | Decimal parses and calculates decimal values | Figure 7 |
| Errors | Empty input, illegal characters, bad parentheses, and division by zero return 400 | Figures 12 to 14 |
| Saved history | A successful calculation is written to calculation_history | Figures 22, 26, and 29 |
| Display and delete | History can be read, one row can be deleted, or all rows can be cleared | Figures 22 and 26 |
Power, square root, factorial, percent, absolute value, and the constants pi and e. Power is right associative. The automated test expects 2^3^2 = 512.
sin, cos, and tan use degrees, so sin(30) = 0.5. ln is the natural logarithm and log is the common logarithm.
Ans reads the latest successful row in the database. It is not a number kept temporarily in the browser.
Every successful row stores its steps. History can be marked as a favorite and searched by expression.
Statistics show the count, average, minimum, and maximum. Search filters the list only. It does not change those four numbers.
The page can copy a result, switch between dark and light themes, accept keyboard input, and stack the panels on a phone-width screen.
CSV export, the health check, Swagger documentation, and the API connection panel are included.
The system has three layers. The presentation layer collects the expression and renders the result. The application layer does lexical analysis, parsing, and the business rules. The data layer stores successful calculations only. A failed request does not add a row.
SYSTEM BOUNDARY browser, API, and database
Browser React + TypeScript
| HTTPS + JSON
v
FastAPI routes, validation, and error conversion
|
+-- Lexer
+-- Recursive-descent parser
|
v
SQLAlchemy --> Neon PostgreSQL
table calculation_history
| Module | Responsibility |
|---|---|
| frontend/src/App.tsx | Keyboard, buttons, theme, history list, favorites, search, and copy |
| frontend/src/services/api.ts | Requests for calculate, history, delete, statistics, export, and health |
| backend/app/api/routes.py | REST endpoints, status codes, and database transactions |
| backend/app/services/calculator.py | Safe parsing and arithmetic. It does not run code from the input. |
| backend/app/models/history.py | Columns of the history table |
| backend/tests | Parser, API, errors, history, and PostgreSQL URL handling |
| Layer | Choice | Reason |
|---|---|---|
| Front end | React, TypeScript, Vite | Component interface with clear types |
| Styling | Plain CSS | Responsive layout without a large component library |
| Back end | FastAPI | Explicit routes and generated OpenAPI documentation |
| Data access | SQLAlchemy 2 | Keeps route handlers separate from database operations |
| Production data | Neon PostgreSQL | History remains after the public deployment |
| Test data | SQLite | Automated tests do not depend on the cloud database |
| Tests | pytest | 46 tests are collected and passing |
| Hosting | Vercel and GitHub | Both services have public HTTPS addresses |
After the home page opens, the top-right corner says Backend online, and the footer names FastAPI, PostgreSQL, and React. API connection is not a decoration. Clicking it expands the real service address, health check, preview endpoint, calculate endpoint, history endpoint, and database type, and it links to Swagger. Arithmetic still runs only on the back end. While the user is building an expression, Calcura keeps the complete expression visible and does not display an answer or create a history row. Only pressing = or Enter submits the finished expression. The page first obtains the result and explanation steps from POST /api/preview, then calls POST /api/calculate to save the successful record. Both operations use the same server-side parser. The browser neither parses nor evaluates the expression.

Figure 1. Public home page. The expression is entered on the left and database history is shown on the right. No rows exist yet, so the statistics are 0.

Figure 2. API connection panel. The status is Backend online, the address is https://calcura-backend.vercel.app/, and the database type is postgresql.
The keyboard accepts digits and operators directly. Users can continue building a long expression without an intermediate answer replacing their input. Enter or = calculates, Esc clears the current input, and Backspace deletes one character. After a successful copy, the button text changes to Copied. The light theme changes only the display. It does not change where the calculation or storage happens.
The screenshots below come from the public website. In a few early screenshots, the status line still briefly says Calculating with the backend after the result and steps are already visible. That is the moment the screenshot was taken. It does not mean the browser calculated the result. The completed state is Result returned by the backend, together with the history row and the API response.

Figure 3. Addition: 12 + 8 = 20. The step is 12 + 8 = 20.

Figure 4. Subtraction: 20 - 8 = 12. The step is 20 - 8 = 12.

Figure 5. Multiplication: 6 x 7 = 42. The step is 6 x 7 = 42.

Figure 6. Division: 20 / 4 = 5. The step is 20 / 4 = 5.
The four captions below keep only the result, so an expression is not split across lines. The automated tests also cover 0.1 + 0.2 = 0.3. For 1 + 2 x 3, the steps first calculate 2 x 3 = 6 and then 1 + 6 = 7. For (1 + 2) x 3, the steps first calculate 1 + 2 = 3 and then 3 x 3 = 9. The tests also cover --5 = 5.

Figure 7. Decimals: 1.5 + 2.25 = 3.75.

Figure 8. Precedence: 1 + 2 x 3 = 7.

Figure 9. Parentheses: (1 + 2) x 3 = 9.

Figure 10. Unary minus: -5 + 8 = 3.

Figure 11. Negative multiplication: 3 x -2 = -6. A unary minus may follow the multiplication sign.
An incomplete expression, an unknown name, a bad decimal, unmatched parentheses, and division by zero are all rejected by the back end. The API returns HTTP 400. The page shows the reason in red, and the failed attempt is not written to the database.

Figure 12. After 12 +, the back end returns an invalid-expression message.

Figure 13. For 8 / 0, the page shows Division by zero is not allowed.

Figure 14. A direct POST /api/calculate with {"expression":"8/0"}. The response is HTTP 400 and the message is Division by zero is not allowed.
Scientific functions are also sent to the back end. The arguments of sin, cos, and tan are degrees, not radians, so sin(30) = 0.5, cos(60) = 0.5, and tan(45) = 1. tan(90), ln(0), log(-2), and the square root of a negative number are rejected. log(100) = 2, which is the common logarithm.

Figure 15. Power and sign: -2 ^ 2 = -4. Power binds before the unary minus, so the first step is 2 ^ 2 = 4.

Figure 16. Square root: sqrt(9) = 3.

Figure 17. Factorial: 5! = 120. Factorial accepts only integers from 0 to 170.

Figure 18. Percent: 50% = 0.5. The percent sign divides by 100.

Figure 19. The constant pi is 3.1415926536. pi and e are constants, not functions.

Figure 20. Sine: sin(30) = 0.5. Here 30 means 30 degrees.

Figure 21. Natural logarithm: ln(1) = 0. Absolute value is also covered by the history in Figure 26: abs(-12) = 12.
Right-associative power is covered by tests. 2^3^2 first calculates 3^2 and then 2^9, so the result is 512, not 64. The result of -2^2 is -4 because the unary minus does not become part of the power base.
Each history row contains the expression, result, steps, favorite flag, and creation time. The page converts the UTC time returned by the back end into the browser's local time. The 2026-10-02 16:02 shown in a screenshot corresponds to 2026-10-02T08:02Z in the API. The eight-hour difference is the time zone, not a wrong record.
The Export button downloads the stored calculation history as calcura-history.csv through GET /api/history/export. The file is generated by the back end from the database rather than copied from the currently visible rows. It contains id, expression, result, created_at, is_favorite, and steps, so the calculation data can be opened in Excel or another spreadsheet program for checking and analysis. The front end does not send the search keyword to the export route, so a search filters only the on-screen list; the CSV still contains up to the latest 1,000 stored records.

Figure 22. History, steps, and favorites. The step for 5 + 1 is 5 + 1 = 6. The stars on 12 + 8 and 20 - 8 are lit. At that moment there were 19 records, the average was 12.731136, and the range was -6 to 120.

Figure 23. After searching for sqrt, the list contains only sqrt(9) = 3 and says 1 result. The statistics above still cover all 19 records: average about 11.046926, range -6 to 120. Search does not recalculate the statistics.
The averages in Figures 22 and 23 differ because rows were added and deleted between the two screenshots, while both totals are 19. Statistics always describe every successful record in the database at that moment.

Figure 24. Copy succeeded in the dark theme. The button says Copied.

Figure 25. Light theme. 5 + 1 = 6, and the status is Result returned by the backend.

Figure 26. After a refresh, the current input returns to its initial state, but the history remains. The light page still shows 19 records, so the data comes from the database rather than page memory. The cross on each row deletes that record.
Deleting one record calls DELETE /api/history/{id}. Clearing everything calls DELETE /api/history. A favorite calls POST /api/history/{id}/favorite. The automated tests check all three operations.
This section explains the important implementation, as the assignment requires, without pasting the whole source. Sections 8.1 and 8.2 give the grammar and the API list. Sections 8.3 through 8.7 explain the front-end request, the back-end calculation, precedence, validation, errors, creating and deleting history, the response format, and why the code is written this way. The files are frontend/src/services/api.ts, backend/app/api/routes.py, backend/app/services/calculator.py, and backend/app/models/history.py. The colored boxes below are the code being explained.
The parser first splits the input into numbers, names, operators, and parentheses, then evaluates it by grammar level. It does not hand the string to Python for execution. The test input import('os') is recognized as an illegal name.
CODE 1 parsing grammar backend/app/services/calculator.py
expression = term ((+|-) term)*
term = unary ((*|/) unary)*
unary = (+|-) unary | power
power = postfix (^ unary)* right associative
postfix = primary (! | %)*
primary = number | constant | Ans | function(expression) | (expression)
Multiplication and division sit in an inner level, so they finish before addition and subtraction. The exponent of a power goes back into unary, so 2^3^2 is evaluated from right to left and the result is 512, not 64. The unary minus wraps the power. -2^2 first calculates 2^2 and then negates it, so the result is -4.
An expression may be at most 200 characters, and the absolute value of an exponent cannot exceed 1000.
Factorial accepts only integers from 0 to 170. Percent divides the current value by 100.
sqrt rejects a negative number. sin, cos, and tan convert degrees to radians before calculating.
Ans reads the latest successful record. Using Ans when there is no history returns a clear error.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/health | Check the service and the database type |
| POST | /api/preview | Return the back-end result without writing history. The page uses it to show an answer quickly. |
| POST | /api/calculate | Calculate the expression. Success returns 201 and saves the row. |
| GET | /api/history | Return history, newest first. keyword searches the expressions. |
| DELETE | /api/history/{id} | Delete one record |
| DELETE | /api/history | Clear all history |
| POST | /api/history/{id}/favorite | Toggle the favorite flag |
| GET | /api/stats | Return the count, average, minimum, and maximum |
| GET | /api/history/export | Export CSV |

Figure 27. Back-end Swagger at https://calcura-backend.vercel.app/docs. It lists the endpoints above and their request and response models.

Figure 28. GET /api/health returns HTTP 200. database is postgresql, so the live service is not using SQLite.

Figure 29. GET /api/history?limit=3 returns the latest 3 rows, while total is still 19. Each row includes steps and a UTC time.

Figure 30. In the same check, GET /api/stats returns total = 19, average = 11.046925929136304, minimum = -6, and maximum = 120.

Figure 31. The CSV export contains id, expression, result, created_at, is_favorite, and steps. The stored result of abs(-12) is 12, and two early rows have favorite set to True.
The work is split into four layers. Neither the calculation nor the save happens in the browser.
CODE 2 four layers of one calculation
Interface buttons, keyboard, theme, copy, and error text
Requests preview, calculate, history, delete, favorite, stats, export, health
Calculation lexer, recursive descent, Decimal, scientific functions, steps
Data save successful calculations only; query, delete, favorite, statistics, export
After the user presses = or Enter, the front end sends the original complete expression to /api/preview. The back end parses and calculates it without writing history, allowing the result and steps to be displayed. The front end then calls /api/calculate; only a successful official calculation creates a calculation_history row. Finally, the page requests /api/history to refresh the list. Merely typing or refreshing the page never calculates an expression or creates a record.
The body of calculate contains only the expression. There is no result field. The browser cannot calculate the answer first and then upload it.
CODE 3 front end frontend/src/services/api.ts calculate sends the expression only
export function calculate(expression: string) {
return request<CalculateResponse>('/api/calculate', {
method: 'POST',
body: JSON.stringify({ expression }),
})
}
POST uses text/plain so the browser can skip one CORS preflight and the public site waits for one fewer round trip. This is not local calculation. The content type is set in the shared request function, before calculate sends the body.
CODE 4 front end frontend/src/services/api.ts POST is text/plain
if (method === 'POST' && init?.body && !headers.has('Content-Type')) {
headers.set('Content-Type', 'text/plain;charset=UTF-8')
}
On failure, the page reads the message from the back end and shows it in red. The preview request starts only after = or Enter is pressed. If the same completed expression is submitted again within a short time, the request layer may reuse that exact result only after the back end has already calculated it; typing alone never enters the preview cache. If the back end is stopped, the page can still accept input, but it cannot produce a new correct result.
/api/calculate adds a row only after parsing succeeds. The success status is 201. Division by zero, an empty expression, and illegal characters become HTTP 400 before any insert, so a failure leaves no history.
CODE 5 back end backend/app/api/routes.py write the row only after success
def calculate(
request: CalculateRequest,
db: Session = Depends(get_db),
) -> CalculateResponse:
needs_ans = expression_needs_ans(request.expression)
ans = latest_answer(db) if needs_ans else None
try:
result, steps = explain_expression(request.expression, ans)
except CalculationError as exc:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail={"success": False, "message": str(exc)},
) from exc
record = CalculationHistory(
expression=request.expression,
result=result,
is_favorite=False,
steps_json=dump_steps(steps),
)
db.add(record)
db.commit()
Preview of an ordinary expression does not query the database. Only when the expression contains Ans does it read the latest successful record, and preview still does not insert a row. Typing can therefore show a back-end result quickly, while pressing equals is what saves it.
The parser is handwritten recursive descent. It does not use eval or exec, and it does not give the user input to a third-party expression library. The lexer accepts only numbers, names, the four arithmetic operators, power, parentheses, percent, and factorial. An unrecognized character raises Unsupported character. Empty input raises Expression cannot be empty. import('os') is treated as a name and is then rejected because it is not pi, e, Ans, or an allowed function. User input therefore cannot become Python code.
Precedence comes from which function calls which, not from temporarily rewriting multiplication before addition. _expression calls _term first, and only _term does multiplication and division, so 1+2*3 first becomes 6 and then 1+6. Parentheses recursively call the whole expression at the innermost level, so they can change the order and can be nested. Decimals use Decimal, which avoids the binary floating-point error in 0.1+0.2. Scientific functions are rounded to a limited number of decimal places only at the end. sin, cos, and tan convert degrees to radians first, so sin(30) equals 0.5.
Validation has three layers. The lexical layer rejects an empty string, an illegal character, and a bad decimal such as 2..3. The syntax layer rejects a missing parenthesis, an operator with no following number, and an unknown name. The semantic layer rejects division by zero, the square root of a negative number, ln(0), log(-2), tan(90), an exponent that is too large, and a non-integer factorial. Division by zero is raised where the division happens:
CODE 6 back end backend/app/services/calculator.py reject division by zero
if operator == "*":
result = left * right
else:
if right == 0:
raise CalculationError("Division by zero is not allowed")
result = left / right
The route catches CalculationError and returns HTTP 400. The body has success set to false and a message. The front end shows that sentence and does not show a stack trace. If a record does not exist, delete and favorite return 404. Bad input is not a 500, because the problem is the user's expression, not a server crash.
A successful calculation creates a row. Queries are ordered by creation time, newest first. keyword filters expressions only and does not change the statistics. Deleting one record first loads it by id. A missing id returns 404; an existing id is deleted and committed. Clearing all history is an extension and returns the number of rows actually deleted. A favorite only toggles the flag and does not change the result. Ans uses the latest successful record. Using Ans when no history exists returns a clear error instead of silently treating it as 0.
The official calculation returns the expression, result, steps, and the complete record. Preview returns the same result and steps, but saved is false. The history response returns both items and total, so the page can show only the newest few rows while still saying how many rows the database contains. The statistics endpoint returns the count, average, minimum, and maximum separately, so a search cannot make the average look like the average of the visible list.
The live service has one business table, calculation_history. Local pytest uses SQLite with the same columns. The Vercel back end connects to Neon PostgreSQL through an environment variable. The database type returned by the health check distinguishes these two environments.
| Column | Type | Meaning |
|---|---|---|
| id | integer, primary key | Record number |
| expression | string, at most 200 | The original expression submitted by the user |
| result | floating point | Result calculated by the back end |
| is_favorite | boolean | Favorite flag, default false |
| steps_json | string, at most 2000 | JSON array of explanation steps |
| created_at | timezone-aware datetime | Stored in UTC |
pytest in the back-end test directory reports 46 passed. The local tests use a temporary SQLite database and do not change the live PostgreSQL database.
| Group | Count | What it covers |
|---|---|---|
| Valid expressions | 23 | Four operations, precedence, parentheses, signs, decimals, power, percent, factorial, and scientific functions |
| Invalid expressions | 14 | Empty input, division by zero, incomplete expressions, parentheses, bad decimals, unknown names, huge exponents, and illegal function arguments |
| Code injection | 1 | Rejects treating Python code as an expression |
| Ans and steps | 1 | Steps for (2+3)4, and Ans3 |
| API | 7 | Health, save, search, error codes, delete, clear, statistics, favorites, export, and database URL normalization |
Valid samples include 1+2*3=7, (1+2)*3=9, 0.1+0.2=0.3, 2^3^2=512, -2^2=-4, sin(30)=0.5, and log(100)=2.
Invalid samples include 1/0, 1+, (1+2, 2..3, 2**3, sqrt(-1), 1.5!, ln(0), log(-2), and tan(90).
The front end and the back end are two GitHub repositories, deployed separately on Vercel. The database address is a Vercel environment variable. The repository does not contain the database password. Pushing code to main redeploys the matching Vercel project. Changing only this report or its screenshots does not require a new deployment. The calculation service is in San Francisco. The current access path arrives there first, so keeping the function in the same region avoids an extra trip through Hong Kong and returns the answer sooner.
On a computer, open https://calcura-frontend.vercel.app/. The top-right corner should say Backend online.
For the API, open https://calcura-backend.vercel.app/api/health. database should be postgresql.
If a phone on mobile data cannot open the site, the phone network usually cannot reach Vercel. A proxy running on the computer is not applied to the phone automatically. Connect the phone to Wi-Fi that can reach the site, or turn on the same proxy on the phone and then open it.
The statistics card in Figure 33 still describes every record: 19 rows, an average of about 11.046926, and a range from -6 to 120. Only the newest 6 rows are shown so the card fits the page. The footer still says 19 results.

Figure 32. Phone width. Function keys are above the numeric keypad.

Figure 33. Phone-width history. The total is still 19; the newest 6 rows are shown.
The table below is my own time estimate for this assignment, in hours. It records planned and actual effort. It is not an audit by someone else.
| Stage | Planned | Actual | Main work |
|---|---|---|---|
| Reading and planning | 1.5 | 2.0 | Confirm the front-end and back-end duties and the deliverables |
| Requirements and design | 2.5 | 3.0 | Function list, API, and table structure |
| Back-end implementation | 6.0 | 8.0 | Parser, API, errors, and database |
| Front-end implementation | 5.0 | 7.0 | Interface, history, theme, search, and mobile layout |
| Testing | 2.5 | 3.5 | 46 automated tests and public manual checks |
| Deployment | 2.0 | 3.0 | GitHub, Vercel, and PostgreSQL |
| Report | 2.5 | 3.0 | Screenshots, captions, and writing |
| Total | 22.0 | 29.5 | Extra time went mainly to deployment and checking screenshots |
The rows follow the stages suggested by the course. Reading and planning is requirements confirmation. Requirements and design includes the system design, the API, and the database table. Back-end implementation includes expression calculation, error handling, and writing history. Front-end implementation includes showing history, deleting it, and interaction. Testing includes the joint check. Deployment and the report correspond to going online and writing the blog. Planned hours were estimated before development. Actual hours were filled in after the work was finished.
The point of this assignment was not to draw a calculator. It was to keep the calculation and the data on the server. Calcura uses its own recursive-descent parser instead of eval, and PostgreSQL stores the history. The health check, Swagger, steps, favorites, search, statistics, and CSV export are there to show the front-end and back-end process.
While debugging, I mainly watched three things: the 46 pytest tests, the status code of a direct API request, and whether the request body sent by the browser was the original expression. After the back end is stopped, the page still opens, but it cannot produce a new answer. That shows the calculation was not left in the browser.
On the public network, a cross-origin preflight can delay the first request, so POST was changed to text/plain to avoid an unnecessary round trip. The interface now keeps a long expression visible while it is being entered and does not show a result until = or Enter is pressed. The health check keeps the service connection active without evaluating an expression or creating history. Preview and the official calculation both remain on the back end, and only the successful official calculation is saved. A phone on mobile data could not open the site because that network could not reach Vercel, not because the page was built only for a computer.
The easy expression mistakes are power and the minus sign. 2^3^2 must be read from right to left, and the result is 512. The result of -2^2 is -4. Trigonometric functions use degrees rather than radians, so sin(30) is 0.5. The database stores UTC, and the page converts it to local time, so a screenshot and the API differ by 8 hours. The record is not wrong. Search filters the list only. Statistics still cover every successful record.
For deployment, local tests use temporary SQLite and the live site uses Neon PostgreSQL. The database address is a Vercel environment variable, and the repository has no password. /api/health returns postgresql, which shows that the live site is not SQLite. What I learned most clearly is how recursive descent expresses precedence, why decimals need Decimal, and what 201, 400, and 404 should each mean.
A later version could add login, so each person sees only their own history. The public database currently holds the demonstration data for this assignment, so the scope was not expanded into an unfinished account system.
The Chinese CSDN post keeps a table-of-contents marker at the top. This Word report uses the Contents list in the opening section instead. The table below maps the course requirements to this report.
| Course requirement | Where it is in this report |
|---|---|
| Assignment background | Section 2 |
| Requirements analysis | Section 3 |
| System architecture | Sections 4, 5, and 6 |
| API design | Section 8.2 |
| Database design | Section 9 |
| Code explanation | Section 8, especially Sections 8.3 to 8.7 |
| Front-end and back-end flow | Sections 8.3 and 8.4 |
| Expression parsing | Sections 8.1 and 8.6 |
| Exception handling | Sections 7.3 and 8.6 |
| Calculation and history modules | Sections 8.5 to 8.7 |
| Deployment address | Sections 1 and 11 |
| GitHub addresses | Section 1 |
| PSP table | Section 12 |
| At least 10 screenshots | Figures 1 through 33 |
| Personal summary | Section 13 |
CSV export, the health check, Swagger, and API connection are in Sections 3.2, 6, and 8.2, and in Figures 2 and 27 through 31. These extensions do not move the calculation into the browser.
Course brief: CSDN discussion, First Assignment: Development of a Separated Front-End and Back-End Calculator System.
FastAPI documentation: https://fastapi.tiangolo.com/
React documentation: https://react.dev/
SQLAlchemy documentation: https://docs.sqlalchemy.org/
Project API documentation: https://calcura-backend.vercel.app/docs