Software Engineering Assignment 1: Calcura Full-Stack Calculator — Chen Linghan 832401201
目录
- Contents
- 1. GitHub repositories and code standards
- 2. Background and objectives
- 3. Requirements
- 3.1 Required functions
- 3.2 Extensions
- 4. Design and implementation
- 5. Technology choices
- 6. Interface and the service boundary
- 7. Finished product
- 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. Code explanation
- 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
- 9. Database design
- 10. Tests
- 11. Deployment and access
- 12. PSP table
- 13. Personal summary
- Appendix A. Assignment blog checklist
- 14. References
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.
Contents
- GitHub repositories and code standards
- Background and objectives
- Requirements
3.1 Required functions
3.2 Extensions
- Design and implementation
- Technology choices
- Interface and the service boundary
- Finished product
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
- Code explanation
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
- Database design
- Tests
- Deployment and access
- PSP table
- Personal summary
Appendix A. Assignment blog checklist
- References
1. GitHub repositories and code standards
| 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.
2. Background and objectives
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.
3. Requirements
3.1 Required functions
| 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 |
3.2 Extensions
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.
4. Design and implementation
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 |
5. Technology choices
| 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 |
6. Interface and the service boundary
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.
7. Finished product
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.
7.1 Four operations

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.
7.2 Precedence, parentheses, signs, and decimals
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.
7.3 Invalid input and division by zero
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.
7.4 Scientific functions and explanation steps
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.
7.5 History, favorites, search, copy, CSV export, and refresh
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.
8. Code explanation
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.
8.1 Parsing rules
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.
8.2 API
| 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.
8.3 How one calculation runs
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.
8.4 Why the front end sends only the expression
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.
8.5 When the back end writes to the database
/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.
8.6 Parsing, precedence, validation, and errors
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.
8.7 History operations and response format
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.
9. Database design
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 |
10. Tests
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).
11. Deployment and access
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.
12. PSP table
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.
13. Personal summary
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.
Appendix A. Assignment blog checklist
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.
14. References
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
相关推荐
从注意力到行动 pdf Agent
从注意力到行动 pdf
同步电机与构网型变流器的频率稳定特性及多时间尺度交互机理研究(Simulink仿真实现)
内容概要:本文系统解读了一套完整的多机器人路径规划(MAPF)MATLAB代码库,重点围绕优先级规划算法展开,涵盖从地图解析、拓扑构建、路径规划、冲突消解到可视化呈现的全流程。文章提出四层架构设计:几何层负责地图解析与栅格化;拓扑层构建四邻域图与Petri网模型;调度层实现基于动态边权膨胀的优先级路径规划与时间同步冲突消解;呈现层完成动画与视频输出。核心创新包括动态边权膨胀机制避免硬封锁导致的虚假不可解、节点预留历史实现高效冲突查询、几何惩罚驱动的局部同步提升效率,以及Petri网双阶段优化支持解的存在性与最优性分离判断。框架兼顾实用性与可扩展性,适用于大规模智能体场景。; 适合人群:具备MATLAB编程基础,从事机器人、自动化、智能交通或路径规划相关研究的科研人员与研究生。; 使用场景及目标:① 学习并复现多机器人无冲突路径规划的经典与先进算法;② 掌握动态边权膨胀、时间同步、冲突消解等关键技术的工程实现;③ 借助模块化框架进行算法改进与性能优化,支撑仓储物流、无人机编队等应用场景的仿真研究。; 阅读建议:建议按文中所述的数据流顺序逐步阅读,重点关注各模块接口设计与算法取舍逻辑,结合提供的完整资源进行代码调试与实验验证,深入理解“空间规划-时间消解”分离的设计思想。
huashu-motion 让编码 Agent 直出商业宣传片-一个专门给 Claude Code、Codex 等工具使用的 Skill
huashu-motion 一个专门给 Claude Code、Codex 等工具使用的 Skill。它不跑黑盒视频生成模型,而是让 Agent 直接用代码绘制动画,成片就是一个网页。 项目最大的特色是“先写谱再拍片”,Agent 能写纯文本乐谱配合乐器采样在本地配乐,并精准对齐每帧音效。在浏览器里按 C 键点击画面就能直接留批注,Agent 会定位源码只改动那一处,也可以在右侧表单手动调参。 它还内置了从顶级发布片提炼的 20 种运动语言、自动化质检与并行渲染机制,小模型同样能做出高级感。
YOLO26算法校园作业场景锚目标检测+训练好的模型+103张数据集+pyqt可视化界面.zip
附训练好的 YOLO 权重与 PyQt 可视化检测界面,页面底部含可视化效果预览,毕设/课设开箱即用。 【数据集概况】 · 检测类别(中文):[锚(anchor)] · 训练集:90 张 · 验证集:9 张 · 测试集:4 张 · 总计:103 张 该数据集聚焦校园作业场景,通过精准标注手写文字中的锚元素,为教育场景下的文本识别与作业分析提供高质量数据支撑,助力教学评估与学生书写质量研究。该数据集训练集占比90%,验证集与测试集分别占9%和4%,符合目标检测任务数据划分的常规比例,训练集规模充足以保障模型学习能力,验证集与测试集合理分配用于模型性能评估,确保数据分布科学合理。该数据集标注工作严格遵循规范,所有锚元素的边界框标注精准,标注内容与... 【训练曲线与评估图】 【模型训练配置】 参数 | 值 模型 | yolo26n 训练轮数 | 100 epochs 输入尺寸 | 640x640 批次大小 | 24 优化器 | auto 初始学习率 | 0.01 训练设备 【关键指标汇总】 训练了 100 个 epoch,最终轮指标: 指标 | 数值 mAP50 | **0.6351** mAP50-95 | 0.1958 Precision | 0.6436 Recall | 0.5714 train/box_loss | 1.6649 train/cls_loss | 1.3181 val/box_loss | 2.3156 val/cls_loss | 2.5145 【训练过程分析】 100 轮训练后 mAP50 仅 0.6351,模型收敛不充分。Loss 曲线前期下降后进入平台期,mAP 提升缓慢。mAP50-95 为 0.1958,定位精度同样偏低。 【模型性能评估】 Precision 0.643... 使用场景:毕业设计、课程设计、深度学习入门、工业场景落地验证。
基于扩展卡尔曼滤波(EKF)的激光雷达与雷达异构传感器融合算法研究(Matlab代码实现)
内容概要:本文系统解读了一套完整的多机器人路径规划(MAPF)MATLAB代码库,聚焦于优先级规划算法的工程实现。该框架将求解过程划分为几何、拓扑、调度与呈现四个解耦层次,实现了从地图解析、四邻域拓扑与Petri网建模,到基于优先级启发式的路径规划、时间同步冲突消解,再到动画可视化的全流程闭环。其核心创新在于“空间规划-时间消解”分离的设计思想:通过动态边权膨胀策略取代传统硬封锁,允许后续智能体在必要时穿越先前路径,提升狭窄环境下的规划成功率;利用节点预留历史机制实现高效的冲突查询,并结合几何惩罚机制在规划阶段预测潜在冲突,显著提高时间同步效率。框架还支持Petri网整数规划求解路径,增强了算法的完备性与优化潜力,整体具备良好的可复现性、可扩展性与教学价值。; 适合人群:具备一定编程基础和机器人路径规划基础知识,从事相关科研或工程应用的研发人员,尤其是高校研究生及自动化、人工智能方向的技术人员。; 使用场景及目标:①应用于仓储物流、无人机编队、自动驾驶等多智能体协同场景中的路径规划问题;②为MAPF算法的教学、实验与性能评估提供可复现的MATLAB实现参考;③支持对优先级规划、冲突消解机制、Petri网建模等关键技术进行二次开发与优化验证。; 阅读建议:建议读者按照文中所述的数据流顺序逐步学习,重点关注各模块之间的接口设计与信息传递机制,结合提供的可视化工具调试与观察算法行为,深入理解“空间规划-时间消解”分离的设计思想,并可在实际应用中尝试调整惩罚权重、同步策略等参数以优化性能。


