Ignore:
Timestamp:
09/24/26 17:43:19 (6 days ago)
Author:
Stefan <trsunovstefan@…>
Branches:
main
Children:
0cee8ec
Parents:
a531b45
Message:

Wiki docs, phase 6 and phase 7 added

File:
1 edited

Legend:

Unmodified
Added
Removed
  • docs/P4-Prototype/UseCase0007Implementation.md

    ra531b45 ref1c1c7  
    1 # Use-case 0007 Implementation — Watchlist
     1# Use-case 0007 Implementation - Manage watchlist
    22
    3 **Initiating actor:** Trader. **Source file:** `server/watchlist.go`.
     3**Initiating actor:** Trader
    44
    5 ## Scenario (implemented)
     5**Other actors:** —
    66
    7 1. **User** chooses `[8] Manage watchlist`.
    8 2. **System** ensures a default watchlist exists:
     7A logged-in Trader keeps a list of crypto assets they want to monitor, with the last
     8price of each. The first time the watchlist is opened the system creates a default
     9watchlist named "Favorites" for the Trader. From a sub-menu the Trader can list the
     10watchlist, add a crypto or remove one. The Trader never types a symbol: for adding, the
     11system lists, numbered, only the cryptos that are not on the watchlist yet, and for
     12removing, only the cryptos that are on it; the Trader picks one by its number. Adding
     13a crypto that is already on the list is a no-op (idempotent), and a number that is not
     14in the list is refused without touching the database.
     15
     16Original use-case description (P3): [UseCase0007](../P3-UseCaseModel/UseCase0007.md).
     17Implementation: [`server/watchlist.go`](../../server/watchlist.go), functions
     18`ManageWatchlist`, `ensureDefaultWatchlist`, `listWatchlist`, `addToWatchlist` and
     19`removeFromWatchlist`, with `pickNumber` from
     20[`server/market.go`](../../server/market.go).
     21
     22All statements run on the `project` schema (the connection sets
     23`search_path=project,public` in `server/db/db.go`). The SQL below is copied from the
     24Go code; only the Go source indentation is removed.
     25
     26The run shown is user `alice` on the seed data, whose watchlist contains BTC, ETH and
     27SOL. She lists it, adds ADA, tries to remove a number that is not in the list, removes
     28SOL and lists the result.
     29
     30## Scenario
     31
     321. **Trader** chooses `[8] Manage watchlist` in the authenticated menu (types `8`).
     332. **System** makes sure the Trader has a watchlist and takes the id of the oldest one
     34   (`ensureDefaultWatchlist`; `$1` = the logged-in user's id):
    935
    1036   ```sql
    11    SELECT id FROM watchlists WHERE user_id = $1 ORDER BY created_at LIMIT 1;
    12    -- else
    13    INSERT INTO watchlists (user_id, name) VALUES ($1, 'Favorites') RETURNING id;
     37   SELECT id FROM watchlists WHERE user_id = $1 ORDER BY created_at LIMIT 1
    1438   ```
    1539
    16 3. **System** offers the submenu: List, Add, Remove, Back.
     40   Only if this returns no row, it creates the default watchlist and uses its id:
    1741
    18 ### List
     42   ```sql
     43   INSERT INTO watchlists (user_id, name) VALUES ($1, 'Favorites') RETURNING id
     44   ```
    1945
    20 ```sql
    21 SELECT c.symbol, c.name, COALESCE(lp.price, 0)
    22   FROM watchlist_items wi
    23   JOIN crypto c ON c.id = wi.crypto_id
    24   LEFT JOIN markets m ON m.crypto_id = c.id AND m.quote_currency = 'USD'
    25   LEFT JOIN v_latest_prices lp ON lp.market_id = m.id
    26  WHERE wi.watchlist_id = $1
    27  ORDER BY c.symbol;
    28 ```
     46   (alice already has the seed watchlist "Favorites", so only the `SELECT` runs.) The
     47   watchlist id is kept in Go and used as `$1` in all statements below.
     483. **System** shows the sub-menu `-- Watchlist --` with `[1] List items`,
     49   `[2] Add crypto`, `[3] Remove crypto` and `[0] Back`.
    2950
    30 ![Adding DOGE to the watchlist, then listing it](screenshots/uc0007_watchlist.png)
     51   ![UC0007 steps 1-3: Trader opens the watchlist, system shows the sub-menu](screenshots/uc0007_1_3_menu.png)
    3152
    32 ### Add
     53### List items
    3354
    34 ```sql
    35 SELECT id FROM crypto WHERE upper(symbol) = upper($1);
     554. **Trader** chooses `[1] List items`.
     565. **System** lists the cryptos on the watchlist with their last price against USD
     57   (`listWatchlist`; `$1` = watchlist id):
    3658
    37 INSERT INTO watchlist_items (watchlist_id, crypto_id)
    38 VALUES ($watchlist_id, $crypto_id)
    39 ON CONFLICT (watchlist_id, crypto_id) DO NOTHING;
    40 ```
     59   ```sql
     60   SELECT c.symbol, c.name, COALESCE(lp.price, 0)
     61     FROM watchlist_items wi
     62     JOIN crypto  c  ON c.id = wi.crypto_id
     63     LEFT JOIN markets       m  ON m.crypto_id = c.id AND m.quote_currency = 'USD'
     64     LEFT JOIN v_latest_prices lp ON lp.market_id = m.id
     65    WHERE wi.watchlist_id = $1
     66    ORDER BY c.symbol
     67   ```
    4168
    42 Re-adding the same symbol is a no-op thanks to the unique constraint + `ON CONFLICT`.
     69   For alice it prints `BTC Bitcoin 67140.000000`, `ETH Ethereum 3520.000000` and
     70   `SOL Solana 166.100000` (an empty watchlist prints `(watchlist is empty)`), then
     71   shows the sub-menu again.
    4372
    44 ### Remove
     73   ![UC0007 List items](screenshots/uc0007_list.png)
    4574
    46 ```sql
    47 DELETE FROM watchlist_items
    48  WHERE watchlist_id = $1
    49    AND crypto_id = (SELECT id FROM crypto WHERE upper(symbol) = upper($2));
    50 ```
     75### Add a crypto
     76
     776. **Trader** chooses `[2] Add crypto`.
     787. **System** lists, numbered, the cryptos that are not on the watchlist yet
     79   (`addToWatchlist`; `$1` = watchlist id):
     80
     81   ```sql
     82   SELECT c.id, c.symbol, c.name
     83     FROM crypto c
     84    WHERE NOT EXISTS (SELECT 1 FROM watchlist_items wi
     85                       WHERE wi.watchlist_id = $1 AND wi.crypto_id = c.id)
     86    ORDER BY c.symbol
     87   ```
     88
     89   For alice it prints `1 ADA Cardano` and `2 DOGE Dogecoin` and asks
     90   `Crypto # to add:`. Go keeps each row's crypto id in memory. (If every crypto is
     91   already on the watchlist, it prints `Every crypto is already on your watchlist.`
     92   instead.)
     93
     94   ![UC0007 Add: system lists the cryptos not yet on the watchlist](screenshots/uc0007_add_1_list.png)
     95
     968. **Trader** picks the crypto by its number in the list: `1` (ADA).
     979. **System** adds the crypto of row 1 to the watchlist (`$1` = watchlist id,
     98   `$2` = the chosen crypto's id); thanks to the unique constraint and
     99   `ON CONFLICT ... DO NOTHING`, adding a crypto that is already there changes
     100   nothing:
     101
     102   ```sql
     103   INSERT INTO watchlist_items (watchlist_id, crypto_id)
     104    VALUES ($1, $2)
     105    ON CONFLICT (watchlist_id, crypto_id) DO NOTHING
     106   ```
     107
     108   It prints `Added ADA.` and shows the sub-menu again.
     109
     110   ![UC0007 Add: Trader picks #1 (ADA), system adds it](screenshots/uc0007_add_2_added.png)
     111
     112### Remove a crypto
     113
     11410. **Trader** chooses `[3] Remove crypto`.
     11511. **System** lists, numbered, the cryptos that are on the watchlist
     116    (`removeFromWatchlist`; `$1` = watchlist id):
     117
     118    ```sql
     119    SELECT c.id, c.symbol, c.name
     120      FROM watchlist_items wi
     121      JOIN crypto c ON c.id = wi.crypto_id
     122     WHERE wi.watchlist_id = $1
     123     ORDER BY c.symbol
     124    ```
     125
     126    For alice it now prints `1 ADA Cardano`, `2 BTC Bitcoin`, `3 ETH Ethereum` and
     127    `4 SOL Solana` and asks `Crypto # to remove:`. Go keeps each row's crypto id in
     128    memory. (If the watchlist is empty, it prints `Your watchlist is empty.` instead.)
     129
     130    ![UC0007 Remove: system lists the watchlist's cryptos](screenshots/uc0007_remove_1_list.png)
     131
     13212. **Trader** picks the crypto by its number in the list: `4` (SOL).
     13313. **System** removes the crypto of row 4 from the watchlist (`$1` = watchlist id,
     134    `$2` = the chosen crypto's id):
     135
     136    ```sql
     137    DELETE FROM watchlist_items WHERE watchlist_id = $1 AND crypto_id = $2
     138    ```
     139
     140    It prints `Removed SOL.` and shows the sub-menu again.
     141
     142    ![UC0007 Remove: Trader picks #4 (SOL), system removes it](screenshots/uc0007_remove_2_removed.png)
     143
     144#### Alternate flow 12a — number not in the list
     145
     146Before removing SOL, alice first chose `[3] Remove crypto` and, at step 12, entered `5`
     147while only numbers 1–4 were listed. `pickNumber` prints
     148`Invalid choice, enter a number from 1 to 4.`, the `DELETE` is not run and the sub-menu
     149is shown again; she then chose `[3]` once more, which returned the scenario to step 11.
     150The same check applies to the number entered at step 8.
     151
     152![UC0007 Remove: a number that is not in the list is refused](screenshots/uc0007_remove_invalid.png)
     153
     154### Verification — list after the changes
     155
     156Choosing `[1] List items` again runs the query from step 5, which now returns
     157`ADA Cardano 0.453750`, `BTC Bitcoin 67140.000000` and `ETH Ethereum 3520.000000`:
     158ADA was added and SOL removed. `[0] Back` returns to the authenticated menu.
     159
     160![UC0007 List items after the changes](screenshots/uc0007_list_after.png)
Note: See TracChangeset for help on using the changeset viewer.