diff --git a/KissMeConsole/Sources/KissConsole+DB.swift b/KissMeConsole/Sources/KissConsole+DB.swift index 7fc96a4..e7e8709 100644 --- a/KissMeConsole/Sources/KissConsole+DB.swift +++ b/KissMeConsole/Sources/KissConsole+DB.swift @@ -220,26 +220,44 @@ class CandleMinuteFileName { extension KissConsole { - func collectCandleMinuteFiles() -> [String: [URL]] { - guard let enumerator = FileManager.subPathFiles("data") else { + func subPathForProduct(productNo: String?) -> FileManager.DirectoryEnumerator? { + if let productNo = productNo { + return FileManager.subPathFiles("data/\(productNo)") + } + else { + return FileManager.subPathFiles("data") + } + } + + func collectCandleMinuteFiles(productNo: String?, year: String?, month: String?) -> [String: [URL]] { + guard let enumerator = subPathForProduct(productNo: productNo) else { return [:] } let candleMinName = CandleMinuteFileName() - var allCandles = [String: [URL]]() + var candleFiles = [String: [URL]]() for case let fileUrl as URL in enumerator { - guard let (productNo, _) = candleMinName.matchedUrl(fileUrl.path) else { + guard let (fileProductNo, yyyyMMdd) = candleMinName.matchedUrl(fileUrl.path) else { + continue + } + if let productNo = productNo, productNo != fileProductNo { + continue + } + if let year = year, yyyyMMdd.prefix(4) != year { + continue + } + if let month = month, yyyyMMdd.dropFirst(4).prefix(2) != month { continue } - if allCandles.keys.contains(productNo) { - allCandles[productNo]!.append(fileUrl) + if candleFiles.keys.contains(fileProductNo) { + candleFiles[fileProductNo]!.append(fileUrl) } else { - allCandles[productNo] = [fileUrl] + candleFiles[fileProductNo] = [fileUrl] } } - return allCandles + return candleFiles } func buildCandleMinuteDB(productNo: String, csvFiles: [URL], removeOldDB: Bool = false) { diff --git a/KissMeConsole/Sources/KissConsole.swift b/KissMeConsole/Sources/KissConsole.swift index d5ced4f..693cf63 100644 --- a/KissMeConsole/Sources/KissConsole.swift +++ b/KissMeConsole/Sources/KissConsole.swift @@ -104,6 +104,9 @@ class KissConsole: KissMe.ShopContext { // 웹소켓 case real = "real" + // DB1 + case db = "db" + // 뉴스 case news = "news" case newsAll = "news all" @@ -136,6 +139,8 @@ class KissConsole: KissMe.ShopContext { return true case .real: return true + case .db: + return true case .news, .newsAll: return false } @@ -283,6 +288,8 @@ class KissConsole: KissMe.ShopContext { case .test: onTest(args) case .real: await onReal(args) + + case .db: await onDB(args) case .news: await onNews(args) case .newsAll: onNewsAll(args) @@ -1253,6 +1260,48 @@ extension KissConsole { } + private func onDB(_ args: [String]) async { + guard args.count >= 3 else { + print("Missing options") + return + } + guard args[0] == "candle" else { + print("Missing candle or something") + return + } + switch args[1] { + case "build": + onDB_Build(Array(args.dropFirst(2))) + case "validate": + onDB_Validate(Array(args.dropFirst(2))) + case "count": + onDB_Count(Array(args.dropFirst(2))) + default: + onDB_Select(Array(args.dropFirst(2))) + } + } + + + private func onDB_Build(_ args: [String]) { + + } + + + private func onDB_Validate(_ args: [String]) { + + } + + + private func onDB_Count(_ args: [String]) { + + } + + + private func onDB_Select(_ args: [String]) { + + } + + private func onNews(_ args: [String]) async { guard args.count == 1, let day = args[0].yyyyMMdd_toDate else { print("Missing day") diff --git a/KissMeConsole/Sources/Tests/DB1_CandleData_Tests.swift b/KissMeConsole/Sources/Tests/DB1_CandleData_Tests.swift index 50ea619..591ee07 100644 --- a/KissMeConsole/Sources/Tests/DB1_CandleData_Tests.swift +++ b/KissMeConsole/Sources/Tests/DB1_CandleData_Tests.swift @@ -52,14 +52,9 @@ struct DB1_CandleData_Tests: KissTestCase { let csvFiles = Self.collectCsv(productNo: productNo, yyyy: year, MM: month) let dataPath = URL.currentDirectory().appending(path: "data") + var itemCount = 0 for csvFile in csvFiles { - let candleMinName = CandleMinuteFileName() - if let (_, yyyyMMdd) = candleMinName.matchedUrl(csvFile.path), let year = Int(yyyyMMdd.prefix(4)) { - let yearDbPath = dataPath.appending(path: "\(productNo)/min/candle-\(year).db1") - } - let yearDbPath = dataPath.appending(path: "\(productNo)/min/candle-\(year).db1") - try? FileManager.default.removeItem(at: yearDbPath) try? FileManager.default.createDirectory(at: yearDbPath, withIntermediateDirectories: true) do { @@ -74,6 +69,7 @@ struct DB1_CandleData_Tests: KissTestCase { try db.insertData(item: item) assertEqual(candleData.candle, candle) + itemCount += 1 } try db.commit() @@ -81,6 +77,7 @@ struct DB1_CandleData_Tests: KissTestCase { print("\(error)") } } + Self.insertCount = itemCount } let test_5_CountSamsungDB: () -> Void = { @@ -94,13 +91,17 @@ struct DB1_CandleData_Tests: KissTestCase { let db = try KissDB(directory: yearDbPath) try db.begin() + var itemCount = 0 try db.selectData(into: { (dataItem: KissDB.DataItem) -> Bool in let candleData = CandleData(key: dataItem.key, data: dataItem.value) assertEqual(String(candleData.candleDate.prefix(6)), yyyyMM) + itemCount += 1 return true }) try db.rollback() + + assertEqual(Self.insertCount, itemCount) } catch { print("\(error)") } @@ -109,6 +110,7 @@ struct DB1_CandleData_Tests: KissTestCase { static let samsungProductNo = "005930" static let year = "2023" static let month = "06" + static var insertCount: Int = 0 static func collectCsv(productNo: String, yyyy: String, MM: String) -> [URL] { guard let enumerator = FileManager.subPathFiles("data/\(productNo)") else { diff --git a/README.md b/README.md index ea17263..b39cf40 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ WIP `modify (PNO) (ONO) (가격) (수량)` | 주문 내역을 변경. (수량) `look (상품명)` | (상품명) 에 해당되는 PNO 를 표시함. `load index` | data/index-products.csv 로부터 전체 지수 상품을 로딩. `update index` | **KRX 지수** 로부터 전체 지수 상품을 얻어서 **data/index-products.csv** 로 저장. -`holiday [yyyyMMdd]` | 휴장일 여부를 판단함. yyyymmDD 를 생략하면 오늘 날짜로 확인. **data/holiday.csv** 로 저장. +`holiday [yyyyMMdd]` | 휴장일 여부를 판단함. yyyyMMdd 를 생략하면 오늘 날짜로 확인. **data/holiday.csv** 로 저장. WIP `showcase` | 추천 상품을 제안함. `loves` | 관심 종목 전체를 열람. profile.json 에 저장된 관심 종목을 표시함. `love (탭).(번호) (PNO)` | 관심 종목에 추가함. (번호) 를 지정하지 않으면 (탭) 마지막에 추가함. @@ -52,6 +52,10 @@ WIP `showcase` | 추천 상품을 제안함. `localize names` | csv field name 에 대해서 한글명을 제공하는 **data/localized-names.csv** 를 저장. `localize (on/off)`| 앞으로 저장하는 모든 csv file 의 field 에 (on) 이면 한글명으로, (off) 이면 영문으로 저장. `real (PNO) (on/off)` | 실시간 웹소켓을 접속하여 수신된 데이터를 기록합니다. (on) 이면 파일로 기록, (off) 이면 기록하지 않음. +`db candle build [PNO] [yyyy] [MM] [dd]` | PNO 의 csv 파일로부터 지정된 yyyy, MM, dd 날짜에 대해서만 DB 로 빌드. **data/(PNO)/min/candle-(yyyy).db1** 파일에 저장. +`db candle validate [PNO] [yyyy]` | PNO 의 yyyy DB에 기록된 분봉에 대해서 유효한지 검사. +`db candle count [PNO] [yyyy]` | PNO 의 yyyy DB에 기록된 분봉 갯수를 열람. +`db candle (PNO) [yyyyMMdd] [HH]` | PNO 의 yyyyMMdd 날짜의 HH 시간의 모든 분봉을 열람. * PNO 는 `Product NO` 의 약자이고, 상품의 `단축코드` (shortCode) 와 동일합니다. * ONO 는 `Order NO` 의 약자이고, 고유한 주문번호 입니다.