Changes between Initial Version and Version 1 of BuildInstructions


Ignore:
Timestamp:
09/24/26 15:12:00 (2 days ago)
Author:
231285
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • BuildInstructions

    v1 v1  
     1= Build Instructions =
     2
     3This page explains how to compile, configure, run and test the !EduBerza prototype.
     4It is linked from [wiki:PrototypeImplementation].
     5
     6== Development environment description ==
     7
     8||= Tool =||= Version tested =||= Needed for =||
     9|| Go || 1.26.0 (`go.mod` asks for 1.25 or newer) || Building `server/` (the CLI) and `bots/` (the market bot). ||
     10|| PostgreSQL || 16.3 (Docker container) || The database. Either the local Docker container or the faculty server. ||
     11|| Docker + Docker Compose || any recent || Optional. Starts a local PostgreSQL with one command. ||
     12|| `psql` || 16 || Optional. Only for running the SQL scripts by hand. ||
     13|| Java || 21 (8+ works) || Optional. Only to open or edit the ER diagram in TerraER. ||
     14|| DBeaver || any recent || Optional. Only to export `relational_schema.jpg`. ||
     15
     16About the PostgreSQL version: `docker-compose.yml` uses the image `postgres` without a version
     17tag. Docker therefore starts whatever version of the official image it has pulled. On the
     18machine where the prototype was tested, that was PostgreSQL 16.3. The only extension the schema
     19needs is `pgcrypto` (`CREATE EXTENSION IF NOT EXISTS pgcrypto`). It ships with PostgreSQL and is
     20included in the official image.
     21
     22You do not need to install anything else. The only third-party Go library is the PostgreSQL
     23driver `github.com/lib/pq`. `go build` downloads it automatically, at the version pinned in
     24`go.mod` and `go.sum`.
     25
     26== Build instructions ==
     27
     28Run all commands from the repository root.
     29
     30=== 1. Configure the database connection ===
     31
     32{{{
     33cp .env.example .env
     34}}}
     35
     36The defaults in `.env.example` (`localhost:5433`, user `bp_project`, database `bp_database`)
     37match the bundled Docker setup. To use the faculty database instead, edit `.env`, or pass the
     38values as real environment variables. Real environment variables take precedence over the file:
     39
     40{{{
     41DBHOST=... DBPORT=5432 DBUSER=... DBPASSWORD=... DBNAME=... ./eduberza
     42}}}
     43
     44`.env` is not committed on purpose (see `.gitignore`), because it holds a password.
     45
     46=== 2. Start PostgreSQL ===
     47
     48{{{
     49docker compose up -d
     50}}}
     51
     52Skip this step if you use the faculty database.
     53
     54=== 3. Build ===
     55
     56{{{
     57go build -o eduberza ./server
     58}}}
     59
     60=== 4. Create the schema and load the sample data ===
     61
     62{{{
     63./eduberza -init
     64}}}
     65
     66This runs `server/db/schema_creation.sql` and then `server/db/data_load.sql`. It logs
     67`Running schema_creation.sql ...`, `Running data_load.sql ...` and `Database initialised.`,
     68then prints:
     69
     70{{{
     71Schema initialised. Re-run without -init to start the CLI.
     72}}}
     73
     74Both scripts are '''compiled into the binary''' (`go:embed`), so `-init` works from any
     75directory. It is destructive and can be run again any number of times: it drops and recreates
     76the whole `project` schema, so it also resets everything if a demo goes wrong. To reload only
     77the data and keep the schema:
     78
     79{{{
     80./eduberza -load-data          # prints "Sample data reloaded."
     81}}}
     82
     83If you prefer to watch the statements run, the same can be done with `psql`:
     84
     85{{{
     86psql "postgresql://$DBUSER:$DBPASSWORD@$DBHOST:$DBPORT/$DBNAME" \
     87  -f server/db/schema_creation.sql
     88psql "postgresql://$DBUSER:$DBPASSWORD@$DBHOST:$DBPORT/$DBNAME" \
     89  -f server/db/data_load.sql
     90}}}
     91
     92=== 5. Run the prototype ===
     93
     94{{{
     95./eduberza
     96}}}
     97
     98=== 6. Optional: run the market simulation bot ===
     99
     100In a second terminal, also from the repository root (the bot reads `.env` from the current
     101directory):
     102
     103{{{
     104go run ./bots                  # add -interval 1s for faster ticks; the default is 3s
     105}}}
     106
     107On every tick the bot moves the price of every active market by a small random step, inserts a
     108row into `market_trades` and updates the current 1-minute candle. Prices in the CLI change
     109while it runs, because the current price is always read from the most recent trade
     110(`v_latest_prices`) and never from a stored column. Leave the bot off if you want the exact
     111numbers in the tests below.
     112
     113=== 7. Optional: richer data for the P6 reports ===
     114
     115`data_load.sql` seeds only a few minutes of trade history. That is not enough for the
     116top traders and market performance reports of [wiki:AdvancedReports] (menu `[10]` and `[11]`)
     117to show more than one period. To see more interesting results, load five quarters of synthetic
     118history on top:
     119
     120{{{
     121psql "postgresql://$DBUSER:$DBPASSWORD@$DBHOST:$DBPORT/$DBNAME" \
     122  -f server/db/reports_demo_data.sql
     123}}}
     124
     125This script is not part of `-init` or `-load-data` on purpose. The header of
     126`reports_demo_data.sql`, shown below, explains why. Running it never changes the balances that
     127the tests below check.
     128
     129{{{
     130-- reports_demo_data.sql
     131-- EduBerza - optional historical data for the P6 reports
     132-- Course: Databases 2025/2026 Winter, FINKI UKIM
     133--
     134-- data_load.sql only seeds ~10 minutes of trade history, which is enough to
     135-- demonstrate UC0001-UC0007 but not enough to show report_top_traders() or
     136-- report_market_performance() doing anything interesting: everything falls
     137-- into a single quarter, so "number of profitable periods" and "consistency"
     138-- are trivial and "market return" has almost no history to work with.
     139--
     140-- This script adds five quarters of synthetic transactions, market trades and
     141-- executed orders on top of an already-loaded data_load.sql, spanning
     142-- 2025-07 to 2026-07, so the two P6 reports have several periods and two
     143-- markets with opposite price trends to actually compare.
     144--
     145-- Deliberately NOT part of -init / -load-data: it only inserts into
     146-- transactions, market_trades and orders, and does not touch
     147-- users.available_balance/invested_balance or holdings, so it does not
     148-- disturb the balances the other use cases' documented "verified run"
     149-- sections depend on. Run it by hand, after data_load.sql, only to exercise
     150-- the two reports:
     151--
     152--   psql "$DATABASE_URL" -f server/db/schema_creation.sql
     153--   psql "$DATABASE_URL" -f server/db/data_load.sql
     154--   psql "$DATABASE_URL" -f server/db/reports_demo_data.sql
     155--
     156-- Idempotent: deletes its own previously-inserted rows (tagged via
     157-- description/source) before re-inserting.
     158}}}
     159
     160== Testing instructions ==
     161
     162=== How to launch and log in ===
     163
     164Start the prototype with `./eduberza` after steps 1–4. The sample data creates three test
     165users. All of them have the password '''`test123`''':
     166
     167||= Username =||= Starting state after `-init` =||
     168|| `alice` || 8250.00 USD available (1750.00 invested), holds 0.5 ETH bought at 3500.00. Watchlist "Favorites": BTC, ETH, SOL. Best demo account. ||
     169|| `bob` || 5000.00 USD available, no crypto. Watchlist "Bobs Picks": BTC, DOGE. ||
     170|| `charlie` || 2500.00 USD available, no crypto, no watchlist yet. ||
     171
     172The five sample markets are ADA, BTC, DOGE, ETH and SOL, all quoted in USD. Their starting
     173last prices are 0.45375, 67140, 0.122, 3520 and 166.1.
     174
     175=== Mini-guide to the application ===
     176
     177You always answer with the number of a menu option. When you have to choose a market, a
     178holding or a crypto, the prototype prints a numbered list and you type the number from that
     179list. You never type an id or a symbol. A number that is not in the list is refused with
     180`Invalid choice, enter a number from 1 to N.`
     181
     182'''Menu before login'''
     183
     184||= Option =||= What it does and how to use it =||
     185|| `[1] Register` || Enter a username, an e-mail (must contain `@`), your full name and a password (at least 6 characters). You get `Account created. You can now log in.`, or `Invalid email.`, `Password must be at least 6 characters.` or `Username or email already taken.` A new account starts with 0 USD. ||
     186|| `[2] Login` || Enter your username and password. You get `Login successful.` and the second menu. A wrong password and an unknown username both give `Invalid credentials.` ||
     187|| `[3] Browse markets` || Prints the numbered list of markets with their last price. ||
     188|| `[0] Exit` || Ends the program. ||
     189
     190'''Menu after login''' (headed `--- Logged in as <username> ---`)
     191
     192||= Option =||= What it does and how to use it =||
     193|| `[1] View balance` || Shows the available, invested and total USD. ||
     194|| `[2] Deposit virtual funds` || Enter an amount in USD. It must be a positive number, otherwise you get `Invalid amount.` You get `Deposited 500.0000 USD.` ||
     195|| `[3] Browse markets` || Same list as before login. ||
     196|| `[4] Place market BUY order` || Lists all markets, numbered, with their last price. Type the number at `Market #:`. The prototype shows the latest price. Type the quantity. You get `Order executed: buy …` or `Insufficient funds: need …, have …`. ||
     197|| `[5] Place market SELL order` || Lists only the cryptos you hold, numbered, with columns `Held` and `Free to sell`. Type the number at `Holding #:`, then the quantity. You get `Order executed: sell …` or `Insufficient holding: …`. If you hold nothing, you get `you hold no crypto that is free to sell`. ||
     198|| `[6] View portfolio` || One row per crypto you hold: quantity, reserved, available, average buy price, current price, value and unrealised P/L. Then your cash, portfolio value and net worth. ||
     199|| `[7] View transaction history` || Your last 20 ledger entries (deposits, buys, sells), newest first. ||
     200|| `[8] Manage watchlist` || Opens a submenu: `[1] List items` shows your watchlist with last prices. `[2] Add crypto` lists, numbered, the cryptos not on it yet; type a number. `[3] Remove crypto` lists, numbered, the cryptos on it; type a number. `[0] Back` returns. A user without a watchlist gets one named "Favorites" the first time. ||
     201|| `[9] Logout` || Back to the first menu. ||
     202|| `[10] Report: top traders` || P6 report. Enter a start date (inclusive) and an end date (exclusive) as `YYYY-MM-DD`. ||
     203|| `[11] Report: market performance` || P6 report, with the same two dates. ||
     204|| `[0] Exit` || Ends the program. ||
     205
     206=== End-to-end smoke test ===
     207
     208These values were checked on 2026-09-24 against freshly loaded sample data (PostgreSQL 16.3),
     209with the bot not running. The expected values are exact.
     210
     211 1. `./eduberza -init` prints `Schema initialised. Re-run without -init to start the CLI.`
     212 2. `./eduberza`, then `2` (Login), then `alice` / `test123` gives `Login successful.`
     213 3. `6` (View portfolio) shows one row: `ETH`, quantity 0.5000, reserved 0.0000, available
     214    0.5000, average buy 3500.000000, current 3520.000000, value 1760.0000, unrealised P/L
     215    `+10.0000`. Cash available is 8250.0000 and net worth is 10010.0000.
     216 4. `4` (BUY). The market list shows `1 ADA`, `2 BTC`, `3 DOGE`, `4 ETH`, `5 SOL`. Type `2` at
     217    `Market #:`, then `0.01` at `Quantity:`. The result is
     218    `Order executed: buy 0.0100 BTC @ 67140.000000 (notional 671.4000 USD)`.
     219 5. `6` (View portfolio) now shows BTC ''and'' ETH, with total value 2431.4000, cash 7578.6000
     220    (= 8250.00 − 671.40), and net worth still 10010.0000.
     221 6. `5` (SELL). The holdings list shows `1 BTC` (held 0.0100) and `2 ETH` (held 0.5000). Type
     222    `2` at `Holding #:`, then `0.5`. The result is
     223    `Order executed: sell 0.5000 ETH @ 3520.000000 (notional 1760.0000 USD)`.
     224 7. `7` (View transaction history) lists, newest first: the `sell` (+1760.0000), the `buy` of
     225    BTC (−671.4000), then the two rows from the sample data, which have the same timestamp:
     226    `deposit` 10000.0000 "Initial virtual deposit" and `buy` −1750.0000 "Market buy 0.5 ETH @
     227    3500.00".
     228 8. `8` (Manage watchlist), then `1` (List items), shows alice's watchlist with BTC, ETH and SOL
     229    and their last prices. Then `2` (Add crypto) lists `1 ADA` and `2 DOGE`; type `1` and you get
     230    `Added ADA.` Then `0` (Back).
     231 9. `9` (Logout), then `0` (Exit).
     232
     233=== Testing the failure paths ===
     234
     235These matter more than the happy path, because they prove that the transactions really roll
     236back and that invalid choices are refused:
     237
     238 * '''Insufficient funds:''' log in as `charlie` (2500 USD). Choose `4`, market `2` (BTC),
     239   quantity `1`. Expect `Insufficient funds: need 67140.0000, have 2500.0000` and ''no'' change to
     240   any table: no order row, no ledger entry, no holding.
     241 * '''Insufficient holding:''' on fresh data (`./eduberza -load-data`), log in as `alice`. Choose
     242   `5`; the list shows only `1 ETH` (held 0.5000, free 0.5000). Choose `1`, quantity `5`. Expect
     243   `Insufficient holding: trying to sell 5.0000, available 0.5000 (of 0.5000 held, 0.0000 reserved)`.
     244 * '''Nothing to sell:''' as `bob` (no crypto), choose `5`. The holdings list is empty, and you
     245   get `you hold no crypto that is free to sell` without being asked for a number.
     246 * '''Invalid choice from a list:''' in any list (for example `8`, then `3` Remove crypto), type a
     247   number larger than the list. Expect `Invalid choice, enter a number from 1 to N.`
     248 * '''Invalid deposit:''' choose `2` and enter `-50`. Expect `Invalid amount.`
     249 * '''Duplicate registration:''' register with username `alice`. Expect
     250   `Username or email already taken.`
     251 * '''Invalid e-mail:''' register with an e-mail without `@`. Expect `Invalid email.`
     252 * '''Wrong password:''' log in as `alice` with any wrong password. Expect
     253   `Invalid credentials.` An unknown username gives the same message, so the prototype does not
     254   reveal which accounts exist.
     255
     256The concurrency guarantee of the sell path (two processes selling the same crypto at the same
     257moment) cannot be reproduced by typing into two terminals, because each order commits within
     258milliseconds. It is described in [wiki:UseCase0005Implementation].
     259
     260=== For the public presentation ===
     261
     262Demo with `alice`. She already has a position, so the portfolio screen is not empty. Register a
     263brand-new account live to show UC0001. Run the bot in a background terminal so the prices
     264visibly move between two portfolio refreshes.
     265
     266== Editing the ER diagram ==
     267
     268TerraER is a third-party tool and is '''not''' committed to this repository on purpose. Download
     269the teacher's build from `https://bazi.finki.ukim.mk/resources/Software/` and run it:
     270
     271{{{
     272java -jar TerraER3.11.jar     # then File → Open → docs/P1-ConceptualModel/ERModel_v03.xml
     273}}}
     274
     275The current version is `ERModel_v03.xml`. Save new versions as `ERModel_v04.xml` and so on,
     276and export a matching PNG for each. TerraER does not add the extension itself: type `.xml`
     277yourself, or the file will not reopen.
     278
     279== Up-to-date source code ==
     280
     281The repository is pushed to the FINKI DEVELOP git server. The clone URL and credentials are in
     282the Repositories section in EPRMS.
     283
     284=== About the source code ===
     285
     286 * All the source needed to run the prototype is in this repository: the CLI (`server/`), the
     287   market bot (`bots/`), the DDL script and the sample-data script (`server/db/`).
     288 * Third-party Go libraries are '''not''' vendored. `go build` downloads `github.com/lib/pq` at
     289   the versions pinned in `go.mod` and `go.sum`.
     290 * Third-party executables are '''not''' committed. `.gitignore` excludes `*.jar`, and TerraER is
     291   downloaded from the URL above.
     292 * No third-party images, styles or frameworks are used. The prototype has no images at all;
     293   the interface is text.