From 4b0b76e2e587153e6cce92c026352835c48ac096 Mon Sep 17 00:00:00 2001 From: Shayokh144 Date: Fri, 16 Aug 2024 16:41:21 +0700 Subject: [PATCH 1/3] Navigation updated --- .gitignore | 51 ++++++++++++++++ .../UserInterfaceState.xcuserstate | Bin 55373 -> 71177 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 56 ------------------ .../BarChart/BarChartViewModel.swift | 8 ++- .../VisualReport/ContentView.swift | 5 +- .../Graph/SingleBar/SingleBarScreen.swift | 5 +- .../Graph/SingleBar/SingleBarViewModel.swift | 21 +++---- .../ProductSelectionScreen.swift | 45 ++++---------- 8 files changed, 89 insertions(+), 102 deletions(-) create mode 100644 .gitignore delete mode 100644 visualization/app/VisualReport/VisualReport.xcodeproj/xcuserdata/nimble.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..45b606ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Cookie cutter +# to ignore any cookie cutter generated files and directories + +cookiecutter.json +{{ cookiecutter.* + +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +# Extra +visualization/app/VisualReport/VisualReport.xcodeproj/project.xcworkspace/xcuserdata/nimble.xcuserdatad/UserInterfaceState.xcuserstate + +# Extra2 +visualization/app/VisualReport/VisualReport.xcodeproj/xcuserdata/nimble.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist + diff --git a/visualization/app/VisualReport/VisualReport.xcodeproj/project.xcworkspace/xcuserdata/nimble.xcuserdatad/UserInterfaceState.xcuserstate b/visualization/app/VisualReport/VisualReport.xcodeproj/project.xcworkspace/xcuserdata/nimble.xcuserdatad/UserInterfaceState.xcuserstate index 289e1f1b6457fa32c2f273bbc4695b82e8f3b119..0d9878763e447dab6d42b0e36eb10783bc800dbc 100644 GIT binary patch literal 71177 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 $oWR1 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 a_p|O7-LL=GfeaY%KgMVLkN>~B8vYN`@&%s& diff --git a/visualization/app/VisualReport/VisualReport.xcodeproj/xcuserdata/nimble.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/visualization/app/VisualReport/VisualReport.xcodeproj/xcuserdata/nimble.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist deleted file mode 100644 index e6f330de..00000000 --- a/visualization/app/VisualReport/VisualReport.xcodeproj/xcuserdata/nimble.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/visualization/app/VisualReport/VisualReport/BarChart/BarChartViewModel.swift b/visualization/app/VisualReport/VisualReport/BarChart/BarChartViewModel.swift index c53f3073..16801384 100644 --- a/visualization/app/VisualReport/VisualReport/BarChart/BarChartViewModel.swift +++ b/visualization/app/VisualReport/VisualReport/BarChart/BarChartViewModel.swift @@ -65,7 +65,13 @@ final class BarChartViewModel: ObservableObject { var avg = 0.0 barChartUIModel = barChartData.map { barChartData in let paddedName = String(repeating: " ", count: maxValueLength - barChartData.name.count + 1) + barChartData.name - let mappedValue = (barChartData.value - minValue) / (maxValue - minValue) * 90 + 10 + var mappedValue = (barChartData.value - minValue) / (maxValue - minValue) * 90 + 10 + if mappedValue < 0 { + mappedValue = 0 + } + if maxValue == minValue { + mappedValue = 100.0 + } avg += barChartData.value return BarChartData( name: paddedName, diff --git a/visualization/app/VisualReport/VisualReport/ContentView.swift b/visualization/app/VisualReport/VisualReport/ContentView.swift index 65669637..0355dfec 100644 --- a/visualization/app/VisualReport/VisualReport/ContentView.swift +++ b/visualization/app/VisualReport/VisualReport/ContentView.swift @@ -60,7 +60,10 @@ struct ContentView: View { nameView } .navigationDestination(for: CSVDataModel.self) { data in - ProductSelectionScreen(viewModel: ProductSelectionViewModel(csvData: data)) + ProductSelectionScreen(viewModel: ProductSelectionViewModel(csvData: data), navigationPath: $navigationPath) + } + .navigationDestination(for: SingleBarViewModelData.self) { data in + SingleBarScreen(viewModel: SingleBarViewModel(singleBarViewModelData: data)) } } } diff --git a/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarScreen.swift b/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarScreen.swift index 93cea73d..686dde61 100644 --- a/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarScreen.swift +++ b/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarScreen.swift @@ -13,9 +13,9 @@ struct SingleBarScreen: View { private var barChartView: some View { let barChartViewModel = BarChartViewModel( - chartName: "Price chart for \(viewModel.productName)", + chartName: "Price chart for \(viewModel.singleBarViewModelData.productName)", timeFrame: viewModel.timeframe, - weightUnitText: "Weight unit: \(viewModel.quantity) \(viewModel.quantityUnit)", + weightUnitText: "Weight unit: \(viewModel.singleBarViewModelData.quantity) \(viewModel.quantityUnit)", currencyUnitText: "Currency unit: BDT(৳)", dataSource: "Data collected from ChalDal.com", xAxisName: "Timestamp", @@ -27,6 +27,7 @@ struct SingleBarScreen: View { var body: some View { barChartView + .navigationTitle("Single Bar") } init(viewModel: SingleBarViewModel) { diff --git a/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarViewModel.swift b/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarViewModel.swift index 963662e6..a76cd58d 100644 --- a/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarViewModel.swift +++ b/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarViewModel.swift @@ -7,27 +7,28 @@ import Foundation -final class SingleBarViewModel: ObservableObject { +struct SingleBarViewModelData: Hashable { - + let csvData: CSVDataModel let productName: String let quantity: Double +} + +final class SingleBarViewModel: ObservableObject { @Published private(set) var barChartData: [BarChartData] = [] @Published private(set) var timeframe: String = "" @Published private(set) var quantityUnit: String = "" - private let csvData: CSVDataModel - init(csvData: CSVDataModel, productName: String, quantity: Double) { - print("pr name: \(productName) qt: \(quantity)") - self.csvData = csvData - self.productName = productName - self.quantity = quantity + let singleBarViewModelData: SingleBarViewModelData + + init(singleBarViewModelData: SingleBarViewModelData) { + self.singleBarViewModelData = singleBarViewModelData populateData() } private func populateData() { - let rows = csvData.rows + let rows = singleBarViewModelData.csvData.rows barChartData.removeAll() quantityUnit = "" for row in rows { @@ -35,7 +36,7 @@ final class SingleBarViewModel: ObservableObject { let quantityValue = Double(row["weight_value"] ?? "0") ?? 0 let price = Int(row["price"] ?? "0") ?? 0 let dateStr = DateFormatter.dayMonthYear.date(from: row["date"] ?? "") - if let date = dateStr, name == productName, quantity == quantityValue { + if let date = dateStr, name == singleBarViewModelData.productName, singleBarViewModelData.quantity == quantityValue { let dataName = DateFormatter.dayMonthYearShort.string(from: date) barChartData.append(BarChartData(name: dataName, value: Double(price))) if quantityUnit.isEmpty { diff --git a/visualization/app/VisualReport/VisualReport/ProductSelection/ProductSelectionScreen.swift b/visualization/app/VisualReport/VisualReport/ProductSelection/ProductSelectionScreen.swift index 7a4db2e8..2c20aede 100644 --- a/visualization/app/VisualReport/VisualReport/ProductSelection/ProductSelectionScreen.swift +++ b/visualization/app/VisualReport/VisualReport/ProductSelection/ProductSelectionScreen.swift @@ -11,6 +11,7 @@ struct ProductSelectionScreen: View { @StateObject private var viewModel: ProductSelectionViewModel @State private var showDetails = false + @Binding private var navigationPath: NavigationPath private var nameView: some View { ScrollView(.vertical) { @@ -18,6 +19,15 @@ struct ProductSelectionScreen: View { Button( action: { showDetails = viewModel.selectProduct(givenName: data.name) + if showDetails { + navigationPath.append( + SingleBarViewModelData( + csvData: viewModel.csvData, + productName: viewModel.selectedName, + quantity: viewModel.selectedQuantity ?? 0.0 + ) + ) + } }, label: { HStack(spacing: 16.0) { @@ -34,46 +44,17 @@ struct ProductSelectionScreen: View { } } - private var detailsView: some View { - SingleBarScreen( - viewModel: SingleBarViewModel( - csvData: viewModel.csvData, - productName: viewModel.selectedName, - quantity: viewModel.selectedQuantity ?? 0.0 - ) - ) - } - - private var backButton: some View { - HStack { - Button( - action: { - showDetails.toggle() - }, - label: { - Text("< Back") - .font(.system(size: 16.0)) - } - ) - Spacer() - } - .padding([.top, .leading]) - } - var body: some View { VStack(alignment: .leading) { - if showDetails { - backButton - detailsView - } nameView - .opacity(showDetails ? 0.0 : 1.0 ) } + .navigationTitle("Select Product") .padding() } - init(viewModel: ProductSelectionViewModel) { + init(viewModel: ProductSelectionViewModel, navigationPath: Binding) { _viewModel = StateObject(wrappedValue: viewModel) + _navigationPath = navigationPath } } From 9750664f9c5125a7b8c2a388fb87e2e43ca33ca7 Mon Sep 17 00:00:00 2001 From: Shayokh144 Date: Mon, 19 Aug 2024 17:00:07 +0700 Subject: [PATCH 2/3] Line chart added --- .../VisualReport.xcodeproj/project.pbxproj | 24 +++ .../VisualReport/BarChart/BarChartView.swift | 20 --- .../DataModel/LineChartDataModel.swift | 34 +++++ .../Extension/Double+Extension.swift | 17 +++ .../Graph/LineChart/LineChartScreen.swift | 140 ++++++++++++++++++ .../Graph/LineChart/LineChartViewModel.swift | 70 +++++++++ .../Graph/SingleBar/SingleBarScreen.swift | 23 ++- .../Graph/SingleBar/SingleBarViewModel.swift | 37 +++++ 8 files changed, 332 insertions(+), 33 deletions(-) create mode 100644 visualization/app/VisualReport/VisualReport/DataModel/LineChartDataModel.swift create mode 100644 visualization/app/VisualReport/VisualReport/Extension/Double+Extension.swift create mode 100644 visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartScreen.swift create mode 100644 visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartViewModel.swift diff --git a/visualization/app/VisualReport/VisualReport.xcodeproj/project.pbxproj b/visualization/app/VisualReport/VisualReport.xcodeproj/project.pbxproj index 96e71ec5..02a1d8be 100644 --- a/visualization/app/VisualReport/VisualReport.xcodeproj/project.pbxproj +++ b/visualization/app/VisualReport/VisualReport.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 043E74DE2BABF81700B9AF91 /* BarChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043E74DD2BABF81700B9AF91 /* BarChartView.swift */; }; 043E74E02BABF81F00B9AF91 /* BarChartViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043E74DF2BABF81F00B9AF91 /* BarChartViewModel.swift */; }; 043E74E32BAC210E00B9AF91 /* SwiftCSV in Frameworks */ = {isa = PBXBuildFile; productRef = 043E74E22BAC210E00B9AF91 /* SwiftCSV */; }; + 043EE2FE2C6F5E0B00F60278 /* LineChartScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043EE2FD2C6F5E0B00F60278 /* LineChartScreen.swift */; }; 048B86C12C67D6CB008036D4 /* ProductSelectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B86C02C67D6CB008036D4 /* ProductSelectionScreen.swift */; }; 048B86C32C67D6D8008036D4 /* ProductSelectionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B86C22C67D6D8008036D4 /* ProductSelectionViewModel.swift */; }; 048B86C62C67E6D0008036D4 /* CSVDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B86C52C67E6D0008036D4 /* CSVDataModel.swift */; }; @@ -21,6 +22,9 @@ 048B86CC2C67E9CB008036D4 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B86CB2C67E9CB008036D4 /* DateFormatter+Extension.swift */; }; 048B86D02C6B58D6008036D4 /* SingleBarScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B86CF2C6B58D6008036D4 /* SingleBarScreen.swift */; }; 048B86D22C6B58E9008036D4 /* SingleBarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048B86D12C6B58E9008036D4 /* SingleBarViewModel.swift */; }; + 04DB73752C725FDF0038AEFD /* LineChartDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DB73742C725FDF0038AEFD /* LineChartDataModel.swift */; }; + 04DB73772C7265300038AEFD /* LineChartViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DB73762C7265300038AEFD /* LineChartViewModel.swift */; }; + 04DB73792C7346140038AEFD /* Double+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DB73782C7346140038AEFD /* Double+Extension.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -32,6 +36,7 @@ 043E74D62BABF77800B9AF91 /* VisualReport.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = VisualReport.entitlements; sourceTree = ""; }; 043E74DD2BABF81700B9AF91 /* BarChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartView.swift; sourceTree = ""; }; 043E74DF2BABF81F00B9AF91 /* BarChartViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartViewModel.swift; sourceTree = ""; }; + 043EE2FD2C6F5E0B00F60278 /* LineChartScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineChartScreen.swift; sourceTree = ""; }; 048B86C02C67D6CB008036D4 /* ProductSelectionScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductSelectionScreen.swift; sourceTree = ""; }; 048B86C22C67D6D8008036D4 /* ProductSelectionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductSelectionViewModel.swift; sourceTree = ""; }; 048B86C52C67E6D0008036D4 /* CSVDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CSVDataModel.swift; sourceTree = ""; }; @@ -39,6 +44,9 @@ 048B86CB2C67E9CB008036D4 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = ""; }; 048B86CF2C6B58D6008036D4 /* SingleBarScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleBarScreen.swift; sourceTree = ""; }; 048B86D12C6B58E9008036D4 /* SingleBarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleBarViewModel.swift; sourceTree = ""; }; + 04DB73742C725FDF0038AEFD /* LineChartDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineChartDataModel.swift; sourceTree = ""; }; + 04DB73762C7265300038AEFD /* LineChartViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineChartViewModel.swift; sourceTree = ""; }; + 04DB73782C7346140038AEFD /* Double+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Extension.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -104,6 +112,15 @@ path = BarChart; sourceTree = ""; }; + 043EE2FC2C6F5DF600F60278 /* LineChart */ = { + isa = PBXGroup; + children = ( + 043EE2FD2C6F5E0B00F60278 /* LineChartScreen.swift */, + 04DB73762C7265300038AEFD /* LineChartViewModel.swift */, + ); + path = LineChart; + sourceTree = ""; + }; 048B86BF2C67D630008036D4 /* ProductSelection */ = { isa = PBXGroup; children = ( @@ -117,6 +134,7 @@ isa = PBXGroup; children = ( 048B86C52C67E6D0008036D4 /* CSVDataModel.swift */, + 04DB73742C725FDF0038AEFD /* LineChartDataModel.swift */, ); path = DataModel; sourceTree = ""; @@ -133,6 +151,7 @@ isa = PBXGroup; children = ( 048B86CB2C67E9CB008036D4 /* DateFormatter+Extension.swift */, + 04DB73782C7346140038AEFD /* Double+Extension.swift */, ); path = Extension; sourceTree = ""; @@ -140,6 +159,7 @@ 048B86CD2C6B58B7008036D4 /* Graph */ = { isa = PBXGroup; children = ( + 043EE2FC2C6F5DF600F60278 /* LineChart */, 048B86CE2C6B58BE008036D4 /* SingleBar */, ); path = Graph; @@ -233,14 +253,18 @@ 048B86C32C67D6D8008036D4 /* ProductSelectionViewModel.swift in Sources */, 043E74D02BABF77700B9AF91 /* ContentView.swift in Sources */, 048B86C12C67D6CB008036D4 /* ProductSelectionScreen.swift in Sources */, + 04DB73752C725FDF0038AEFD /* LineChartDataModel.swift in Sources */, 048B86D22C6B58E9008036D4 /* SingleBarViewModel.swift in Sources */, 048B86D02C6B58D6008036D4 /* SingleBarScreen.swift in Sources */, 048B86C62C67E6D0008036D4 /* CSVDataModel.swift in Sources */, + 043EE2FE2C6F5E0B00F60278 /* LineChartScreen.swift in Sources */, + 04DB73792C7346140038AEFD /* Double+Extension.swift in Sources */, 043E74E02BABF81F00B9AF91 /* BarChartViewModel.swift in Sources */, 048B86CC2C67E9CB008036D4 /* DateFormatter+Extension.swift in Sources */, 048B86C92C67E75C008036D4 /* CSVFileReader.swift in Sources */, 043E74CE2BABF77700B9AF91 /* VisualReportApp.swift in Sources */, 043E74DE2BABF81700B9AF91 /* BarChartView.swift in Sources */, + 04DB73772C7265300038AEFD /* LineChartViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/visualization/app/VisualReport/VisualReport/BarChart/BarChartView.swift b/visualization/app/VisualReport/VisualReport/BarChart/BarChartView.swift index 4c375154..2f0b7422 100644 --- a/visualization/app/VisualReport/VisualReport/BarChart/BarChartView.swift +++ b/visualization/app/VisualReport/VisualReport/BarChart/BarChartView.swift @@ -140,23 +140,3 @@ struct BarChartData: Identifiable { self.calculatedValue = calculatedValue } } - -extension BarChartData { - - static var dummyList: [BarChartData] { - [ - .init(name: "January", value: 160), - .init(name: "February", value: 140), - .init(name: "March", value: 130), - .init(name: "April", value: 180), - .init(name: "May", value: 160), - .init(name: "June", value: 190), - .init(name: "July", value: 260), - .init(name: "August", value: 460), - .init(name: "September", value: 177), - .init(name: "October", value: 199), - .init(name: "November", value: 160), - .init(name: "December", value: 80) - ] - } -} diff --git a/visualization/app/VisualReport/VisualReport/DataModel/LineChartDataModel.swift b/visualization/app/VisualReport/VisualReport/DataModel/LineChartDataModel.swift new file mode 100644 index 00000000..e3b55e16 --- /dev/null +++ b/visualization/app/VisualReport/VisualReport/DataModel/LineChartDataModel.swift @@ -0,0 +1,34 @@ +// +// LineChartDataModel.swift +// VisualReport +// +// Created by Taher's nimble macbook on 18/8/24. +// + +import Foundation + +struct LineChartDataSeries: Identifiable { + + let id = UUID() + let type: String + var firstDate: Date + var lastDate: Date + var lineChartDataList: [LineChartDataModel] + var ruleMarkDataList: [RuleMarkDataModel] +} + + +struct LineChartDataModel: Identifiable { + + var id: Int + let xTimeValue: Date + let yValue: Double +} + +struct RuleMarkDataModel: Identifiable { + + let id: Int + let yValue: Double + let yName: String + let ruleMarkName: String +} diff --git a/visualization/app/VisualReport/VisualReport/Extension/Double+Extension.swift b/visualization/app/VisualReport/VisualReport/Extension/Double+Extension.swift new file mode 100644 index 00000000..85c73fc8 --- /dev/null +++ b/visualization/app/VisualReport/VisualReport/Extension/Double+Extension.swift @@ -0,0 +1,17 @@ +// +// Double+Extension.swift +// VisualReport +// +// Created by Taher's nimble macbook on 19/8/24. +// + +extension Double { + + var fractionTwoDigitString:String { + return String(format: "%.2f", self) + } + + var fractionOneDigitString:String { + return String(format: "%.1f", self) + } +} diff --git a/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartScreen.swift b/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartScreen.swift new file mode 100644 index 00000000..3449f3e4 --- /dev/null +++ b/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartScreen.swift @@ -0,0 +1,140 @@ +// +// LineChartScreen.swift +// VisualReport +// +// Created by Taher's nimble macbook on 16/8/24. +// + +import Charts +import SwiftUI + +struct LineChartScreen: View { + + @StateObject private var viewModel: LineChartViewModel + + @ViewBuilder private var chartView: some View { + if let firstDate = viewModel.uiFirstDate, + let lastDate = viewModel.uiLastDate { + Chart($viewModel.uiModels) { $dataSeries in + ForEach($dataSeries.lineChartDataList) { $data in + LineMark(x: .value("Date", data.xTimeValue, unit: .day), + y: .value("Price", data.yValue)) + } + .foregroundStyle(by: .value("Chart type", dataSeries.type)) + ForEach($dataSeries.ruleMarkDataList) { $data in + RuleMark(y: .value(data.yName, data.yValue)) + .annotation(position: .bottom, + alignment: .bottomLeading) { + Text("\(data.ruleMarkName): \(data.yValue.fractionTwoDigitString)") + .foregroundColor(.orange) + } + } + } + .chartXScale( + domain: firstDate...lastDate + ) + .aspectRatio(1, contentMode: .fit) + } + } + + private var topSection: some View { + VStack(alignment: .center, spacing: .zero) { + Text(viewModel.viewModelData.chartName) + .foregroundStyle(Color.purple) + .font(.system(size: 32.0, weight: .bold)) + .padding(.bottom, 8.0) + Text(viewModel.viewModelData.timeFrame) + .foregroundStyle(Color.green) + .font(.system(size: 22.0, weight: .bold)) + .padding(.bottom, 24.0) + HStack { + Text(viewModel.viewModelData.weightUnitText) + Spacer() + Text(viewModel.viewModelData.currencyUnitText) + Spacer() + Text(viewModel.viewModelData.dataSource) + } + .font(.system(size: 14.0, weight: .bold)) + .foregroundStyle(Color.purple) + } + .padding(.bottom, 24.0) + } + + var body: some View { + VStack { + topSection + chartView + } + .padding() + .onAppear { + viewModel.createDataModel() + } + } + + init(viewModel: LineChartViewModel) { + _viewModel = StateObject(wrappedValue: viewModel) + } +} + +//struct LineChartScreen: View { +// +// var newData = [PetDataModel]() +// +// var data: [PetDataSeries] { +// [PetDataSeries(type: "ABC", petData: newData)] +// } +// +// let firstDate: Date +// let lastDate: Date +// var body: some View { +// Chart(data, id: \.type) { dataSeries in +// ForEach(dataSeries.petData) { data in +// LineMark(x: .value("Year", data.year, unit: .day), +// y: .value("Population", data.population)) +// } +// .foregroundStyle(by: .value("Pet type", dataSeries.type)) +// .symbol(by: .value("Pet type", dataSeries.type)) +// RuleMark(y: .value("Average 1", 1.5)) +// .annotation(position: .bottom, +// alignment: .bottomLeading) { +// Text("average 1.5") +// .foregroundColor(.orange) +// } +// RuleMark(y: .value("Average 2", 2.5)) +// .annotation(position: .bottom, +// alignment: .bottomLeading) { +// Text("average 2.5") +// .foregroundColor(.orange) +// } +// } +// .chartXScale(domain: firstDate...lastDate) +// .aspectRatio(1, contentMode: .fit) +// .padding() +// } +// +// init() { +// for i in 0 ..< 10 { +// let date = Calendar.current.date(byAdding: .day, value: i, to: .now) ?? .now +// let rep = Double.random(in: 10...100) +// newData.append( +// .init(year: date, population: rep) +// ) +// } +// firstDate = newData[0].year +// lastDate = newData[9].year +// } +//} +// +//struct PetDataSeries: Identifiable { +// let type: String +// let petData: [PetDataModel] +// var id: String { type } +//} +// +// +//struct PetDataModel: Identifiable { +// +// let id = UUID() +// let year: Date +// let population: Double +//} diff --git a/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartViewModel.swift b/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartViewModel.swift new file mode 100644 index 00000000..ad90ae2c --- /dev/null +++ b/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartViewModel.swift @@ -0,0 +1,70 @@ +// +// LineChartViewModel.swift +// VisualReport +// +// Created by Taher's nimble macbook on 19/8/24. +// + +import Foundation + +final class LineChartViewModel: ObservableObject { + + @Published var uiModels: [LineChartDataSeries] + @Published var uiFirstDate: Date? + @Published var uiLastDate: Date? + let viewModelData: LineChartViewModelData + + init(lineChartViewModelData: LineChartViewModelData) { + uiModels = [] + viewModelData = lineChartViewModelData + } + + func createDataModel() { + guard uiModels.isEmpty else { + return + } + guard let firstDate = viewModelData.lineChartDataList.first?.xTimeValue, + let lastDate = viewModelData.lineChartDataList.last?.xTimeValue else { + return + } + uiFirstDate = firstDate + uiLastDate = lastDate + let ruleMarks = getRuleMarkList( + chartDataPoints: viewModelData.lineChartDataList + ) + let uiModel = LineChartDataSeries( + type: "All year", + firstDate: firstDate, + lastDate: lastDate, + lineChartDataList: viewModelData.lineChartDataList, + ruleMarkDataList: ruleMarks + ) + uiModels = [uiModel] + } + + private func getRuleMarkList( + chartDataPoints: [LineChartDataModel] + ) -> [RuleMarkDataModel] { + let avgValue: Double = chartDataPoints.reduce(0.0) { + return $1.yValue + $0 + } / Double(chartDataPoints.count) + return [ + RuleMarkDataModel( + id: 1, + yValue: avgValue, + yName: "Average price:", + ruleMarkName: "Average price" + ) + ] + } +} + +struct LineChartViewModelData { + + let chartName: String + let timeFrame: String + let weightUnitText: String + let currencyUnitText: String + let dataSource: String + let lineChartDataList: [LineChartDataModel] +} diff --git a/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarScreen.swift b/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarScreen.swift index 686dde61..7ed8c980 100644 --- a/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarScreen.swift +++ b/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarScreen.swift @@ -11,23 +11,20 @@ struct SingleBarScreen: View { @StateObject private var viewModel: SingleBarViewModel + private var barChartView: some View { - let barChartViewModel = BarChartViewModel( - chartName: "Price chart for \(viewModel.singleBarViewModelData.productName)", - timeFrame: viewModel.timeframe, - weightUnitText: "Weight unit: \(viewModel.singleBarViewModelData.quantity) \(viewModel.quantityUnit)", - currencyUnitText: "Currency unit: BDT(৳)", - dataSource: "Data collected from ChalDal.com", - xAxisName: "Timestamp", - yAxisName: "Price", - barChartData: viewModel.barChartData - ) - return BarChartView(viewModel: barChartViewModel) + return BarChartView(viewModel: viewModel.barChartViewModel) } var body: some View { - barChartView - .navigationTitle("Single Bar") + TabView { + barChartView + .navigationTitle("Single Bar") + .tabItem { Text("Bar") } + LineChartScreen(viewModel: viewModel.lineChartViewModel) + .navigationTitle("Line chart") + .tabItem { Text("Line Chart") } + } } init(viewModel: SingleBarViewModel) { diff --git a/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarViewModel.swift b/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarViewModel.swift index a76cd58d..aa649049 100644 --- a/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarViewModel.swift +++ b/visualization/app/VisualReport/VisualReport/Graph/SingleBar/SingleBarViewModel.swift @@ -21,16 +21,45 @@ final class SingleBarViewModel: ObservableObject { @Published private(set) var quantityUnit: String = "" let singleBarViewModelData: SingleBarViewModelData + private var lineChartDataModelList: [LineChartDataModel] = [] init(singleBarViewModelData: SingleBarViewModelData) { self.singleBarViewModelData = singleBarViewModelData populateData() } + var barChartViewModel: BarChartViewModel { + BarChartViewModel( + chartName: "Price chart for \(singleBarViewModelData.productName)", + timeFrame: timeframe, + weightUnitText: "Weight unit: \(singleBarViewModelData.quantity) \(quantityUnit)", + currencyUnitText: "Currency unit: BDT(৳)", + dataSource: "Data collected from ChalDal.com", + xAxisName: "Timestamp", + yAxisName: "Price", + barChartData: barChartData + ) + } + + var lineChartViewModel: LineChartViewModel { + LineChartViewModel( + lineChartViewModelData: LineChartViewModelData( + chartName: "Price chart for \(singleBarViewModelData.productName)", + timeFrame: timeframe, + weightUnitText: "Weight unit: \(singleBarViewModelData.quantity) \(quantityUnit)", + currencyUnitText: "Currency unit: BDT(৳)", + dataSource: "Data collected from ChalDal.com", + lineChartDataList: lineChartDataModelList + ) + ) + } + private func populateData() { let rows = singleBarViewModelData.csvData.rows barChartData.removeAll() + lineChartDataModelList.removeAll() quantityUnit = "" + var index = 0 for row in rows { let name = row["product_name"] let quantityValue = Double(row["weight_value"] ?? "0") ?? 0 @@ -39,6 +68,14 @@ final class SingleBarViewModel: ObservableObject { if let date = dateStr, name == singleBarViewModelData.productName, singleBarViewModelData.quantity == quantityValue { let dataName = DateFormatter.dayMonthYearShort.string(from: date) barChartData.append(BarChartData(name: dataName, value: Double(price))) + lineChartDataModelList.append( + .init( + id: index, + xTimeValue: date, + yValue: Double(price) + ) + ) + index += 1 if quantityUnit.isEmpty { quantityUnit = row["weight_unit"] ?? "" } From 364873ce26015b876194b8b15cc509100d81401d Mon Sep 17 00:00:00 2001 From: Shayokh144 Date: Mon, 19 Aug 2024 23:33:35 +0700 Subject: [PATCH 3/3] Remove unused code, update line chart --- .../Extension/DateFormatter+Extension.swift | 7 ++ .../Graph/LineChart/LineChartScreen.swift | 82 ++++--------------- .../Graph/LineChart/LineChartViewModel.swift | 23 ++++++ 3 files changed, 47 insertions(+), 65 deletions(-) diff --git a/visualization/app/VisualReport/VisualReport/Extension/DateFormatter+Extension.swift b/visualization/app/VisualReport/VisualReport/Extension/DateFormatter+Extension.swift index 879beea9..10be5fc9 100644 --- a/visualization/app/VisualReport/VisualReport/Extension/DateFormatter+Extension.swift +++ b/visualization/app/VisualReport/VisualReport/Extension/DateFormatter+Extension.swift @@ -29,4 +29,11 @@ extension DateFormatter { dateFormatter.dateFormat = "yyyy-MM" return dateFormatter }() + + public static let monthYearShort: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.calendar = Calendar(identifier: .gregorian) + dateFormatter.dateFormat = "yy-MM" + return dateFormatter + }() } diff --git a/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartScreen.swift b/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartScreen.swift index 3449f3e4..b532f98f 100644 --- a/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartScreen.swift +++ b/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartScreen.swift @@ -9,7 +9,6 @@ import Charts import SwiftUI struct LineChartScreen: View { - @StateObject private var viewModel: LineChartViewModel @ViewBuilder private var chartView: some View { @@ -23,6 +22,8 @@ struct LineChartScreen: View { .foregroundStyle(by: .value("Chart type", dataSeries.type)) ForEach($dataSeries.ruleMarkDataList) { $data in RuleMark(y: .value(data.yName, data.yValue)) + .foregroundStyle(Color.orange) + .lineStyle(StrokeStyle(lineWidth: 1)) .annotation(position: .bottom, alignment: .bottomLeading) { Text("\(data.ruleMarkName): \(data.yValue.fractionTwoDigitString)") @@ -30,10 +31,24 @@ struct LineChartScreen: View { } } } + .chartYAxis{ + AxisMarks(position: .leading, values: viewModel.chartYAxisValues) + } + .chartXAxis{ + AxisMarks(position: .bottom, values: viewModel.chartXAxisValues) { value in + AxisGridLine() + AxisTick() + AxisValueLabel() { + if let date = value.as(Date.self) { + Text(DateFormatter.monthYearShort.string(from: date)) + } + } + } + } .chartXScale( domain: firstDate...lastDate ) - .aspectRatio(1, contentMode: .fit) + .frame(maxWidth: .infinity, maxHeight: .infinity) } } @@ -75,66 +90,3 @@ struct LineChartScreen: View { _viewModel = StateObject(wrappedValue: viewModel) } } - -//struct LineChartScreen: View { -// -// var newData = [PetDataModel]() -// -// var data: [PetDataSeries] { -// [PetDataSeries(type: "ABC", petData: newData)] -// } -// -// let firstDate: Date -// let lastDate: Date -// var body: some View { -// Chart(data, id: \.type) { dataSeries in -// ForEach(dataSeries.petData) { data in -// LineMark(x: .value("Year", data.year, unit: .day), -// y: .value("Population", data.population)) -// } -// .foregroundStyle(by: .value("Pet type", dataSeries.type)) -// .symbol(by: .value("Pet type", dataSeries.type)) -// RuleMark(y: .value("Average 1", 1.5)) -// .annotation(position: .bottom, -// alignment: .bottomLeading) { -// Text("average 1.5") -// .foregroundColor(.orange) -// } -// RuleMark(y: .value("Average 2", 2.5)) -// .annotation(position: .bottom, -// alignment: .bottomLeading) { -// Text("average 2.5") -// .foregroundColor(.orange) -// } -// } -// .chartXScale(domain: firstDate...lastDate) -// .aspectRatio(1, contentMode: .fit) -// .padding() -// } -// -// init() { -// for i in 0 ..< 10 { -// let date = Calendar.current.date(byAdding: .day, value: i, to: .now) ?? .now -// let rep = Double.random(in: 10...100) -// newData.append( -// .init(year: date, population: rep) -// ) -// } -// firstDate = newData[0].year -// lastDate = newData[9].year -// } -//} -// -//struct PetDataSeries: Identifiable { -// let type: String -// let petData: [PetDataModel] -// var id: String { type } -//} -// -// -//struct PetDataModel: Identifiable { -// -// let id = UUID() -// let year: Date -// let population: Double -//} diff --git a/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartViewModel.swift b/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartViewModel.swift index ad90ae2c..6f611e93 100644 --- a/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartViewModel.swift +++ b/visualization/app/VisualReport/VisualReport/Graph/LineChart/LineChartViewModel.swift @@ -12,6 +12,8 @@ final class LineChartViewModel: ObservableObject { @Published var uiModels: [LineChartDataSeries] @Published var uiFirstDate: Date? @Published var uiLastDate: Date? + @Published var chartYAxisValues: [Int] = [] + @Published var chartXAxisValues: [Date] = [] let viewModelData: LineChartViewModelData init(lineChartViewModelData: LineChartViewModelData) { @@ -40,6 +42,27 @@ final class LineChartViewModel: ObservableObject { ruleMarkDataList: ruleMarks ) uiModels = [uiModel] + + // Chart Data + let maxValue = Int(viewModelData.lineChartDataList.map { $0.yValue }.max() ?? 0.0) + 20 + var minValue = Int(viewModelData.lineChartDataList.map { $0.yValue }.min() ?? 0.0) - 20 + if minValue < 0 { + minValue = 0 + } + chartYAxisValues = stride(from: minValue, to: maxValue, by: 20).map { $0 } + + chartXAxisValues.removeAll() + var currentDate = firstDate + let calendar = Calendar.current + let dayInterval = Int(viewModelData.lineChartDataList.count / 10) + while currentDate <= lastDate { + chartXAxisValues.append(currentDate) + if let nextDate = calendar.date(byAdding: .day, value: dayInterval, to: currentDate) { + currentDate = nextDate + } else { + break + } + } } private func getRuleMarkList(