From f059ee959183d7089fc252a6e8e8d6ef07316b0e Mon Sep 17 00:00:00 2001 From: Jack Turner Date: Tue, 17 Mar 2026 02:28:54 +0000 Subject: [PATCH 01/26] Sprint 2 Commit 1 --- .DS_Store | Bin 8196 -> 8196 bytes Meeting-Documentation/Meeting-2.md | 2 +- stockscope/.DS_Store | Bin 6148 -> 8196 bytes stockscope/pom.xml | 4 + stockscope/src/.DS_Store | Bin 6148 -> 6148 bytes stockscope/src/main/.DS_Store | Bin 6148 -> 6148 bytes stockscope/src/main/java/.DS_Store | Bin 6148 -> 6148 bytes stockscope/src/main/java/com/.DS_Store | Bin 6148 -> 6148 bytes .../StockComparisonApplication.java | 28 ++++ .../application/ComparisonController.java | 145 ++++++++++++++++++ .../application/MarketDataService.java | 16 ++ .../application/PriceRepository.java | 18 +++ .../InMemoryPriceRepository.java | 39 +++++ .../infrastructure/YahooFinanceService.java | 99 ++++++++++++ .../presentation/WebPageController.java | 131 ++++++++++++---- .../src/main/resources/templates/index.html | 50 +++--- 16 files changed, 467 insertions(+), 65 deletions(-) create mode 100644 stockscope/src/main/java/com/sharecomparison/StockComparisonApplication.java create mode 100644 stockscope/src/main/java/com/sharecomparison/application/ComparisonController.java create mode 100644 stockscope/src/main/java/com/sharecomparison/application/MarketDataService.java create mode 100644 stockscope/src/main/java/com/sharecomparison/application/PriceRepository.java create mode 100644 stockscope/src/main/java/com/sharecomparison/infrastructure/InMemoryPriceRepository.java create mode 100644 stockscope/src/main/java/com/sharecomparison/infrastructure/YahooFinanceService.java diff --git a/.DS_Store b/.DS_Store index be5f3458a8ee5ed9c581776992eff8142d24768b..fbf407f23de180ed66676047c5776431b1ef10b9 100644 GIT binary patch delta 226 zcmZp1XmOa}&&aniU^hP_-(((v)7qj8z6_~ASi+FWkjIeDpv&OGkk63JP|A=C6wL#Q zCxYej8S*9z2+B{66*QW>Uqp2B0ulSk#|2A8jLZyl6pT#_YjqT=EscO|6JxW<0Yb); z4~dFS?h-O*l9ru(Sm?QQya0c3Mt-?xa(-SwQEFLcYI#H^kR6;^l{)#Tusav0vO@p^ z7)%xuQI=F=)MvC{bY%2m^kEET3}Z}ZEMP2UEMqL693-L)GJErS(FmrE4d#rS*(JWQ OY&H?Q%($_^gb@H~5Irsc delta 426 zcmZp1XmOa}&nUbxU^hRb@MIo=)0`{}xeR3tc?{)~GX&-RIbb{nJ%(f;JH<06KRGEU zKZ${XL4bjQi3v#S{Raaei-Ccip@1PDs7{Ze0!ZedsV(aXIf4x$hK7bZ3TCFYIttZ>1{Rhew#j4zA!Bx!q6#5%CK>6;8-<=rM`R}F=LKh0 zrN#^J7iZ*`djh!uMX6<(spXS53%heEI|P7T%D_39MMPOrmQkP4g3*!Di_wQMlrfAk zow0zil(CGlezJpzvM|*Avf!e;ocz3Wphm{Y0fOO-+b6FOEL0P(t~NF`fw|1W$O7!J t+FDKyQDuGWp!n>Z+`RnG$qqvDj9r@(L<*QTvrBwqnS4|*gjB7$OaRd&Z~FiM diff --git a/Meeting-Documentation/Meeting-2.md b/Meeting-Documentation/Meeting-2.md index 637bca3..971cf45 100644 --- a/Meeting-Documentation/Meeting-2.md +++ b/Meeting-Documentation/Meeting-2.md @@ -34,4 +34,4 @@ - Review Sprint 1 for code review + fix any issues (**Luke + Sameer**) - Support team with repository processes (**Jack + Azlan**) - Final check of architecture diagram to ensure it matches code structure (**Azlan**) -- Update Kanban with Sprint 2 tasks from feedback (**Luke + Jack**) +- Update Kanban with Sprint 2 tasks from feedback (**Jack**) diff --git a/stockscope/.DS_Store b/stockscope/.DS_Store index b8c287f99d4bba8fa33cc5725098bd103e5e5a91..36781bc263b07eb432fb7e108da0969e1a76c3db 100644 GIT binary patch delta 409 zcmZoMXmOBWU|?W$DortDU;r^WfEYvza8E20o2aKK$^()I@)T4n*}+< zST-wg^fE7I=ir#wz)}xV#=?-xP{xqQP>!q=DDDKrdjG)y$O72`RmY&mkPKv}psC-4 zrkf;Hc0n?pnW31Wh#?tG*(5YWp{kO~iwmH>+A&#)rBGd> zy4uLhKu5vY#IROJq1w_2$Tl%HtF7ha5LMQ<4vNpt$<52}n!KAup0RuLZI)^#bvA|) zhD4y1=?tk1C8*XiF(Itotikh)nM;BjDB=nVU|=AAXP(S2;yKx$hl2wc#-Olg*c{I@ GhZz9PJ!a1U delta 196 zcmZp1XfcprU|?W$DortDU=RQ@Ie-{Mvv5r;6q~50D9R3!2Z>M0|%s^v-K!6)axPlzAvG6kh2(= PAWj6inqhN1&m3j|u{S8S diff --git a/stockscope/pom.xml b/stockscope/pom.xml index 86244eb..90b87f1 100644 --- a/stockscope/pom.xml +++ b/stockscope/pom.xml @@ -50,6 +50,10 @@ org.springframework.boot spring-boot-maven-plugin + 4.0.2 + + com.sharecomparison.StockComparisonApplication + diff --git a/stockscope/src/.DS_Store b/stockscope/src/.DS_Store index eb37a49a5c443871fd938aefd86a9e35b3a0d000..934af43d55951043d88fabcb77157bfbaa09f161 100644 GIT binary patch delta 119 zcmZoMXfc@J&&atkU^g=(=VTrhd&V7;eOL? zP7YCJee0n3?3~=Z{I1DQS>zeJH?y*~Gbyt$lrW?+6f=}~=Hw?Q<>V(ZFfa%Ju@eyM Q{Re}Ig^`=tIsWnk0JAb9sQ>@~ delta 110 zcmZoMXfc@J&&aVcU^g=($7CKBd&ZrUeOL-TB&(~9O$~Jv49!eybrh-%4J<5m6wFLb uYHK+;M3wcegW|Jua`W=L8Nh&%5kfQYLTMP)vzdjpooQl8^=5XCzx)86Iu{xM diff --git a/stockscope/src/main/.DS_Store b/stockscope/src/main/.DS_Store index f05c353bc789d77a6ab486ccd09c0d0663a00013..1682cf970ef421206ecf92b79db87b3c50dc9ecf 100644 GIT binary patch literal 6148 zcmeHK%}*0S6n_I1wji=VL6m43n-~u$pCNu6ECpjwlM;$p1gzWbP*%1(o84^-B1zAB z^bhcA;@x=krvHGROuTzC@oJ)PK3cxS6ETMDOJ;s==6y`xZ#z3X06s)TD5jWKiWK+$FSf#nofHO0zK!56U^Y0#otG^h7(ZH-P055~vFN45v!Th~TW zA0M08-i~U$iJ=>Fxplkbu;-F?g6}0E9SZC40+mKRmRV8tkT3OmzUp1|ilS}pCp%7c z>RsJEvF_gPzP?!B>Hafk2L{gN^q#z7t`|N2Tc?=Kyp&z0OGVErWL(3voMJYh8$rf; zN^^SmKG!)~c(gM$zv-e+Y-d8GyX{fxhQ=jI+vc@d^^2cDc!4pq`JI)Az+>7Y)xdoFYwauGmJN+G!`nf`FB{C(mXw zXV#-UQ`SPAmzB8rT~eAz-mNQ_q@qJYDtLUv>Y755cyHE(KJPGxMx%-3tTJhE8Kxi& zSt!9XcnPoI4ZMYq@Cm-bPtrnSu>F?MES%zW(~d1P|C&S@4n(YsdDO~8-B5^F z9p~f19Y|7Q*oO>+4D4l~Sq^o6{@=nqe8~NOZ%{Z283-9TRt%8HbY?n*C8@LZKym!6 wjbnR^jSKgyNfaU2spD8E{3!0nCIoG9t{^5A)+FK#O86llXb8KIfq%-tZ-R;e1poj5 delta 78 zcmZoMXfc=|#>B`mu~2NHo+2ab#DLw5tdn_IOeV9i=Wmwb;AYv_;LEg`or9kPsA{ty b$9Lw*{34bD3_!rhz`z8hSvE(AtYHQK$=(lx diff --git a/stockscope/src/main/java/.DS_Store b/stockscope/src/main/java/.DS_Store index c736446f6d9ac01c433ab3166dbd014818382d11..87968d8e80e265cd9e453f7fa6434815044d6586 100644 GIT binary patch literal 6148 zcmeHK%}>-o6rX_=x*$@P1yQ2eWE0~7ykEDS9fS~8YL!uhWQ33e zjbfq$x<3fD({?~w#0)iJnu8d+O>`L1bg{$?#0(sF2I#xniVQ@MGi&d8e-*G?{>g+D zA-wcP;EA{Vt?t=Y5RQy|#x1Rh6K(BMyVNPIsB5I5hFVw)6wP0vqaq=;9_gCCV401w zoSq?Ws9CP*vjD4Ng3Iz<%dL=xLTYX$V6iGUNJ%MKmIt=B#wJIGGUF4Y+e4YHD`TKf zj8ATFC#AmZ@YVV9y6w2+2`3%HH;GA?zzCj#BC4^Bx~K+!iK_W3_eAw1J33Exo$8i* zQoZR^U#h=9-G6%E%-O-gb7i?#*NpY5Pj@;)Y~m|+&0MPbRz>wR!*Z*o(A)@B>!Dee zQ~O-!&C30qsp6&wKIxqamhOgcnl8&%SX~Xwu(V5Gyr*pxe`oIw44>Hc90{z@BJSdH z0F=*$rZelV5?H(rbQe5o3KmR104&S9O+0Ch_mmECew>9am{2ho0}6R30*=nsDMhy zL66XL^b);BZ_o$y34KF9a2rnJ3wRh$;c0vm-^O?F0hzq z;)&bUQPm)hx64CV9FotAU$*q|@v|3Guij7Z#5tt$7pu2+Sk}7gyEbvFkSW^TuBDGu z75kAok8~ID;xPj;1ILj8`hCbiqnH!e17_$1U`}9@Bg}wc z9ty}qvAAMjad*Hx9PBya-{i={5oRkx9xkZ?qddG2KF-0%7-#N z|F0i@|KA%Fk75R52L3Asa3Zhf6(~uZtp|#uXKe=B187vJUy~yXfleI9Orb~d05m3G a%TNU{C$Py8WZiy8Q*4EzFNj_`&6 delta 77 zcmZoMXfc=|#>CJ*u~2NHo+3XB0|Nsi1A_pAVQ_MOZo$MtT}GD4pV*2w%W!bBY;5?; hw3(fQp983VvmnQJ=E?jbhKvl8ZFrH+`AG~63<5yx1jKs(!GM7Qq+qfh3peAA z$yO|d?h@72MrH;&3dSaewK@vbmPSCfiLqI2EhmSlvc7dte0EN5UVazQ1R!8!gwPDU VP#Q*cgG}8l%DRARXSFjXnNLE)Hn;PmU7@C>Z>L^qj8dzBB sD43a=)Yfuxh$`z_2gPUS data1 = marketDataService.fetchSharePrices(symbol1, start, end); + List data2 = marketDataService.fetchSharePrices(symbol2, start, end); + + if (data1.isEmpty() || data2.isEmpty()) { + model.addAttribute("error", "No data found for one or both symbols. Try different dates or symbols."); + model.addAttribute("symbol1", symbol1); + model.addAttribute("symbol2", symbol2); + model.addAttribute("startDate", start.toString()); + model.addAttribute("endDate", end.toString()); + return "index"; + } + + // Sort both lists by date + data1.sort(Comparator.comparing(PriceData::getDate)); + data2.sort(Comparator.comparing(PriceData::getDate)); + + // ==================== CHART CALCULATION ==================== + double chartWidth = 800; + double chartHeight = 300; + double left = 80; + double right = left + chartWidth; + double bottom = 320; + double top = bottom - chartHeight; + double midY = top + chartHeight / 2; + + // Global min/max price + double minPrice = Double.MAX_VALUE; + double maxPrice = Double.MIN_VALUE; + for (PriceData p : data1) { minPrice = Math.min(minPrice, p.getClosingPrice()); maxPrice = Math.max(maxPrice, p.getClosingPrice()); } + for (PriceData p : data2) { minPrice = Math.min(minPrice, p.getClosingPrice()); maxPrice = Math.max(maxPrice, p.getClosingPrice()); } + if (maxPrice == minPrice) maxPrice += 1; + double priceRange = maxPrice - minPrice; + + // Use the shorter list length for chart alignment + int n = Math.min(data1.size(), data2.size()); + double xStep = (n > 1) ? chartWidth / (n - 1) : 0; + + StringBuilder path1 = new StringBuilder(); + StringBuilder path2 = new StringBuilder(); + List> points1 = new ArrayList<>(); + List> points2 = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + PriceData p1 = data1.get(i); + double x = left + i * xStep; + double y1 = bottom - ((p1.getClosingPrice() - minPrice) / priceRange) * chartHeight; + + if (i == 0) path1.append("M ").append(x).append(",").append(y1); + else path1.append(" L ").append(x).append(",").append(y1); + + points1.add(Map.of("x", x, "y", y1, "value", p1.getClosingPrice())); + + // Symbol 2 + PriceData p2 = data2.get(i); + double y2 = bottom - ((p2.getClosingPrice() - minPrice) / priceRange) * chartHeight; + + if (i == 0) path2.append("M ").append(x).append(",").append(y2); + else path2.append(" L ").append(x).append(",").append(y2); + + points2.add(Map.of("x", x, "y", y2, "value", p2.getClosingPrice())); + } + + Map chartData = new HashMap<>(); + chartData.put("left", left); + chartData.put("right", right); + chartData.put("top", top); + chartData.put("bottom", bottom); + chartData.put("midY", midY); + chartData.put("symbol1Path", path1.toString()); + chartData.put("symbol2Path", path2.toString()); + chartData.put("symbol1Points", points1); + chartData.put("symbol2Points", points2); + + // Result object your HTML expects + Map result = new HashMap<>(); + result.put("symbol1", symbol1); + result.put("symbol2", symbol2); + result.put("startDate", start); + result.put("endDate", end); + result.put("chartData", chartData); + result.put("symbol1Data", data1); + result.put("symbol2Data", data2); + + model.addAttribute("result", result); + model.addAttribute("symbol1", symbol1); + model.addAttribute("symbol2", symbol2); + model.addAttribute("startDate", start.toString()); + model.addAttribute("endDate", end.toString()); + + return "index"; + } +}*/ \ No newline at end of file diff --git a/stockscope/src/main/java/com/sharecomparison/application/MarketDataService.java b/stockscope/src/main/java/com/sharecomparison/application/MarketDataService.java new file mode 100644 index 0000000..2e21a9b --- /dev/null +++ b/stockscope/src/main/java/com/sharecomparison/application/MarketDataService.java @@ -0,0 +1,16 @@ +package com.sharecomparison.application; + +import java.time.LocalDate; +import java.util.List; + +import com.sharecomparison.domain.PriceData; + +public interface MarketDataService { + + List fetchSharePrices( + String symbol, + LocalDate startDate, + LocalDate endDate + ); + +} \ No newline at end of file diff --git a/stockscope/src/main/java/com/sharecomparison/application/PriceRepository.java b/stockscope/src/main/java/com/sharecomparison/application/PriceRepository.java new file mode 100644 index 0000000..9eaf87d --- /dev/null +++ b/stockscope/src/main/java/com/sharecomparison/application/PriceRepository.java @@ -0,0 +1,18 @@ +package com.sharecomparison.application; + +import java.time.LocalDate; +import java.util.List; + +import com.sharecomparison.domain.PriceData; + +public interface PriceRepository { + + void savePrices(List prices); + + List loadPrices( + String symbol, + LocalDate startDate, + LocalDate endDate + ); + +} \ No newline at end of file diff --git a/stockscope/src/main/java/com/sharecomparison/infrastructure/InMemoryPriceRepository.java b/stockscope/src/main/java/com/sharecomparison/infrastructure/InMemoryPriceRepository.java new file mode 100644 index 0000000..15fae83 --- /dev/null +++ b/stockscope/src/main/java/com/sharecomparison/infrastructure/InMemoryPriceRepository.java @@ -0,0 +1,39 @@ +package com.sharecomparison.infrastructure; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +import com.sharecomparison.application.PriceRepository; +import com.sharecomparison.domain.PriceData; + +public class InMemoryPriceRepository implements PriceRepository { + + private List storedPrices = new ArrayList<>(); + + @Override + public void savePrices(List prices) { + storedPrices.addAll(prices); + } + + @Override + public List loadPrices( + String symbol, + LocalDate startDate, + LocalDate endDate) { + + List results = new ArrayList<>(); + + for (PriceData p : storedPrices) { + + if (p.getSymbol().equals(symbol) + && !p.getDate().isBefore(startDate) + && !p.getDate().isAfter(endDate)) { + + results.add(p); + } + } + + return results; + } +} \ No newline at end of file diff --git a/stockscope/src/main/java/com/sharecomparison/infrastructure/YahooFinanceService.java b/stockscope/src/main/java/com/sharecomparison/infrastructure/YahooFinanceService.java new file mode 100644 index 0000000..35d4868 --- /dev/null +++ b/stockscope/src/main/java/com/sharecomparison/infrastructure/YahooFinanceService.java @@ -0,0 +1,99 @@ +package com.sharecomparison.infrastructure; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.List; + +import com.sharecomparison.application.MarketDataService; +import com.sharecomparison.application.PriceRepository; +import com.sharecomparison.domain.PriceData; + +public class YahooFinanceService implements MarketDataService { + + private PriceRepository repository; + + public YahooFinanceService(PriceRepository repository) { + this.repository = repository; + } + + @Override + public List fetchSharePrices( + String symbol, + LocalDate startDate, + LocalDate endDate) { + + try { + + long startEpoch = startDate.atStartOfDay().toEpochSecond(ZoneOffset.UTC); + long endEpoch = endDate.atStartOfDay().toEpochSecond(ZoneOffset.UTC); + + String urlString = + "https://query1.finance.yahoo.com/v7/finance/download/" + + symbol + + "?period1=" + startEpoch + + "&period2=" + endEpoch + + "&interval=1d&events=history"; + + URL url = new URL(urlString); + + HttpURLConnection connection = + (HttpURLConnection) url.openConnection(); + + connection.setRequestMethod("GET"); + + BufferedReader reader = + new BufferedReader( + new InputStreamReader(connection.getInputStream()) + ); + + List prices = + parseCSV(symbol, reader); + + repository.savePrices(prices); + + return prices; + + } catch (Exception e) { + + System.out.println("API failed. Using cached data."); + + return repository.loadPrices(symbol, startDate, endDate); + } + } + + private List parseCSV( + String symbol, + BufferedReader reader) throws Exception { + + List prices = new ArrayList<>(); + + String line; + + reader.readLine(); + + while ((line = reader.readLine()) != null) { + + String[] values = line.split(","); + + if (values.length < 5) continue; + + LocalDate date = + LocalDate.parse(values[0]); + + double closePrice = + Double.parseDouble(values[4]); + + PriceData data = + new PriceData(symbol, date, closePrice); + + prices.add(data); + } + + return prices; + } +} \ No newline at end of file diff --git a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java index 8d094b4..74a0f9d 100644 --- a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java +++ b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java @@ -1,70 +1,135 @@ package com.sharecomparison.presentation; -import com.sharecomparison.application.IPriceController; -import com.sharecomparison.domain.ComparisonResult; -import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDate; // adjust package if needed +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; // or GetMapping if form is GET + import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; -import java.time.LocalDate; +import com.sharecomparison.application.MarketDataService; +import com.sharecomparison.domain.PriceData; @Controller public class WebPageController { - private final IPriceController priceController; + private final MarketDataService marketDataService; - public WebPageController(IPriceController priceController) { - this.priceController = priceController; + public WebPageController(MarketDataService marketDataService) { + this.marketDataService = marketDataService; } @GetMapping("/") - public String index(Model model) { - LocalDate end = LocalDate.now(); - LocalDate start = end.minusMonths(6); - + public String showForm(Model model) { + // Defaults for empty form on first visit model.addAttribute("symbol1", "AAPL"); model.addAttribute("symbol2", "MSFT"); - model.addAttribute("startDate", start); - model.addAttribute("endDate", end); - - return "index"; + model.addAttribute("startDate", LocalDate.now().minusYears(1).toString()); + model.addAttribute("endDate", LocalDate.now().toString()); + return "index"; // your Thymeleaf template } - @PostMapping("/compare") + @PostMapping("/compare") // Change to @GetMapping("/compare") if you update form method="get" public String compare( @RequestParam String symbol1, @RequestParam String symbol2, - @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, - @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate, Model model) { - String s1 = symbol1 == null ? "" : symbol1.trim().toUpperCase(); - String s2 = symbol2 == null ? "" : symbol2.trim().toUpperCase(); + LocalDate start = (startDate != null && !startDate.isBlank()) ? LocalDate.parse(startDate) : LocalDate.now().minusYears(1); + LocalDate end = (endDate != null && !endDate.isBlank()) ? LocalDate.parse(endDate) : LocalDate.now(); - LocalDate effectiveEnd = endDate == null ? LocalDate.now() : endDate; - LocalDate effectiveStart = startDate == null ? effectiveEnd.minusMonths(6) : startDate; + symbol1 = symbol1.trim().toUpperCase(); + symbol2 = symbol2.trim().toUpperCase(); - model.addAttribute("symbol1", s1); - model.addAttribute("symbol2", s2); - model.addAttribute("startDate", effectiveStart); - model.addAttribute("endDate", effectiveEnd); + List data1 = marketDataService.fetchSharePrices(symbol1, start, end); + List data2 = marketDataService.fetchSharePrices(symbol2, start, end); - if (s1.isEmpty() || s2.isEmpty()) { - model.addAttribute("error", "Both symbols are required."); + if (data1.isEmpty() || data2.isEmpty()) { + model.addAttribute("error", "No data available for one or both symbols in this range."); + model.addAttribute("symbol1", symbol1); + model.addAttribute("symbol2", symbol2); + model.addAttribute("startDate", start.toString()); + model.addAttribute("endDate", end.toString()); return "index"; } - if (effectiveStart.isAfter(effectiveEnd)) { - model.addAttribute("error", "Start date must be on or before end date."); - return "index"; + data1.sort(Comparator.comparing(PriceData::getDate)); + data2.sort(Comparator.comparing(PriceData::getDate)); + + // Chart coords calculation (fits your SVG 900x360 viewBox) + double chartWidth = 800, chartHeight = 300; + double left = 80, right = left + chartWidth; + double bottom = 320, top = bottom - chartHeight; + double midY = top + chartHeight / 2; + + double minPrice = Double.MAX_VALUE, maxPrice = Double.MIN_VALUE; + for (PriceData p : data1) { + minPrice = Math.min(minPrice, p.getClosingPrice()); + maxPrice = Math.max(maxPrice, p.getClosingPrice()); + } + for (PriceData p : data2) { + minPrice = Math.min(minPrice, p.getClosingPrice()); + maxPrice = Math.max(maxPrice, p.getClosingPrice()); } + if (maxPrice == minPrice) maxPrice += 1.0; + double priceRange = maxPrice - minPrice; + + int n = Math.min(data1.size(), data2.size()); + double xStep = n > 1 ? chartWidth / (n - 1) : 0; + + StringBuilder path1 = new StringBuilder(); + StringBuilder path2 = new StringBuilder(); + List> points1 = new ArrayList<>(); + List> points2 = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + double x = left + i * xStep; + + PriceData p1 = data1.get(i); + double y1 = bottom - ((p1.getClosingPrice() - minPrice) / priceRange) * chartHeight; + path1.append(i == 0 ? "M" : "L").append(x).append(",").append(y1).append(" "); + points1.add(Map.of("x", x, "y", y1, "value", p1.getClosingPrice())); + + PriceData p2 = data2.get(i); + double y2 = bottom - ((p2.getClosingPrice() - minPrice) / priceRange) * chartHeight; + path2.append(i == 0 ? "M" : "L").append(x).append(",").append(y2).append(" "); + points2.add(Map.of("x", x, "y", y2, "value", p2.getClosingPrice())); + } + + Map chartData = new HashMap<>(); + chartData.put("left", left); + chartData.put("right", right); + chartData.put("top", top); + chartData.put("bottom", bottom); + chartData.put("midY", midY); + chartData.put("symbol1Path", path1.toString()); + chartData.put("symbol2Path", path2.toString()); + chartData.put("symbol1Points", points1); + chartData.put("symbol2Points", points2); + + Map result = new HashMap<>(); + result.put("symbol1", symbol1); + result.put("symbol2", symbol2); + result.put("startDate", start); + result.put("endDate", end); + result.put("chartData", chartData); + result.put("symbol1Data", data1); + result.put("symbol2Data", data2); - ComparisonResult result = priceController.comparePrices(s1, s2, effectiveStart, effectiveEnd); model.addAttribute("result", result); + model.addAttribute("symbol1", symbol1); + model.addAttribute("symbol2", symbol2); + model.addAttribute("startDate", start.toString()); + model.addAttribute("endDate", end.toString()); return "index"; } -} +} \ No newline at end of file diff --git a/stockscope/src/main/resources/templates/index.html b/stockscope/src/main/resources/templates/index.html index 1d31952..aeaa569 100644 --- a/stockscope/src/main/resources/templates/index.html +++ b/stockscope/src/main/resources/templates/index.html @@ -157,6 +157,12 @@ display: block; } + .error { + color: #911d1d; + font-weight: bold; + margin-bottom: 1rem; + } + @media (max-width: 768px) { .cards { grid-template-columns: 1fr; } form { flex-direction: column; border: none; gap: 1rem; } @@ -169,7 +175,7 @@

Software Architecture and Design

-
+ @@ -188,32 +194,21 @@

Software Architecture and Design

- - - - - Date - Price + + + + + Date + Price - - - @@ -222,12 +217,7 @@

Software Architecture and Design

- - - - - - + @@ -240,12 +230,7 @@

DateClose
DateClose
- - - - - - + @@ -255,8 +240,11 @@

DateClose
DateClose
+ +
+ Enter two stock symbols and a date range above to see live historical comparison.
- + \ No newline at end of file From 67d93a2e901a73d290202057c6bed2dd967fcd01 Mon Sep 17 00:00:00 2001 From: Jack Turner Date: Tue, 17 Mar 2026 12:22:20 +0000 Subject: [PATCH 02/26] Sprint 2 Commit 2 Implemented system interfaces and external data integration for share price retrieval using Clean Architecture principles. Added Alpha Vantage support with API key configuration via environment variables and introduced an output size setting to stay within free tier limits. Implemented basic rate limiting of one request per second to prevent throttling when comparing multiple symbols. Improved error handling to detect API specific responses such as Information, Note, and Error Message despite HTTP 200 status codes, with appropriate logging. Fixed parsing logic to reliably extract closing prices from the Time Series Daily data. Ensured data is converted into domain models and cached locally to support offline access and improve system reliability. --- README.md | 6 +- stockscope/pom.xml | 1 + .../StockComparisonApplication.java | 12 +- .../application/ComparisonController.java | 145 ---------- .../infrastructure/AlphaVantageService.java | 257 ++++++++++++++++++ .../infrastructure/YahooFinanceService.java | 99 ------- .../presentation/WebPageController.java | 7 +- .../src/main/resources/application.properties | 30 ++ 8 files changed, 304 insertions(+), 253 deletions(-) delete mode 100644 stockscope/src/main/java/com/sharecomparison/application/ComparisonController.java create mode 100644 stockscope/src/main/java/com/sharecomparison/infrastructure/AlphaVantageService.java delete mode 100644 stockscope/src/main/java/com/sharecomparison/infrastructure/YahooFinanceService.java create mode 100644 stockscope/src/main/resources/application.properties diff --git a/README.md b/README.md index 7214545..80a099f 100644 --- a/README.md +++ b/README.md @@ -22,4 +22,8 @@ This is a Java-based web application for charting and comparing share prices ove | **Individual Reflection** | 23.4.26 | 10% | - | ## How to run -./mvnw spring-boot:run +`cd stockscope && ./mvnw spring-boot:run` + +### Set your API key +- Mac/Linux (zsh): `export ALPHAVANTAGE_KEY="YOUR_KEY_HERE"` then restart the app. +- If you run from an IDE (IntelliJ/Eclipse), set `ALPHAVANTAGE_KEY` in the Run/Debug configuration environment variables (Terminal exports may not be inherited). diff --git a/stockscope/pom.xml b/stockscope/pom.xml index 90b87f1..10738fe 100644 --- a/stockscope/pom.xml +++ b/stockscope/pom.xml @@ -35,6 +35,7 @@ spring-boot-starter-test test + diff --git a/stockscope/src/main/java/com/sharecomparison/StockComparisonApplication.java b/stockscope/src/main/java/com/sharecomparison/StockComparisonApplication.java index 62def39..8012b7f 100644 --- a/stockscope/src/main/java/com/sharecomparison/StockComparisonApplication.java +++ b/stockscope/src/main/java/com/sharecomparison/StockComparisonApplication.java @@ -3,11 +3,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +import org.springframework.beans.factory.annotation.Value; import com.sharecomparison.application.MarketDataService; import com.sharecomparison.application.PriceRepository; +import com.sharecomparison.infrastructure.AlphaVantageService; import com.sharecomparison.infrastructure.InMemoryPriceRepository; -import com.sharecomparison.infrastructure.YahooFinanceService; @SpringBootApplication public class StockComparisonApplication { @@ -22,7 +23,10 @@ public PriceRepository priceRepository() { } @Bean - public MarketDataService marketDataService(PriceRepository priceRepository) { - return new YahooFinanceService(priceRepository); + public MarketDataService marketDataService( + PriceRepository repository, + @Value("${alphavantage.key:demo}") String apiKey, + @Value("${alphavantage.outputsize:compact}") String outputSize) { + return new AlphaVantageService(repository, apiKey, outputSize); } -} \ No newline at end of file +} diff --git a/stockscope/src/main/java/com/sharecomparison/application/ComparisonController.java b/stockscope/src/main/java/com/sharecomparison/application/ComparisonController.java deleted file mode 100644 index bf17b6c..0000000 --- a/stockscope/src/main/java/com/sharecomparison/application/ComparisonController.java +++ /dev/null @@ -1,145 +0,0 @@ -/*package com.sharecomparison.application; - -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; - -import com.sharecomparison.domain.PriceData; - -@Controller -public class ComparisonController { - - private final MarketDataService marketDataService; - - public ComparisonController(MarketDataService marketDataService) { - this.marketDataService = marketDataService; - } - - @GetMapping("/") - public String home(Model model) { - // Default values for the form on first load - model.addAttribute("symbol1", "AAPL"); - model.addAttribute("symbol2", "MSFT"); - model.addAttribute("startDate", LocalDate.now().minusYears(1).toString()); - model.addAttribute("endDate", LocalDate.now().toString()); - return "index"; - } - - @GetMapping("/compare") - public String compare( - @RequestParam String symbol1, - @RequestParam String symbol2, - @RequestParam(required = false) String startDate, - @RequestParam(required = false) String endDate, - Model model) { - - LocalDate start = (startDate != null && !startDate.isEmpty()) - ? LocalDate.parse(startDate) - : LocalDate.now().minusYears(1); - LocalDate end = (endDate != null && !endDate.isEmpty()) - ? LocalDate.parse(endDate) - : LocalDate.now(); - - symbol1 = symbol1.trim().toUpperCase(); - symbol2 = symbol2.trim().toUpperCase(); - - List data1 = marketDataService.fetchSharePrices(symbol1, start, end); - List data2 = marketDataService.fetchSharePrices(symbol2, start, end); - - if (data1.isEmpty() || data2.isEmpty()) { - model.addAttribute("error", "No data found for one or both symbols. Try different dates or symbols."); - model.addAttribute("symbol1", symbol1); - model.addAttribute("symbol2", symbol2); - model.addAttribute("startDate", start.toString()); - model.addAttribute("endDate", end.toString()); - return "index"; - } - - // Sort both lists by date - data1.sort(Comparator.comparing(PriceData::getDate)); - data2.sort(Comparator.comparing(PriceData::getDate)); - - // ==================== CHART CALCULATION ==================== - double chartWidth = 800; - double chartHeight = 300; - double left = 80; - double right = left + chartWidth; - double bottom = 320; - double top = bottom - chartHeight; - double midY = top + chartHeight / 2; - - // Global min/max price - double minPrice = Double.MAX_VALUE; - double maxPrice = Double.MIN_VALUE; - for (PriceData p : data1) { minPrice = Math.min(minPrice, p.getClosingPrice()); maxPrice = Math.max(maxPrice, p.getClosingPrice()); } - for (PriceData p : data2) { minPrice = Math.min(minPrice, p.getClosingPrice()); maxPrice = Math.max(maxPrice, p.getClosingPrice()); } - if (maxPrice == minPrice) maxPrice += 1; - double priceRange = maxPrice - minPrice; - - // Use the shorter list length for chart alignment - int n = Math.min(data1.size(), data2.size()); - double xStep = (n > 1) ? chartWidth / (n - 1) : 0; - - StringBuilder path1 = new StringBuilder(); - StringBuilder path2 = new StringBuilder(); - List> points1 = new ArrayList<>(); - List> points2 = new ArrayList<>(); - - for (int i = 0; i < n; i++) { - PriceData p1 = data1.get(i); - double x = left + i * xStep; - double y1 = bottom - ((p1.getClosingPrice() - minPrice) / priceRange) * chartHeight; - - if (i == 0) path1.append("M ").append(x).append(",").append(y1); - else path1.append(" L ").append(x).append(",").append(y1); - - points1.add(Map.of("x", x, "y", y1, "value", p1.getClosingPrice())); - - // Symbol 2 - PriceData p2 = data2.get(i); - double y2 = bottom - ((p2.getClosingPrice() - minPrice) / priceRange) * chartHeight; - - if (i == 0) path2.append("M ").append(x).append(",").append(y2); - else path2.append(" L ").append(x).append(",").append(y2); - - points2.add(Map.of("x", x, "y", y2, "value", p2.getClosingPrice())); - } - - Map chartData = new HashMap<>(); - chartData.put("left", left); - chartData.put("right", right); - chartData.put("top", top); - chartData.put("bottom", bottom); - chartData.put("midY", midY); - chartData.put("symbol1Path", path1.toString()); - chartData.put("symbol2Path", path2.toString()); - chartData.put("symbol1Points", points1); - chartData.put("symbol2Points", points2); - - // Result object your HTML expects - Map result = new HashMap<>(); - result.put("symbol1", symbol1); - result.put("symbol2", symbol2); - result.put("startDate", start); - result.put("endDate", end); - result.put("chartData", chartData); - result.put("symbol1Data", data1); - result.put("symbol2Data", data2); - - model.addAttribute("result", result); - model.addAttribute("symbol1", symbol1); - model.addAttribute("symbol2", symbol2); - model.addAttribute("startDate", start.toString()); - model.addAttribute("endDate", end.toString()); - - return "index"; - } -}*/ \ No newline at end of file diff --git a/stockscope/src/main/java/com/sharecomparison/infrastructure/AlphaVantageService.java b/stockscope/src/main/java/com/sharecomparison/infrastructure/AlphaVantageService.java new file mode 100644 index 0000000..c226b18 --- /dev/null +++ b/stockscope/src/main/java/com/sharecomparison/infrastructure/AlphaVantageService.java @@ -0,0 +1,257 @@ +package com.sharecomparison.infrastructure; + +import java.net.URI; +import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.sharecomparison.application.MarketDataService; +import com.sharecomparison.application.PriceRepository; +import com.sharecomparison.domain.PriceData; + +public class AlphaVantageService implements MarketDataService { + + private static final Logger log = LoggerFactory.getLogger(AlphaVantageService.class); + private static final int LOG_SNIPPET_LENGTH = 250; + private static final long MIN_REQUEST_INTERVAL_MS = 1100; + private static final Object RATE_LIMIT_LOCK = new Object(); + private static long lastRequestAtMs = 0L; + + private final HttpClient httpClient = HttpClient.newHttpClient(); + private final PriceRepository repository; + + private final String apiKey; + private final String outputSize; + + public AlphaVantageService(PriceRepository repository) { + this(repository, null); + } + + public AlphaVantageService(PriceRepository repository, String apiKey) { + this(repository, apiKey, null); + } + + public AlphaVantageService(PriceRepository repository, String apiKey, String outputSize) { + this.repository = repository; + this.apiKey = resolveApiKey(apiKey); + this.outputSize = normalizeOutputSize(outputSize); + } + + @Override + public List fetchSharePrices(String symbol, LocalDate startDate, LocalDate endDate) { + try { + if (symbol == null || symbol.isBlank()) { + return repository.loadPrices(symbol, startDate, endDate); + } + + String json = fetchTimeSeriesDailyJson(symbol, outputSize); + if (json == null) { + return repository.loadPrices(symbol, startDate, endDate); + } + + if (json.contains("\"Note\"")) { + log.warn("Alpha Vantage throttled request: {}", extractStringFieldOrSnippet(json, "Note")); + return repository.loadPrices(symbol, startDate, endDate); + } + if (json.contains("\"Information\"")) { + log.warn("Alpha Vantage returned information: {}", extractStringFieldOrSnippet(json, "Information")); + if ("full".equals(outputSize) + && json.contains("outputsize=full") + && (json.toLowerCase().contains("premium") || json.toLowerCase().contains("subscribe"))) { + log.warn("Retrying Alpha Vantage request with outputsize=compact (free-tier fallback)."); + String fallbackJson = fetchTimeSeriesDailyJson(symbol, "compact"); + if (fallbackJson != null + && !fallbackJson.contains("\"Information\"") + && !fallbackJson.contains("\"Note\"") + && !fallbackJson.contains("\"Error Message\"") + && !fallbackJson.contains("\"error message\"")) { + json = fallbackJson; + } else { + return repository.loadPrices(symbol, startDate, endDate); + } + } else { + return repository.loadPrices(symbol, startDate, endDate); + } + } + if (json.contains("\"Error Message\"") || json.contains("\"error message\"")) { + log.warn("Alpha Vantage error for symbol={}: {}", symbol, extractStringFieldOrSnippet(json, "Error Message")); + return repository.loadPrices(symbol, startDate, endDate); + } + + List prices = parseTimeSeriesDailyJson(symbol, json, startDate, endDate); + + if (prices.isEmpty()) { + log.warn("Alpha Vantage returned no price data for symbol={}. Response snippet: {}", symbol, snippet(json)); + return repository.loadPrices(symbol, startDate, endDate); + } + + repository.savePrices(prices); + return prices; + + } catch (Exception e) { + log.warn("Alpha Vantage fetch failed for symbol={}: {}", symbol, e.toString()); + return repository.loadPrices(symbol, startDate, endDate); + } + } + + private String fetchTimeSeriesDailyJson(String symbol, String outputSize) throws Exception { + rateLimit(); + String url = "https://www.alphavantage.co/query" + + "?function=TIME_SERIES_DAILY" + + "&symbol=" + URLEncoder.encode(symbol.trim(), StandardCharsets.UTF_8) + + "&outputsize=" + URLEncoder.encode(normalizeOutputSize(outputSize), StandardCharsets.UTF_8) + + "&apikey=" + URLEncoder.encode(apiKey, StandardCharsets.UTF_8); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Accept", "application/json") + .GET() + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + if (response.statusCode() != 200) { + log.warn("Alpha Vantage HTTP error: status={}", response.statusCode()); + return null; + } + return response.body(); + } + + private static void rateLimit() { + synchronized (RATE_LIMIT_LOCK) { + long now = System.currentTimeMillis(); + long elapsed = now - lastRequestAtMs; + if (elapsed < MIN_REQUEST_INTERVAL_MS) { + long sleepMs = MIN_REQUEST_INTERVAL_MS - elapsed; + try { + Thread.sleep(sleepMs); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } + } + lastRequestAtMs = System.currentTimeMillis(); + } + } + + List parseTimeSeriesDailyJson( + String symbol, + String json, + LocalDate start, + LocalDate end) { + + List prices = new ArrayList<>(); + + int timeSeriesIndex = json.indexOf("\"Time Series (Daily)\""); + if (timeSeriesIndex == -1) { + log.warn("No 'Time Series (Daily)' section found in Alpha Vantage response"); + return prices; + } + + int startObj = json.indexOf('{', timeSeriesIndex); + if (startObj == -1) return prices; + + int braceCount = 1; + int cursor = startObj + 1; + while (cursor < json.length() && braceCount > 0) { + char c = json.charAt(cursor); + if (c == '{') braceCount++; + if (c == '}') braceCount--; + cursor++; + } + if (braceCount != 0) { + log.warn("Unbalanced braces while parsing 'Time Series (Daily)'"); + return prices; + } + + String timeSeriesBlock = json.substring(startObj, cursor); + + DateTimeFormatter fmt = DateTimeFormatter.ISO_LOCAL_DATE; + + Pattern entryPattern = Pattern.compile( + "\"(\\d{4}-\\d{2}-\\d{2})\"\\s*:\\s*\\{[^}]*?\"4\\. close\"\\s*:\\s*\"([^\"]+)\"", + Pattern.DOTALL + ); + + Matcher matcher = entryPattern.matcher(timeSeriesBlock); + while (matcher.find()) { + String dateStr = matcher.group(1); + String closeStr = matcher.group(2); + LocalDate date; + try { + date = LocalDate.parse(dateStr, fmt); + } catch (Exception ex) { + continue; + } + + if (date.isBefore(start) || date.isAfter(end)) continue; + + double closePrice; + try { + closePrice = Double.parseDouble(closeStr); + } catch (NumberFormatException ex) { + continue; + } + + prices.add(new PriceData(symbol, date, closePrice)); + } + + // Sort just in case + prices.sort((a, b) -> a.getDate().compareTo(b.getDate())); + + return prices; + } + + private static String resolveApiKey(String apiKey) { + String resolvedKey = (apiKey == null || apiKey.isBlank()) + ? System.getenv("ALPHAVANTAGE_KEY") + : apiKey; + if (resolvedKey == null || resolvedKey.isBlank()) { + log.warn("Alpha Vantage API key not set (property/env). Falling back to 'demo' key."); + resolvedKey = "demo"; + } + if ("demo".equalsIgnoreCase(resolvedKey)) { + log.warn("Alpha Vantage is configured with the 'demo' key (very limited symbols). Set ALPHAVANTAGE_KEY (or alphavantage.key) to use a real key."); + } + return resolvedKey; + } + + private static String normalizeOutputSize(String outputSize) { + if (outputSize == null || outputSize.isBlank()) return "compact"; + String normalized = outputSize.trim().toLowerCase(); + if (!"compact".equals(normalized) && !"full".equals(normalized)) { + log.warn("Invalid alphavantage.outputsize='{}'. Falling back to 'compact'.", outputSize); + return "compact"; + } + return normalized; + } + + private static String extractStringFieldOrSnippet(String json, String fieldName) { + String extracted = extractStringField(json, fieldName); + return extracted != null ? extracted : snippet(json); + } + + private static String extractStringField(String json, String fieldName) { + if (json == null || json.isBlank() || fieldName == null || fieldName.isBlank()) return null; + Pattern pattern = Pattern.compile("\"" + Pattern.quote(fieldName) + "\"\\s*:\\s*\"([^\"]*)\""); + Matcher matcher = pattern.matcher(json); + if (!matcher.find()) return null; + return matcher.group(1); + } + + private static String snippet(String value) { + if (value == null) return "null"; + String trimmed = value.trim(); + if (trimmed.length() <= LOG_SNIPPET_LENGTH) return trimmed; + return trimmed.substring(0, LOG_SNIPPET_LENGTH) + "..."; + } +} diff --git a/stockscope/src/main/java/com/sharecomparison/infrastructure/YahooFinanceService.java b/stockscope/src/main/java/com/sharecomparison/infrastructure/YahooFinanceService.java deleted file mode 100644 index 35d4868..0000000 --- a/stockscope/src/main/java/com/sharecomparison/infrastructure/YahooFinanceService.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.sharecomparison.infrastructure; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; -import java.time.LocalDate; -import java.time.ZoneOffset; -import java.util.ArrayList; -import java.util.List; - -import com.sharecomparison.application.MarketDataService; -import com.sharecomparison.application.PriceRepository; -import com.sharecomparison.domain.PriceData; - -public class YahooFinanceService implements MarketDataService { - - private PriceRepository repository; - - public YahooFinanceService(PriceRepository repository) { - this.repository = repository; - } - - @Override - public List fetchSharePrices( - String symbol, - LocalDate startDate, - LocalDate endDate) { - - try { - - long startEpoch = startDate.atStartOfDay().toEpochSecond(ZoneOffset.UTC); - long endEpoch = endDate.atStartOfDay().toEpochSecond(ZoneOffset.UTC); - - String urlString = - "https://query1.finance.yahoo.com/v7/finance/download/" - + symbol - + "?period1=" + startEpoch - + "&period2=" + endEpoch - + "&interval=1d&events=history"; - - URL url = new URL(urlString); - - HttpURLConnection connection = - (HttpURLConnection) url.openConnection(); - - connection.setRequestMethod("GET"); - - BufferedReader reader = - new BufferedReader( - new InputStreamReader(connection.getInputStream()) - ); - - List prices = - parseCSV(symbol, reader); - - repository.savePrices(prices); - - return prices; - - } catch (Exception e) { - - System.out.println("API failed. Using cached data."); - - return repository.loadPrices(symbol, startDate, endDate); - } - } - - private List parseCSV( - String symbol, - BufferedReader reader) throws Exception { - - List prices = new ArrayList<>(); - - String line; - - reader.readLine(); - - while ((line = reader.readLine()) != null) { - - String[] values = line.split(","); - - if (values.length < 5) continue; - - LocalDate date = - LocalDate.parse(values[0]); - - double closePrice = - Double.parseDouble(values[4]); - - PriceData data = - new PriceData(symbol, date, closePrice); - - prices.add(data); - } - - return prices; - } -} \ No newline at end of file diff --git a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java index 74a0f9d..78ff57d 100644 --- a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java +++ b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java @@ -10,7 +10,6 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import com.sharecomparison.application.MarketDataService; @@ -32,10 +31,10 @@ public String showForm(Model model) { model.addAttribute("symbol2", "MSFT"); model.addAttribute("startDate", LocalDate.now().minusYears(1).toString()); model.addAttribute("endDate", LocalDate.now().toString()); - return "index"; // your Thymeleaf template + return "index"; } - @PostMapping("/compare") // Change to @GetMapping("/compare") if you update form method="get" + @GetMapping("/compare") public String compare( @RequestParam String symbol1, @RequestParam String symbol2, @@ -64,7 +63,7 @@ public String compare( data1.sort(Comparator.comparing(PriceData::getDate)); data2.sort(Comparator.comparing(PriceData::getDate)); - // Chart coords calculation (fits your SVG 900x360 viewBox) + // Chart coords calculation double chartWidth = 800, chartHeight = 300; double left = 80, right = left + chartWidth; double bottom = 320, top = bottom - chartHeight; diff --git a/stockscope/src/main/resources/application.properties b/stockscope/src/main/resources/application.properties new file mode 100644 index 0000000..7139445 --- /dev/null +++ b/stockscope/src/main/resources/application.properties @@ -0,0 +1,30 @@ +# ================================ +# Alpha Vantage API Configuration +# ================================ +# Get your free key at: https://www.alphavantage.co/support/#api-key +# Use 'demo' for quick testing (limited symbols, 25 calls/day) +alphavantage.key=${ALPHAVANTAGE_KEY:demo} +# "full" is now treated as premium for TIME_SERIES_DAILY by Alpha Vantage (free keys will get an "Information" response). +alphavantage.outputsize=compact + +# ================================ +# Development / Debugging Helpers +# ================================ +# Disable Thymeleaf template caching → changes to index.html reload instantly without restart +spring.thymeleaf.cache=false + +# Show detailed startup logs and bean wiring info (helpful for debugging ambiguous mappings, etc.) +logging.level.org.springframework=INFO +# logging.level.org.springframework.web=DEBUG ← uncomment for more request details +# logging.level.com.sharecomparison=DEBUG ← uncomment to log your own classes + +# Optional: Change server port if 8080 is blocked/conflicted +# server.port=8081 + +# Optional: If you later add a database (e.g. H2 for persistent cache) +# spring.datasource.url=jdbc:h2:mem:testdb +# spring.datasource.driverClassName=org.h2.Driver +# spring.datasource.username=sa +# spring.datasource.password= +# spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +# spring.h2.console.enabled=true From 027ff4d7b07c6dc947c9fb57ceb2b7c0c8d3c4a5 Mon Sep 17 00:00:00 2001 From: Luke Pring Date: Fri, 20 Mar 2026 15:41:37 +0000 Subject: [PATCH 03/26] Delete stockscope/src/main/java/com/.DS_Store --- stockscope/src/main/java/com/.DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 stockscope/src/main/java/com/.DS_Store diff --git a/stockscope/src/main/java/com/.DS_Store b/stockscope/src/main/java/com/.DS_Store deleted file mode 100644 index a8ac13f6a1e0168cad6d9b17245bc86e435fae2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKPfrs;6n_I1S`fRGf+%X5^x^^KZ-~Z&wO|ZtQbH>h0qbr%lnvXNW_R0yNbFgU zegHp!cjM8cUqDYL-aYvNO!Up4m46y;O3c1w=J)2!dvAAsyE|_O07$k`nE)^VAc{?3 zqzAj-h?p1ch;(R61d+%b6w1J%26Zrxc!!3Nfslcd#(?;C|9)Mt(awR* zJG^W;+(J*WO=PiM{UHUlW23E>SJ%<+th!@+{`mMO($N_?)zuyCj`l{^@*BLB_f5ay zWleWQj0&7PR-l{Kl1*DBBRR{NZ`zEyGQh4-bXk39vkGrzd4p9v8JkxV(O5KAGKP0{ zCTGS+Q&ZCuyQ8U{>yxNYPtEM^#-fAiu^Wq}O{d2A3%L#;xQ9vU(-^=@RF2`uE;scm z$>(5|UyS}>ow1(Y(|u>+Mt@=;nHWqA4JC)p4xc+ea$%%o3|MA)v+9Z!%yNgjS*Jl) zs;*th*Uhrcszsk}`FZ;(Eg6YJu8Xwtc+XVWuA@(K&mhy?aVcdoUv7QfqkeIpzWm7C zD*VbG_sTAJoCWULzRlV4s)vta-lw&Bw$Ab5O?(Wwx|s5osOuq?j6*TSWz%&{R;AhP zHOI85lVe%#`@B}P%bcl2#AR9>F1S>rOyB&bh)!qj1-%P@<0*M1`Y0rTsD*+X_`U z5P4lQbIw@XlyG)3rhGSAZZA@kb%F- Fz)xOt>c;>8 From 976cbb26d6bff09b43f697707ec3c2e7cf380724 Mon Sep 17 00:00:00 2001 From: Luke Pring Date: Fri, 20 Mar 2026 15:42:06 +0000 Subject: [PATCH 04/26] Delete stockscope/src/main/java/.DS_Store --- stockscope/src/main/java/.DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 stockscope/src/main/java/.DS_Store diff --git a/stockscope/src/main/java/.DS_Store b/stockscope/src/main/java/.DS_Store deleted file mode 100644 index 87968d8e80e265cd9e453f7fa6434815044d6586..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}>-o6rX_=x*$@P1yQ2eWE0~7ykEDS9fS~8YL!uhWQ33e zjbfq$x<3fD({?~w#0)iJnu8d+O>`L1bg{$?#0(sF2I#xniVQ@MGi&d8e-*G?{>g+D zA-wcP;EA{Vt?t=Y5RQy|#x1Rh6K(BMyVNPIsB5I5hFVw)6wP0vqaq=;9_gCCV401w zoSq?Ws9CP*vjD4Ng3Iz<%dL=xLTYX$V6iGUNJ%MKmIt=B#wJIGGUF4Y+e4YHD`TKf zj8ATFC#AmZ@YVV9y6w2+2`3%HH;GA?zzCj#BC4^Bx~K+!iK_W3_eAw1J33Exo$8i* zQoZR^U#h=9-G6%E%-O-gb7i?#*NpY5Pj@;)Y~m|+&0MPbRz>wR!*Z*o(A)@B>!Dee zQ~O-!&C30qsp6&wKIxqamhOgcnl8&%SX~Xwu(V5Gyr*pxe`oIw44>Hc90{z@BJSdH z0F=*$rZelV5?H(rbQe5o3KmR104&S9O+0Ch_mmECew>9am{2ho0}6R30*=nsDMhy zL66XL^b);BZ_o$y34KF9a2rnJ3wRh$;c0vm-^O?F0hzq z;)&bUQPm)hx64CV9FotAU$*q|@v|3Guij7Z#5tt$7pu2+Sk}7gyEbvFkSW^TuBDGu z75kAok8~ID;xPj;1ILj8`hCbiqnH!e17_$1U`}9@Bg}wc z9ty}qvAAMjad*Hx9PBya-{i={5oRkx9xkZ?qddG2KF-0%7-#N z|F0i@|KA%Fk75R52L3Asa3Zhf6(~uZtp|#uXKe=B187vJUy~yXfleI9Orb~d05m3G a%TNU{C$Py8WZiy8Q*4EzFNj_`&6 From e0fcb9ef58f7eac3ba39a67d8d65f27ed4a30c6f Mon Sep 17 00:00:00 2001 From: Luke Pring Date: Fri, 20 Mar 2026 15:42:32 +0000 Subject: [PATCH 05/26] Delete stockscope/src/main/.DS_Store --- stockscope/src/main/.DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 stockscope/src/main/.DS_Store diff --git a/stockscope/src/main/.DS_Store b/stockscope/src/main/.DS_Store deleted file mode 100644 index 1682cf970ef421206ecf92b79db87b3c50dc9ecf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}*0S6n_I1wji=VL6m43n-~u$pCNu6ECpjwlM;$p1gzWbP*%1(o84^-B1zAB z^bhcA;@x=krvHGROuTzC@oJ)PK3cxS6ETMDOJ;s==6y`xZ#z3X06s)TD5jWKiWK+$FSf#nofHO0zK!56U^Y0#otG^h7(ZH-P055~vFN45v!Th~TW zA0M08-i~U$iJ=>Fxplkbu;-F?g6}0E9SZC40+mKRmRV8tkT3OmzUp1|ilS}pCp%7c z>RsJEvF_gPzP?!B>Hafk2L{gN^q#z7t`|N2Tc?=Kyp&z0OGVErWL(3voMJYh8$rf; zN^^SmKG!)~c(gM$zv-e+Y-d8GyX{fxhQ=jI+vc@d^^2cDc!4pq`JI)Az+>7Y)xdoFYwauGmJN+G!`nf`FB{C(mXw zXV#-UQ`SPAmzB8rT~eAz-mNQ_q@qJYDtLUv>Y755cyHE(KJPGxMx%-3tTJhE8Kxi& zSt!9XcnPoI4ZMYq@Cm-bPtrnSu>F?MES%zW(~d1P|C&S@4n(YsdDO~8-B5^F z9p~f19Y|7Q*oO>+4D4l~Sq^o6{@=nqe8~NOZ%{Z283-9TRt%8HbY?n*C8@LZKym!6 wjbnR^jSKgyNfaU2spD8E{3!0nCIoG9t{^5A)+FK#O86llXb8KIfq%-tZ-R;e1poj5 From 88e83c9c46e6c7ae4cb98559863a57e5b2b5963b Mon Sep 17 00:00:00 2001 From: Luke Pring Date: Fri, 20 Mar 2026 15:42:52 +0000 Subject: [PATCH 06/26] Delete stockscope/src/.DS_Store --- stockscope/src/.DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 stockscope/src/.DS_Store diff --git a/stockscope/src/.DS_Store b/stockscope/src/.DS_Store deleted file mode 100644 index 934af43d55951043d88fabcb77157bfbaa09f161..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKPfrs;6rX`ATM$`FK@>GjdhvkrHw5FsQfdfl(uP(n0@iIilnvXNW_R0yNbFhT z$q(SwL=$hulkuisKu;##J^2Al^v$0wP{b24YW5{Fzc=sAd%N$qJ3BiFAsnw3#}LvH zLK-xVfllatA>7Z~0clf~ARy*B<}FspTPzP_p3oPc0iS`tn*qLd1E`D)WFp$KeqVv_ z?GGWMh~~U}-KnT`{w80sOvfD^{e;`v1ILbcXdPOYwwhjN^|WiaRVQxP%Y2k!%*uPZ zkzX`PJ*S6f8FdYl5?chAMFLY+Zkx2o>Ty=3MMuP@r9%s9!JIz0y*)lPIvkmp9NQU= zY+oJ+eR5)IXD6ujM@OzKv7jgrmpb(UGoj$2i-T(Zq#x?&VeTFSa) z(@mTANlp*7OkE(wySt{$Rt4sScMT%loJ|N7`I2jE4so-4^rbt-W~Q0F$0^v%vQo@3 zU6awJ6$eP4cS(7kt}(cH4G2$GxXW22wgZ^cTVk?HhHV?PMB-bkmXRk`lE#_qvU1ie zFe;1a5ouwVvI$Qaz4}d38jaoXlnYYPQB*3heS|$tA&$N)G=!d^jFMn9mXe+zY&93r zEJ~s*yp<2oQ}hhIK(EmU^bvhU-|=xA#^>+|p2joyI=+c-;YD1+6 Date: Fri, 20 Mar 2026 15:43:20 +0000 Subject: [PATCH 07/26] Delete stockscope/.DS_Store --- stockscope/.DS_Store | Bin 8196 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 stockscope/.DS_Store diff --git a/stockscope/.DS_Store b/stockscope/.DS_Store deleted file mode 100644 index 36781bc263b07eb432fb7e108da0969e1a76c3db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHM&2JM&6n`%X$-04N2_}R>D+^yZ(0l+wR2(o)Dim5N5e~uOqwab=F)P+HtKGF7 z2ob*0N}Z1w6-SdasRC4)VU zG(v(pl~AXm*W1}xE!|G?PeS_Dv{?0yje}z zdRhq{vYI>I7kZsFzSMq4hmMmU=tr;zrzQ~Nz+Hj$$4SL1RXO=-M{^a+Rz03t?xngNadvdf6 zeF}512vz#cK7yz441R-oO=XVuWq%;A354AiLt9 z=zgZLN0zzbVRN?Pi&m8RwmmH|KW~27&Ub%IKe_nBt1EAN8}IN&_x7vWsl+@Kyh)Yh z*$qPq9^GwCV36*-;|Aoyx_#FdOh(CIPotV5DYXg{+V!7!v0@o`pBNa_Cp#(k|DW&w z{r`QMl08?;fMwtxGJr$#m3jHHuU7RvlI1F2p!PL2DKf67QKgXD@ghL|tv?LOE>Nh< WDH-f(q!AMPAwbb!m1W>xW#A3RizT`M From 39ea586152753b2e57f2122831e92213bb97d7c5 Mon Sep 17 00:00:00 2001 From: Luke Pring Date: Fri, 20 Mar 2026 15:43:43 +0000 Subject: [PATCH 08/26] Delete .DS_Store --- .DS_Store | Bin 8196 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index fbf407f23de180ed66676047c5776431b1ef10b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHM&2JM&6n|qvSd&5)#|fdR$ih<91BC<-YRdt~aX>D#iDD->1?sNX6K9q6%xZTX z10j;n?G63_u1Ij=fO@KWYyW|As#K}Ro;dbUseSX|;5d#H38{+O8ENMCJo7&G{Z=!( zZw&w>->OdoOag#~U7$UJ!+<8%Mc>sDdgeGHL3;oXVxZvS*xIFSCJY0H0mFb{z%XDK zI0y{jGg~xkkLSKxHKSp`Fz~-*K%5U5>;iKdI|}8|frBgo5QlJDHk7drkTw~OIgK5K z(iDBF>VareqDu^-%yB&<)Dd$UI|`LK5M>TTM<%*LAu>AJWu!WgoI*1i1`Gqc84$Jm z7|cM3Rr}hG`aJ{(d?;W&AK`ohSkdQh&F3|=5SuKI{rumup#+eES(r!tIjACD$5H(L z5@(iBD#LkbLcPz&%RV2Y+NKPPP{asJXjeoZHwIagvwha%@C9Bi#jg(@FudZAF*e7% zS{Rw)OpFF#eJ$|4D4v>nO%jKO4i6u(j#wksz0w+QmtrSwMR_N@BYu@R_iJ6*sV#f7UA5D* zoW+jEXebT5Iz^Scw>?(p?L2R>dL(U2YQaic$*MiMxq0!*)Va*1%hOxuGMk@XMEUZi zD_dJhYdm}5>SA@>Z!-QZ=7EjqqZp2<-`nGNC=KMpb2oHuZQPOJ=Uni^nP6O z9@46v>Qh~$^{?Mfl^+DClYToP-Q5T&WzygL>S{#eN{_tqm2RHR{i^Jdb1E6q>@D z%h^Img{O*#vPdg??Tz9$o4eT+K2m}WSta1Be7Y-XB-tO`j+D!cGn!0ha|?RO!u=t3 zXa#r2BX|N&;W@m5pWzqy1O6hzBuzdf7sw}MhI~$LktMQB8YCbsvO(I)L!x{C&uEVt zNC$<1_up?!mt5Wqdh`eCHF=?+_ki{1Li>l$p8Yhlm(M1z?^`!m+h6>RQpkMH8W>aL zI4n<&5Kk05TXybgc?!PhIR$%CGooSOKr?ViUEQRp|L6Ap{(qozFc)VSFbupK17xUB zDu~zl?gPEwop?RYVE+cYXhN@}P@3Q%RK&b2#}T*xFhn_{*K!&=3ULM7zy2X$AMYRL N`7dvc8UDiz`~!cWd3FE* From 533acce8e212a792a48323d2174955ae7ff4d9d3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:45:02 +0000 Subject: [PATCH 09/26] Initial plan From 7074ea7ca44e0eb5bb6bdbb9b96160a81883a7f5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:45:22 +0000 Subject: [PATCH 10/26] Initial plan From 8d78a4531026972e9162797b18797e966f2b1bd1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:45:42 +0000 Subject: [PATCH 11/26] Initial plan From b1fddb952fee6de0b94f0413ed7797093fac5cb9 Mon Sep 17 00:00:00 2001 From: Luke Pring Date: Fri, 20 Mar 2026 15:45:52 +0000 Subject: [PATCH 12/26] Update stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../com/sharecomparison/presentation/WebPageController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java index 78ff57d..38e313c 100644 --- a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java +++ b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java @@ -1,11 +1,11 @@ package com.sharecomparison.presentation; -import java.time.LocalDate; // adjust package if needed +import java.time.LocalDate; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; -import java.util.Map; // or GetMapping if form is GET +import java.util.Map; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; From 7c66b3e06ceb9643df657e6aec44d980cb2e5b40 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:46:49 +0000 Subject: [PATCH 13/26] Initial plan From 6f94517eca969d82210e2db2ee07c9aaae2b804e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:48:23 +0000 Subject: [PATCH 14/26] Add startDate <= endDate validation in compare endpoint Co-authored-by: lukepring <68820847+lukepring@users.noreply.github.com> --- .../sharecomparison/presentation/WebPageController.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java index 78ff57d..f123bc8 100644 --- a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java +++ b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java @@ -48,6 +48,15 @@ public String compare( symbol1 = symbol1.trim().toUpperCase(); symbol2 = symbol2.trim().toUpperCase(); + if (start.isAfter(end)) { + model.addAttribute("error", "Start date must be on or before end date."); + model.addAttribute("symbol1", symbol1); + model.addAttribute("symbol2", symbol2); + model.addAttribute("startDate", start.toString()); + model.addAttribute("endDate", end.toString()); + return "index"; + } + List data1 = marketDataService.fetchSharePrices(symbol1, start, end); List data2 = marketDataService.fetchSharePrices(symbol2, start, end); From 7812b772557fc00251dd76bec8bff448d045785f Mon Sep 17 00:00:00 2001 From: Luke Pring Date: Fri, 20 Mar 2026 15:48:30 +0000 Subject: [PATCH 15/26] Update stockscope/src/main/java/com/sharecomparison/infrastructure/InMemoryPriceRepository.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../InMemoryPriceRepository.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/stockscope/src/main/java/com/sharecomparison/infrastructure/InMemoryPriceRepository.java b/stockscope/src/main/java/com/sharecomparison/infrastructure/InMemoryPriceRepository.java index 15fae83..21094db 100644 --- a/stockscope/src/main/java/com/sharecomparison/infrastructure/InMemoryPriceRepository.java +++ b/stockscope/src/main/java/com/sharecomparison/infrastructure/InMemoryPriceRepository.java @@ -13,7 +13,24 @@ public class InMemoryPriceRepository implements PriceRepository { @Override public void savePrices(List prices) { - storedPrices.addAll(prices); + for (PriceData newPrice : prices) { + boolean replaced = false; + + for (int i = 0; i < storedPrices.size(); i++) { + PriceData existing = storedPrices.get(i); + + if (existing.getSymbol().equals(newPrice.getSymbol()) + && existing.getDate().equals(newPrice.getDate())) { + storedPrices.set(i, newPrice); + replaced = true; + break; + } + } + + if (!replaced) { + storedPrices.add(newPrice); + } + } } @Override From 9ce7e53abafda302c4a83353031ff810c915b748 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:49:19 +0000 Subject: [PATCH 16/26] Align chart series by date intersection to fix index-based date mismatch Co-authored-by: lukepring <68820847+lukepring@users.noreply.github.com> --- .../presentation/WebPageController.java | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java index 78ff57d..458592d 100644 --- a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java +++ b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java @@ -4,8 +4,11 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; // or GetMapping if form is GET +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -81,7 +84,26 @@ public String compare( if (maxPrice == minPrice) maxPrice += 1.0; double priceRange = maxPrice - minPrice; - int n = Math.min(data1.size(), data2.size()); + // Align series by date: only plot dates present in both data sets + Map map1 = data1.stream() + .collect(Collectors.toMap(PriceData::getDate, p -> p, (existing, replacement) -> existing)); + Map map2 = data2.stream() + .collect(Collectors.toMap(PriceData::getDate, p -> p, (existing, replacement) -> existing)); + + Set commonDates = new LinkedHashSet<>(map1.keySet()); + commonDates.retainAll(map2.keySet()); + List sortedDates = new ArrayList<>(commonDates); + sortedDates.sort(Comparator.naturalOrder()); + + int n = sortedDates.size(); + if (n == 0) { + model.addAttribute("error", "No overlapping trading dates found for the two symbols in this range."); + model.addAttribute("symbol1", symbol1); + model.addAttribute("symbol2", symbol2); + model.addAttribute("startDate", start.toString()); + model.addAttribute("endDate", end.toString()); + return "index"; + } double xStep = n > 1 ? chartWidth / (n - 1) : 0; StringBuilder path1 = new StringBuilder(); @@ -91,16 +113,17 @@ public String compare( for (int i = 0; i < n; i++) { double x = left + i * xStep; + LocalDate date = sortedDates.get(i); - PriceData p1 = data1.get(i); + PriceData p1 = map1.get(date); double y1 = bottom - ((p1.getClosingPrice() - minPrice) / priceRange) * chartHeight; path1.append(i == 0 ? "M" : "L").append(x).append(",").append(y1).append(" "); - points1.add(Map.of("x", x, "y", y1, "value", p1.getClosingPrice())); + points1.add(Map.of("x", x, "y", y1, "value", p1.getClosingPrice(), "date", date.toString())); - PriceData p2 = data2.get(i); + PriceData p2 = map2.get(date); double y2 = bottom - ((p2.getClosingPrice() - minPrice) / priceRange) * chartHeight; path2.append(i == 0 ? "M" : "L").append(x).append(",").append(y2).append(" "); - points2.add(Map.of("x", x, "y", y2, "value", p2.getClosingPrice())); + points2.add(Map.of("x", x, "y", y2, "value", p2.getClosingPrice(), "date", date.toString())); } Map chartData = new HashMap<>(); From dbf480ec15281845a339d4155ef490d45b24ba6e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:49:54 +0000 Subject: [PATCH 17/26] Initial plan From a2b66f1543ec235ff219ed7f98ede5c3357fdf63 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:50:08 +0000 Subject: [PATCH 18/26] Initial plan From 28fb4a514135887bb0709f3f3e95fb97100501c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:50:55 +0000 Subject: [PATCH 19/26] Refactor WebPageController to use IPriceController and ComparisonResult Co-authored-by: lukepring <68820847+lukepring@users.noreply.github.com> --- .../presentation/WebPageController.java | 85 ++----------------- 1 file changed, 8 insertions(+), 77 deletions(-) diff --git a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java index 38e313c..328b35a 100644 --- a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java +++ b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java @@ -1,27 +1,22 @@ package com.sharecomparison.presentation; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; -import com.sharecomparison.application.MarketDataService; -import com.sharecomparison.domain.PriceData; +import com.sharecomparison.application.IPriceController; +import com.sharecomparison.domain.ComparisonResult; @Controller public class WebPageController { - private final MarketDataService marketDataService; + private final IPriceController priceController; - public WebPageController(MarketDataService marketDataService) { - this.marketDataService = marketDataService; + public WebPageController(IPriceController priceController) { + this.priceController = priceController; } @GetMapping("/") @@ -34,7 +29,7 @@ public String showForm(Model model) { return "index"; } - @GetMapping("/compare") + @GetMapping("/compare") public String compare( @RequestParam String symbol1, @RequestParam String symbol2, @@ -48,10 +43,9 @@ public String compare( symbol1 = symbol1.trim().toUpperCase(); symbol2 = symbol2.trim().toUpperCase(); - List data1 = marketDataService.fetchSharePrices(symbol1, start, end); - List data2 = marketDataService.fetchSharePrices(symbol2, start, end); + ComparisonResult result = priceController.comparePrices(symbol1, symbol2, start, end); - if (data1.isEmpty() || data2.isEmpty()) { + if (result.getSymbol1Data().isEmpty() || result.getSymbol2Data().isEmpty()) { model.addAttribute("error", "No data available for one or both symbols in this range."); model.addAttribute("symbol1", symbol1); model.addAttribute("symbol2", symbol2); @@ -60,69 +54,6 @@ public String compare( return "index"; } - data1.sort(Comparator.comparing(PriceData::getDate)); - data2.sort(Comparator.comparing(PriceData::getDate)); - - // Chart coords calculation - double chartWidth = 800, chartHeight = 300; - double left = 80, right = left + chartWidth; - double bottom = 320, top = bottom - chartHeight; - double midY = top + chartHeight / 2; - - double minPrice = Double.MAX_VALUE, maxPrice = Double.MIN_VALUE; - for (PriceData p : data1) { - minPrice = Math.min(minPrice, p.getClosingPrice()); - maxPrice = Math.max(maxPrice, p.getClosingPrice()); - } - for (PriceData p : data2) { - minPrice = Math.min(minPrice, p.getClosingPrice()); - maxPrice = Math.max(maxPrice, p.getClosingPrice()); - } - if (maxPrice == minPrice) maxPrice += 1.0; - double priceRange = maxPrice - minPrice; - - int n = Math.min(data1.size(), data2.size()); - double xStep = n > 1 ? chartWidth / (n - 1) : 0; - - StringBuilder path1 = new StringBuilder(); - StringBuilder path2 = new StringBuilder(); - List> points1 = new ArrayList<>(); - List> points2 = new ArrayList<>(); - - for (int i = 0; i < n; i++) { - double x = left + i * xStep; - - PriceData p1 = data1.get(i); - double y1 = bottom - ((p1.getClosingPrice() - minPrice) / priceRange) * chartHeight; - path1.append(i == 0 ? "M" : "L").append(x).append(",").append(y1).append(" "); - points1.add(Map.of("x", x, "y", y1, "value", p1.getClosingPrice())); - - PriceData p2 = data2.get(i); - double y2 = bottom - ((p2.getClosingPrice() - minPrice) / priceRange) * chartHeight; - path2.append(i == 0 ? "M" : "L").append(x).append(",").append(y2).append(" "); - points2.add(Map.of("x", x, "y", y2, "value", p2.getClosingPrice())); - } - - Map chartData = new HashMap<>(); - chartData.put("left", left); - chartData.put("right", right); - chartData.put("top", top); - chartData.put("bottom", bottom); - chartData.put("midY", midY); - chartData.put("symbol1Path", path1.toString()); - chartData.put("symbol2Path", path2.toString()); - chartData.put("symbol1Points", points1); - chartData.put("symbol2Points", points2); - - Map result = new HashMap<>(); - result.put("symbol1", symbol1); - result.put("symbol2", symbol2); - result.put("startDate", start); - result.put("endDate", end); - result.put("chartData", chartData); - result.put("symbol1Data", data1); - result.put("symbol2Data", data2); - model.addAttribute("result", result); model.addAttribute("symbol1", symbol1); model.addAttribute("symbol2", symbol2); From ae6143425a72d6c500aab9921071fa5fc4233d3e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:53:49 +0000 Subject: [PATCH 20/26] Add cache-first check in AlphaVantageService.fetchSharePrices() Co-authored-by: lukepring <68820847+lukepring@users.noreply.github.com> --- .../sharecomparison/infrastructure/AlphaVantageService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stockscope/src/main/java/com/sharecomparison/infrastructure/AlphaVantageService.java b/stockscope/src/main/java/com/sharecomparison/infrastructure/AlphaVantageService.java index c226b18..3a09484 100644 --- a/stockscope/src/main/java/com/sharecomparison/infrastructure/AlphaVantageService.java +++ b/stockscope/src/main/java/com/sharecomparison/infrastructure/AlphaVantageService.java @@ -55,6 +55,12 @@ public List fetchSharePrices(String symbol, LocalDate startDate, Loca return repository.loadPrices(symbol, startDate, endDate); } + List cached = repository.loadPrices(symbol, startDate, endDate); + if (!cached.isEmpty()) { + log.debug("Returning cached prices for symbol={} ({} to {})", symbol, startDate, endDate); + return cached; + } + String json = fetchTimeSeriesDailyJson(symbol, outputSize); if (json == null) { return repository.loadPrices(symbol, startDate, endDate); From bfc28381ac74a2379607a41210d64014184cc177 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:53:59 +0000 Subject: [PATCH 21/26] Remove redundant WebUI @SpringBootApplication entrypoint Co-authored-by: lukepring <68820847+lukepring@users.noreply.github.com> --- .../java/com/sharecomparison/presentation/WebUI.java | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 stockscope/src/main/java/com/sharecomparison/presentation/WebUI.java diff --git a/stockscope/src/main/java/com/sharecomparison/presentation/WebUI.java b/stockscope/src/main/java/com/sharecomparison/presentation/WebUI.java deleted file mode 100644 index e8f8828..0000000 --- a/stockscope/src/main/java/com/sharecomparison/presentation/WebUI.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sharecomparison.presentation; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication(scanBasePackages = "com.sharecomparison") -public class WebUI { - - public static void main(String[] args) { - SpringApplication.run(WebUI.class, args); - } -} From 902bbb1c5992015e7afc3e21279e724b19bcd0fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:54:21 +0000 Subject: [PATCH 22/26] Fix DateTimeParseException for malformed date query params Co-authored-by: lukepring <68820847+lukepring@users.noreply.github.com> --- .../presentation/WebPageController.java | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java index 78ff57d..fde285d 100644 --- a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java +++ b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java @@ -7,10 +7,13 @@ import java.util.List; import java.util.Map; // or GetMapping if form is GET +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import com.sharecomparison.application.MarketDataService; import com.sharecomparison.domain.PriceData; @@ -38,12 +41,12 @@ public String showForm(Model model) { public String compare( @RequestParam String symbol1, @RequestParam String symbol2, - @RequestParam(required = false) String startDate, - @RequestParam(required = false) String endDate, + @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, + @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate, Model model) { - LocalDate start = (startDate != null && !startDate.isBlank()) ? LocalDate.parse(startDate) : LocalDate.now().minusYears(1); - LocalDate end = (endDate != null && !endDate.isBlank()) ? LocalDate.parse(endDate) : LocalDate.now(); + LocalDate start = (startDate != null) ? startDate : LocalDate.now().minusYears(1); + LocalDate end = (endDate != null) ? endDate : LocalDate.now(); symbol1 = symbol1.trim().toUpperCase(); symbol2 = symbol2.trim().toUpperCase(); @@ -131,4 +134,21 @@ public String compare( return "index"; } + + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public String handleDateParseError(MethodArgumentTypeMismatchException ex, Model model) { + String paramName = ex.getName(); + if (LocalDate.class.equals(ex.getRequiredType()) + && ("startDate".equals(paramName) || "endDate".equals(paramName))) { + model.addAttribute("error", + "Invalid date format for '" + paramName + "'. Please use YYYY-MM-DD (e.g. 2024-01-15)."); + } else { + model.addAttribute("error", "Invalid value for parameter '" + paramName + "'."); + } + model.addAttribute("symbol1", "AAPL"); + model.addAttribute("symbol2", "MSFT"); + model.addAttribute("startDate", LocalDate.now().minusYears(1).toString()); + model.addAttribute("endDate", LocalDate.now().toString()); + return "index"; + } } \ No newline at end of file From 38c290fd589814aad812985f09a75f84e2ec6f27 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 16:05:07 +0000 Subject: [PATCH 23/26] Fix WebPageController merge regression Co-authored-by: lukepring <68820847+lukepring@users.noreply.github.com> --- .../presentation/WebPageController.java | 93 +------------------ 1 file changed, 1 insertion(+), 92 deletions(-) diff --git a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java index 78f6106..9d0f45e 100644 --- a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java +++ b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java @@ -1,14 +1,6 @@ package com.sharecomparison.presentation; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -62,89 +54,6 @@ public String compare( return "index"; } - data1.sort(Comparator.comparing(PriceData::getDate)); - data2.sort(Comparator.comparing(PriceData::getDate)); - - // Chart coords calculation - double chartWidth = 800, chartHeight = 300; - double left = 80, right = left + chartWidth; - double bottom = 320, top = bottom - chartHeight; - double midY = top + chartHeight / 2; - - double minPrice = Double.MAX_VALUE, maxPrice = Double.MIN_VALUE; - for (PriceData p : data1) { - minPrice = Math.min(minPrice, p.getClosingPrice()); - maxPrice = Math.max(maxPrice, p.getClosingPrice()); - } - for (PriceData p : data2) { - minPrice = Math.min(minPrice, p.getClosingPrice()); - maxPrice = Math.max(maxPrice, p.getClosingPrice()); - } - if (maxPrice == minPrice) maxPrice += 1.0; - double priceRange = maxPrice - minPrice; - - // Align series by date: only plot dates present in both data sets - Map map1 = data1.stream() - .collect(Collectors.toMap(PriceData::getDate, p -> p, (existing, replacement) -> existing)); - Map map2 = data2.stream() - .collect(Collectors.toMap(PriceData::getDate, p -> p, (existing, replacement) -> existing)); - - Set commonDates = new LinkedHashSet<>(map1.keySet()); - commonDates.retainAll(map2.keySet()); - List sortedDates = new ArrayList<>(commonDates); - sortedDates.sort(Comparator.naturalOrder()); - - int n = sortedDates.size(); - if (n == 0) { - model.addAttribute("error", "No overlapping trading dates found for the two symbols in this range."); - model.addAttribute("symbol1", symbol1); - model.addAttribute("symbol2", symbol2); - model.addAttribute("startDate", start.toString()); - model.addAttribute("endDate", end.toString()); - return "index"; - } - double xStep = n > 1 ? chartWidth / (n - 1) : 0; - - StringBuilder path1 = new StringBuilder(); - StringBuilder path2 = new StringBuilder(); - List> points1 = new ArrayList<>(); - List> points2 = new ArrayList<>(); - - for (int i = 0; i < n; i++) { - double x = left + i * xStep; - LocalDate date = sortedDates.get(i); - - PriceData p1 = map1.get(date); - double y1 = bottom - ((p1.getClosingPrice() - minPrice) / priceRange) * chartHeight; - path1.append(i == 0 ? "M" : "L").append(x).append(",").append(y1).append(" "); - points1.add(Map.of("x", x, "y", y1, "value", p1.getClosingPrice(), "date", date.toString())); - - PriceData p2 = map2.get(date); - double y2 = bottom - ((p2.getClosingPrice() - minPrice) / priceRange) * chartHeight; - path2.append(i == 0 ? "M" : "L").append(x).append(",").append(y2).append(" "); - points2.add(Map.of("x", x, "y", y2, "value", p2.getClosingPrice(), "date", date.toString())); - } - - Map chartData = new HashMap<>(); - chartData.put("left", left); - chartData.put("right", right); - chartData.put("top", top); - chartData.put("bottom", bottom); - chartData.put("midY", midY); - chartData.put("symbol1Path", path1.toString()); - chartData.put("symbol2Path", path2.toString()); - chartData.put("symbol1Points", points1); - chartData.put("symbol2Points", points2); - - Map result = new HashMap<>(); - result.put("symbol1", symbol1); - result.put("symbol2", symbol2); - result.put("startDate", start); - result.put("endDate", end); - result.put("chartData", chartData); - result.put("symbol1Data", data1); - result.put("symbol2Data", data2); - model.addAttribute("result", result); model.addAttribute("symbol1", symbol1); model.addAttribute("symbol2", symbol2); @@ -153,4 +62,4 @@ public String compare( return "index"; } -} \ No newline at end of file +} From 577066df92eb8688cc45d6ec7ccf290675862f94 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 16:16:53 +0000 Subject: [PATCH 24/26] Fix WebPageController merge regression and keep date validation Co-authored-by: lukepring <68820847+lukepring@users.noreply.github.com> --- .../com/sharecomparison/presentation/WebPageController.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java index 4e2fae2..1801159 100644 --- a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java +++ b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java @@ -21,7 +21,6 @@ public WebPageController(IPriceController priceController) { @GetMapping("/") public String showForm(Model model) { - // Defaults for empty form on first visit model.addAttribute("symbol1", "AAPL"); model.addAttribute("symbol2", "MSFT"); model.addAttribute("startDate", LocalDate.now().minusYears(1).toString()); @@ -52,8 +51,7 @@ public String compare( return "index"; } - List data1 = marketDataService.fetchSharePrices(symbol1, start, end); - List data2 = marketDataService.fetchSharePrices(symbol2, start, end); + ComparisonResult result = priceController.comparePrices(symbol1, symbol2, start, end); if (result.getSymbol1Data().isEmpty() || result.getSymbol2Data().isEmpty()) { model.addAttribute("error", "No data available for one or both symbols in this range."); From 50e2279752d43379e797b532a682575b97828a1e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 16:27:18 +0000 Subject: [PATCH 25/26] Initial plan From 3f588c77ca2fb69cba5df22e5e2eb5719fc2596a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 16:32:37 +0000 Subject: [PATCH 26/26] Merge origin/main into copilot/sub-pr-55 Co-authored-by: lukepring <68820847+lukepring@users.noreply.github.com> --- .gitignore | 4 + .../ArchitecturalCompliance.md | 19 ++ ArchitecturalDesign/BusinessConceptModel.md | 26 +++ ArchitecturalDesign/BusinessTypeModel.md | 9 + ...ME.md => ComponentSpecificationDiagram.md} | 1 + ArchitecturalDesign/DependencyStructure.md | 19 ++ ArchitecturalDesign/UseCase.md | 41 ++++ ArchitecturalDesign/component-diagram.png | Bin 0 -> 202302 bytes CONTRIBUTING.md | 2 +- Meeting-Documentation/Meeting-4.md | 41 ++++ .../application/CompareSharesUseCase.java | 28 +++ .../application/FetchSharePricesUseCase.java | 38 +++ .../application/PriceComparisonService.java | 44 ++-- .../infrastructure/ICacheStore.java | 9 +- .../infrastructure/LocalCacheStore.java | 8 +- .../presentation/WebPageController.java | 1 - .../src/main/resources/templates/index.html | 218 +++++++++++------- 17 files changed, 402 insertions(+), 106 deletions(-) create mode 100644 ArchitecturalDesign/ArchitecturalCompliance.md create mode 100644 ArchitecturalDesign/BusinessConceptModel.md create mode 100644 ArchitecturalDesign/BusinessTypeModel.md rename ArchitecturalDesign/{README.md => ComponentSpecificationDiagram.md} (98%) create mode 100644 ArchitecturalDesign/DependencyStructure.md create mode 100644 ArchitecturalDesign/UseCase.md create mode 100644 ArchitecturalDesign/component-diagram.png create mode 100644 Meeting-Documentation/Meeting-4.md create mode 100644 stockscope/src/main/java/com/sharecomparison/application/CompareSharesUseCase.java create mode 100644 stockscope/src/main/java/com/sharecomparison/application/FetchSharePricesUseCase.java diff --git a/.gitignore b/.gitignore index 667aaef..9212cbc 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,7 @@ build/ ### VS Code ### .vscode/ +.DS_Store +stockscope/src/main/java/com/.DS_Store +stockscope/.DS_Store +stockscope/.DS_Store diff --git a/ArchitecturalDesign/ArchitecturalCompliance.md b/ArchitecturalDesign/ArchitecturalCompliance.md new file mode 100644 index 0000000..77a7d20 --- /dev/null +++ b/ArchitecturalDesign/ArchitecturalCompliance.md @@ -0,0 +1,19 @@ +# Architecture Compliance + +## Overview +The system has been designed using a layered structure based on Clean Architecture principles. This approach was chosen to keep the application organised and to make it easier to maintain and extend as new features are added in later stages of development. + +## Layered Structure +The architecture is divided into four main layers: presentation, application, domain, and infrastructure. The presentation layer is responsible for handling user interaction through the Web UI and passing requests to the controller. The application layer manages the main flow of the system, including retrieving share price data, processing it, and preparing it for comparison. The domain layer represents the core concepts of the system and remains independent from technical concerns. The infrastructure layer handles external operations such as accessing market data and storing cached data locally. + +## Dependency Direction +The system follows the principle that dependencies should flow inward. This means that outer layers depend on inner layers, but inner layers do not depend on outer layers. As a result, the core logic of the system remains independent from frameworks, APIs, and storage mechanisms. This makes the design more stable, as changes in external components do not directly affect the core of the application. + +## Use of Interfaces +Interfaces are used to separate the application logic from the infrastructure layer. The application layer defines the required behaviour through interfaces, while the infrastructure layer provides the concrete implementations. For example, data retrieval and caching are handled through interfaces, allowing different implementations to be used without changing the main logic of the system. + +## Separation of Concerns +The design ensures that responsibilities are clearly separated. Business logic is contained within the service layer and is not mixed with API calls or database operations. This helps to avoid tightly coupled code and makes the system easier to understand and maintain. + +## Summary +Overall, the system follows Clean Architecture principles by organising components into clear layers, controlling the direction of dependencies, and using interfaces to reduce coupling. This results in a design that is flexible, maintainable, and suitable for future development. diff --git a/ArchitecturalDesign/BusinessConceptModel.md b/ArchitecturalDesign/BusinessConceptModel.md new file mode 100644 index 0000000..bea2c27 --- /dev/null +++ b/ArchitecturalDesign/BusinessConceptModel.md @@ -0,0 +1,26 @@ +# Business Concept Model + +## Overview +This diagram shows the main ideas behind the share price comparison app and how they connect. It focuses on what the system does from a user point of view rather than how it is built. + +## Business Concept Model Diagram +image + +## Main Concepts +The centre of the model is the Comparison. This represents the main task in the system where a user compares share prices over time. + +A User can create multiple comparisons which shows that users can analyse different shares whenever they want. + +Each comparison uses a Date Range. This defines the time period for the data being viewed. + +The comparison is carried out on one or two Shares. Each share belongs to a Company which represents the business that owns the stock. + +Each share has a Price History which stores all price data over time. This is made up of multiple Price Records where each record represents the price on a specific day. + +The results are shown using a Chart which displays the comparison in a clear visual way. + +## Relationships +The diagram shows how these concepts are linked. A user requests a comparison, a comparison uses a date range and compares shares, and each share is connected to its company and price data. The chart then displays the final result. + +## Summary +This model gives a clear view of the key concepts in the system and how they relate to each other. It helps explain the problem before moving on to the technical design in later stages. diff --git a/ArchitecturalDesign/BusinessTypeModel.md b/ArchitecturalDesign/BusinessTypeModel.md new file mode 100644 index 0000000..d8c8f51 --- /dev/null +++ b/ArchitecturalDesign/BusinessTypeModel.md @@ -0,0 +1,9 @@ +# Business Type Model + +Business Type Model + +### Notes +- Derived from previous models/diagrams +- Aligned with Java code +- Date ranges must not exceed two years +- Focussed only on domain types, core design only diff --git a/ArchitecturalDesign/README.md b/ArchitecturalDesign/ComponentSpecificationDiagram.md similarity index 98% rename from ArchitecturalDesign/README.md rename to ArchitecturalDesign/ComponentSpecificationDiagram.md index 3a0c6fb..4bc545f 100644 --- a/ArchitecturalDesign/README.md +++ b/ArchitecturalDesign/ComponentSpecificationDiagram.md @@ -7,6 +7,7 @@ image +![Component Specification Diagram](component-diagram.png) ## Architectural Concepts and Their Application diff --git a/ArchitecturalDesign/DependencyStructure.md b/ArchitecturalDesign/DependencyStructure.md new file mode 100644 index 0000000..0733a13 --- /dev/null +++ b/ArchitecturalDesign/DependencyStructure.md @@ -0,0 +1,19 @@ +# Review of Dependency Structure + +## Overview +The dependency structure of the system was reviewed to ensure that components are organised correctly and that dependencies follow a clear and logical direction. The aim was to confirm that the system avoids tight coupling and supports a maintainable design. + +## Dependency Relationships +The system does not contain any circular dependencies between classes or packages. Each component depends only on what is necessary, and the overall flow of control is clear. The user interacts with the UI, which passes requests to the controller. The controller then calls the service layer, and the service layer interacts with external functionality through defined interfaces. This creates a clear and predictable flow through the system. + +## Layer Interaction +The infrastructure layer depends on interfaces defined in the application layer rather than the other way around. This ensures that the core logic of the system is not directly tied to specific implementations. As a result, changes to external systems such as APIs or storage mechanisms can be made without affecting the main application logic. + +## Loose Coupling and Modularity +The structure supports loose coupling by separating responsibilities across layers and using interfaces between them. This means that individual components can be modified or replaced without causing issues in other parts of the system. The package structure reflects this separation, with distinct layers for presentation, application, domain, and infrastructure. + +## Testability and Scalability +The dependency structure also improves testability, as components can be tested independently by using interfaces instead of concrete implementations. It also supports scalability, since new features or components can be added without disrupting the existing system. This makes the design more suitable for future development. + +## Summary +Overall, the dependency structure is clear, logical, and well organised. It avoids unnecessary dependencies, maintains loose coupling, and supports both testing and future growth of the system. diff --git a/ArchitecturalDesign/UseCase.md b/ArchitecturalDesign/UseCase.md new file mode 100644 index 0000000..14e0a30 --- /dev/null +++ b/ArchitecturalDesign/UseCase.md @@ -0,0 +1,41 @@ +# Use Case Model + +## Overview +This use case model describes how the user interacts with the Share Price Comparison Web App. It focuses on the main tasks the user can perform and how the system responds to those actions. The diagram identifies the key use cases and shows how some actions depend on others. + +## Use Case Diagram +image + +## Main Use Cases +The user can retrieve share price data, compare share prices, and view share price charts. These represent the main goals of the user when using the application. + +The system also carries out supporting actions such as storing price data and retrieving cached data. These actions are not directly triggered by the user but are required for the system to function properly and to support offline use. + +## Use Case Description: Compare Share Prices + +### Actor +User + +### Description +This use case allows the user to compare the share prices of one or two companies over a selected date range. The system retrieves the relevant data and presents it in a chart so that the user can easily compare performance over time. + +### Precondition +The user has access to the application and enters valid share symbols and a date range within the allowed limit. + +### Main Flow +1. The user enters one or two share symbols. +2. The system validates the input. +3. The user selects a date range. +4. The system retrieves share price data for the selected shares from an external source. +5. The system stores the retrieved data locally for future use. +6. The system processes the data to prepare it for comparison. +7. The system generates a chart based on the processed data. +8. The system displays the chart to the user. + +### Alternative Flow +If there is no internet connection, the system retrieves previously stored data instead of fetching new data. + +If the user enters invalid share symbols or an invalid date range, the system displays an error message and requests valid input. + +### Postcondition +The user is presented with a chart showing the comparison of share prices over the selected time period, and the data may be stored for later use. diff --git a/ArchitecturalDesign/component-diagram.png b/ArchitecturalDesign/component-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..da4203751346f32d6946da99b59051e105496a87 GIT binary patch literal 202302 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≷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 data1, List data2) { + + ChartData chartData = chartBuilder.buildChart(data1, data2); + + return new ComparisonResult(symbol1, symbol2, startDate, endDate, data1, data2, chartData); + } +} diff --git a/stockscope/src/main/java/com/sharecomparison/application/FetchSharePricesUseCase.java b/stockscope/src/main/java/com/sharecomparison/application/FetchSharePricesUseCase.java new file mode 100644 index 0000000..495923a --- /dev/null +++ b/stockscope/src/main/java/com/sharecomparison/application/FetchSharePricesUseCase.java @@ -0,0 +1,38 @@ +package com.sharecomparison.application; + +import com.sharecomparison.domain.PriceData; +import com.sharecomparison.infrastructure.ICacheStore; +import com.sharecomparison.infrastructure.IMarketDataClient; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.util.List; + +@Component +public class FetchSharePricesUseCase { + + private final ICacheStore cacheStore; + private final IMarketDataClient marketDataClient; + + public FetchSharePricesUseCase(ICacheStore cacheStore, IMarketDataClient marketDataClient) { + this.cacheStore = cacheStore; + this.marketDataClient = marketDataClient; + } + + public List fetch(String symbol, LocalDate startDate, LocalDate endDate) { + String cacheKey = symbol + ":" + startDate + ":" + endDate; + + List cached = cacheStore.load(cacheKey); + if (cached != null && !cached.isEmpty()) { + return cached; + } + + List fetched = marketDataClient.fetch(symbol, startDate, endDate); + + if (fetched != null && !fetched.isEmpty()) { + cacheStore.save(cacheKey, fetched); + } + + return fetched; + } +} diff --git a/stockscope/src/main/java/com/sharecomparison/application/PriceComparisonService.java b/stockscope/src/main/java/com/sharecomparison/application/PriceComparisonService.java index 14cb8fa..aefe521 100644 --- a/stockscope/src/main/java/com/sharecomparison/application/PriceComparisonService.java +++ b/stockscope/src/main/java/com/sharecomparison/application/PriceComparisonService.java @@ -1,10 +1,7 @@ package com.sharecomparison.application; -import com.sharecomparison.domain.ChartData; import com.sharecomparison.domain.ComparisonResult; import com.sharecomparison.domain.PriceData; -import com.sharecomparison.infrastructure.ICacheStore; -import com.sharecomparison.infrastructure.IMarketDataClient; import org.springframework.stereotype.Service; import java.time.LocalDate; @@ -13,29 +10,38 @@ @Service public class PriceComparisonService implements IPriceComparisonService { - private final ICacheStore cacheStore; - private final IMarketDataClient marketDataClient; - private final IChartBuilder chartBuilder; + private final FetchSharePricesUseCase fetchSharePricesUseCase; + private final CompareSharesUseCase compareSharesUseCase; - public PriceComparisonService(ICacheStore cacheStore, - IMarketDataClient marketDataClient, - IChartBuilder chartBuilder) { - this.cacheStore = cacheStore; - this.marketDataClient = marketDataClient; - this.chartBuilder = chartBuilder; + public PriceComparisonService(FetchSharePricesUseCase fetchSharePricesUseCase, + CompareSharesUseCase compareSharesUseCase) { + this.fetchSharePricesUseCase = fetchSharePricesUseCase; + this.compareSharesUseCase = compareSharesUseCase; } @Override public ComparisonResult compare(String symbol1, String symbol2, LocalDate startDate, LocalDate endDate) { - List data1 = marketDataClient.fetch(symbol1, startDate, endDate); - List data2 = marketDataClient.fetch(symbol2, startDate, endDate); + validateDateRange(startDate, endDate); - cacheStore.save(symbol1, data1); - cacheStore.save(symbol2, data2); + List data1 = fetchSharePricesUseCase.fetch(symbol1, startDate, endDate); + List data2 = fetchSharePricesUseCase.fetch(symbol2, startDate, endDate); - ChartData chartData = chartBuilder.buildChart(data1, data2); - return new ComparisonResult(symbol1, symbol2, startDate, endDate, data1, data2, chartData); + return compareSharesUseCase.compare(symbol1, symbol2, startDate, endDate, data1, data2); } -} + + private void validateDateRange(LocalDate start, LocalDate end) { + if (start == null || end == null) { + throw new IllegalArgumentException("Start and End dates must be provided."); + } + + if (start.plusYears(2).isBefore(end)) { + throw new IllegalArgumentException("The maximum date range permitted is two years."); + } + + if (start.isAfter(end)) { + throw new IllegalArgumentException("Start date cannot be after end date."); + } + } +} \ No newline at end of file diff --git a/stockscope/src/main/java/com/sharecomparison/infrastructure/ICacheStore.java b/stockscope/src/main/java/com/sharecomparison/infrastructure/ICacheStore.java index b93f607..56d1f6a 100644 --- a/stockscope/src/main/java/com/sharecomparison/infrastructure/ICacheStore.java +++ b/stockscope/src/main/java/com/sharecomparison/infrastructure/ICacheStore.java @@ -1,8 +1,9 @@ package com.sharecomparison.infrastructure; -public interface ICacheStore { - - void save(String key, Object data); +import com.sharecomparison.domain.PriceData; +import java.util.List; - Object load(String key); +public interface ICacheStore { + void save(String key, List data); + List load(String key); } \ No newline at end of file diff --git a/stockscope/src/main/java/com/sharecomparison/infrastructure/LocalCacheStore.java b/stockscope/src/main/java/com/sharecomparison/infrastructure/LocalCacheStore.java index 2ea0c76..cbb4824 100644 --- a/stockscope/src/main/java/com/sharecomparison/infrastructure/LocalCacheStore.java +++ b/stockscope/src/main/java/com/sharecomparison/infrastructure/LocalCacheStore.java @@ -1,22 +1,24 @@ package com.sharecomparison.infrastructure; +import com.sharecomparison.domain.PriceData; import org.springframework.stereotype.Component; import java.util.HashMap; +import java.util.List; import java.util.Map; @Component public class LocalCacheStore implements ICacheStore { - private final Map cache = new HashMap<>(); + private final Map> cache = new HashMap<>(); @Override - public void save(String key, Object data) { + public void save(String key, List data) { cache.put(key, data); } @Override - public Object load(String key) { + public List load(String key) { return cache.get(key); } } diff --git a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java index cc5a273..a4c2e1e 100644 --- a/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java +++ b/stockscope/src/main/java/com/sharecomparison/presentation/WebPageController.java @@ -91,4 +91,3 @@ public String handleDateParseError(MethodArgumentTypeMismatchException ex, Model return "index"; } } -} diff --git a/stockscope/src/main/resources/templates/index.html b/stockscope/src/main/resources/templates/index.html index aeaa569..695e766 100644 --- a/stockscope/src/main/resources/templates/index.html +++ b/stockscope/src/main/resources/templates/index.html @@ -3,7 +3,7 @@ - Software Architecture and Design + Share Price Comparison -