Software Engineering Assignment 1: Calcura Full-Stack Calculator — Chen Linghan 832401201

832401201陈凌晗 2026-10-03 13:25:48

目录

  • 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

    1. GitHub repositories and code standards
    1. Background and objectives
    1. Requirements
  • 3.1 Required functions

  • 3.2 Extensions

    1. Design and implementation
    1. Technology choices
    1. Interface and the service boundary
    1. 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

    1. 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

    1. Database design
    1. Tests
    1. Deployment and access
    1. PSP table
    1. Personal summary
  • Appendix A. Assignment blog checklist

    1. References

1. GitHub repositories and code standards

ItemLocation
Name / student IDChen Linghan / 832401201
Deadline7 October 2026, 23:59
Front-end websitehttps://calcura-frontend.vercel.app/
Back-end APIhttps://calcura-backend.vercel.app/
API documentationhttps://calcura-backend.vercel.app/docs
Front-end repositoryhttps://github.com/chenlinghan82-debug/calcura-frontend
Back-end repositoryhttps://github.com/chenlinghan82-debug/calcura-backend
Front-end code stylehttps://github.com/chenlinghan82-debug/calcura-frontend/blob/main/codestyle.md
Back-end code stylehttps://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

RequirementImplementationEvidence
Four operations+ - * / ; buttons show + - x and divisionFigures 3 to 6
Separated servicesThe React page calls the FastAPI JSON APIFigures 1, 2, and 28
PrecedenceRecursive descent does multiplication and division before additionFigure 8
ParenthesesParentheses change the order and may be nestedFigure 9
Unary signs-5 + 8, 3 x -2, and --5Figures 10 and 11
DecimalsDecimal parses and calculates decimal valuesFigure 7
ErrorsEmpty input, illegal characters, bad parentheses, and division by zero return 400Figures 12 to 14
Saved historyA successful calculation is written to calculation_historyFigures 22, 26, and 29
Display and deleteHistory can be read, one row can be deleted, or all rows can be clearedFigures 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
ModuleResponsibility
frontend/src/App.tsxKeyboard, buttons, theme, history list, favorites, search, and copy
frontend/src/services/api.tsRequests for calculate, history, delete, statistics, export, and health
backend/app/api/routes.pyREST endpoints, status codes, and database transactions
backend/app/services/calculator.pySafe parsing and arithmetic. It does not run code from the input.
backend/app/models/history.pyColumns of the history table
backend/testsParser, API, errors, history, and PostgreSQL URL handling

5. Technology choices

LayerChoiceReason
Front endReact, TypeScript, ViteComponent interface with clear types
StylingPlain CSSResponsive layout without a large component library
Back endFastAPIExplicit routes and generated OpenAPI documentation
Data accessSQLAlchemy 2Keeps route handlers separate from database operations
Production dataNeon PostgreSQLHistory remains after the public deployment
Test dataSQLiteAutomated tests do not depend on the cloud database
Testspytest46 tests are collected and passing
HostingVercel and GitHubBoth 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

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

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

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

Figure 4

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

Figure 5

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

Figure 6

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

Figure 7. Decimals: 1.5 + 2.25 = 3.75.

Figure 8

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

Figure 9

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

Figure 10

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

Figure 11

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

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

Figure 13

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

Figure 14

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

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

Figure 16

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

Figure 17

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

Figure 18

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

Figure 19

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

Figure 20

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

Figure 21

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

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

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

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

Figure 25

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

Figure 26

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

MethodPathPurpose
GET/api/healthCheck the service and the database type
POST/api/previewReturn the back-end result without writing history. The page uses it to show an answer quickly.
POST/api/calculateCalculate the expression. Success returns 201 and saves the row.
GET/api/historyReturn history, newest first. keyword searches the expressions.
DELETE/api/history/{id}Delete one record
DELETE/api/historyClear all history
POST/api/history/{id}/favoriteToggle the favorite flag
GET/api/statsReturn the count, average, minimum, and maximum
GET/api/history/exportExport CSV

Figure 27

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

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

Figure 29

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

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

Figure 31

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.

ColumnTypeMeaning
idinteger, primary keyRecord number
expressionstring, at most 200The original expression submitted by the user
resultfloating pointResult calculated by the back end
is_favoritebooleanFavorite flag, default false
steps_jsonstring, at most 2000JSON array of explanation steps
created_attimezone-aware datetimeStored 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.

GroupCountWhat it covers
Valid expressions23Four operations, precedence, parentheses, signs, decimals, power, percent, factorial, and scientific functions
Invalid expressions14Empty input, division by zero, incomplete expressions, parentheses, bad decimals, unknown names, huge exponents, and illegal function arguments
Code injection1Rejects treating Python code as an expression
Ans and steps1Steps for (2+3)4, and Ans3
API7Health, 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

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

Figure 33

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.

StagePlannedActualMain work
Reading and planning1.52.0Confirm the front-end and back-end duties and the deliverables
Requirements and design2.53.0Function list, API, and table structure
Back-end implementation6.08.0Parser, API, errors, and database
Front-end implementation5.07.0Interface, history, theme, search, and mobile layout
Testing2.53.546 automated tests and public manual checks
Deployment2.03.0GitHub, Vercel, and PostgreSQL
Report2.53.0Screenshots, captions, and writing
Total22.029.5Extra 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 requirementWhere it is in this report
Assignment backgroundSection 2
Requirements analysisSection 3
System architectureSections 4, 5, and 6
API designSection 8.2
Database designSection 9
Code explanationSection 8, especially Sections 8.3 to 8.7
Front-end and back-end flowSections 8.3 and 8.4
Expression parsingSections 8.1 and 8.6
Exception handlingSections 7.3 and 8.6
Calculation and history modulesSections 8.5 to 8.7
Deployment addressSections 1 and 11
GitHub addressesSection 1
PSP tableSection 12
At least 10 screenshotsFigures 1 through 33
Personal summarySection 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

...全文
80 回复 打赏 收藏 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
内容概要:本文提出了一种基于PSCAD与MATLAB联合仿真的IEEE 9节点电力系统网络攻击数据集生成框架,旨在为智能电网的安全防护研究提供高保真、可复现的攻击数据支持。该框架深度融合PSCAD的电磁暂态仿真能力与MATLAB在数据分析、算法建模及自动化控制方面的优势,实现了对虚假数据注入攻击(FDIA)等典型网络攻击场景的精确模拟。研究系统性地构建了从电力系统建模、正常工况仿真、攻击向量设计与动态注入、到多源数据采集、时序对齐与标准化后处理的完整技术链条。框架特别强调攻击的隐蔽性特征与系统动态响应的耦合关系,能够生成涵盖电压、电流、功率、频率等多维度、长时间序列的时空数据集,有效反映攻击对状态估计、潮流分布及系统稳定性的影响,最终输出适用于机器学习模型训练、验证与攻击检测算法评估的高质量标准化数据集。; 适合人群:从事电力系统安全、网络安全、智能电网、工业控制系统安全等领域研究的高校研究人员、研究生,以及电力行业从事安全防护技术研发的工程师。; 使用场景及目标:①为电力系统网络攻击检测、防御机制与韧性提升技术的研究提供高可信度的仿真数据支撑;②支撑基于数据驱动的异常检测、入侵识别、攻击溯源等智能算法的开发与验证;③用于教学与培训,帮助学生和从业人员理解网络攻击对物理系统的影响机理。; 阅读建议:建议读者结合PSCAD和MATLAB软件环境,动手复现文中所述的联合仿真流程,重点关注攻击注入接口的设计与数据同步机制,并尝试在此框架基础上扩展其他类型的攻击场景或应用不同的检测算法进行验证。
内容概要:本文围绕跟网型储能变流器在dq坐标系下的阻抗建模展开深入研究,提出了一种基于电感电容电流环PI控制与前馈补偿矩阵构造的理论建模方法,并在Simulink仿真平台上完成了模型搭建与对比验证。研究系统地分析了电流环PI控制器对系统阻抗特性的影响机制,构建了包含前馈补偿环节的完整控制框图,并推导出变流器在dq域的小信号阻抗模型。通过仿真手段对比了理论模型与仿真结果在不同频率下的阻抗幅频与相频特性,验证了模型的有效性与准确性,揭示了控制器参数对系统稳定性的作用规律,为储能系统并网稳定性分析与优化设计提供了坚实的理论支撑与技术路径。; 适合人群:从事电力电子、新能源并网、储能系统控制、微电网稳定性分析及相关领域的科研人员与工程技术人员,具备自动控制理论、电力系统分析及Simulink仿真基础的专业人士; 使用场景及目标:①掌握跟网型变流器在dq域的阻抗建模方法与理论推导流程;②理解PI控制器与前馈补偿对系统阻抗特性的影响机理;③应用于储能系统并网稳定性评估、振荡问题分析及控制器参数优化设计; 阅读建议:建议读者结合文中理论推导与Simulink仿真模型同步学习,重点关注控制结构框图的构建、小信号建模过程及仿真验证方法,通过调整控制器参数观察阻抗特性变化,深化对系统稳定性的理解与工程应用能力。

173

社区成员

发帖
与我相关
我的任务
社区描述
梅努斯-软件工程
软件工程 高校 福建省·福州市
社区管理员
  • FZU_SE_LQF
  • zjcTander
  • 木村修
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧