Implement matrix model with given index set
This commit is contained in:
@@ -166,6 +166,17 @@ extension String {
|
||||
public var hasComma: Bool {
|
||||
return nil != rangeOfCharacter(from: commaCharSet)
|
||||
}
|
||||
|
||||
public var kmiIndex: String? {
|
||||
guard utf8.count == 8, String(prefix(4)).uppercased() == "KMI-" else {
|
||||
return nil
|
||||
}
|
||||
let index = String(suffix(4))
|
||||
guard let _ = Int(index) else {
|
||||
return nil
|
||||
}
|
||||
return "KMI-\(index)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public enum GeneralError: Error {
|
||||
case invalidAccessToken
|
||||
case invalidAccountNo
|
||||
case unsupportedQueryAtMockServer
|
||||
case emptyData
|
||||
case emptyData(String)
|
||||
case cannotReadFile
|
||||
case cannotWriteFile
|
||||
case cannotReadFileLine
|
||||
|
||||
@@ -159,7 +159,7 @@ extension KissShop {
|
||||
continuation.resume(returning: (result.response.body.totalCount, items.item))
|
||||
}
|
||||
else {
|
||||
continuation.resume(throwing: GeneralError.emptyData)
|
||||
continuation.resume(throwing: GeneralError.emptyData("getProduct(\(baseDate),\(pageNo))"))
|
||||
}
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
|
||||
@@ -330,7 +330,11 @@ extension KissIndex {
|
||||
|
||||
var outputs = [KissIndexResult.Output]()
|
||||
for array in scoreArray {
|
||||
let weight = Double(array.1) / (array.1 > 0 ? Double(positiveTotalScores): Double(negativeTotalScores))
|
||||
let weight = Double(array.1) / (array.1 >= 0 ? Double(positiveTotalScores): Double(negativeTotalScores))
|
||||
if weight.isNaN {
|
||||
print("Ignored NaN: \(array.1) \(positiveTotalScores) \(negativeTotalScores) from \(array.0)")
|
||||
continue
|
||||
}
|
||||
let name: String? = (includeName ? getProduct(shortCode: array.0)?.itemName: nil)
|
||||
let output = KissIndexResult.Output(shortCode: array.0, productName: name, weight: weight)
|
||||
outputs.append(output)
|
||||
@@ -342,17 +346,6 @@ extension KissIndex {
|
||||
|
||||
|
||||
extension String {
|
||||
var kmiIndex: String? {
|
||||
guard utf8.count == 8, String(prefix(4)).uppercased() == "KMI-" else {
|
||||
return nil
|
||||
}
|
||||
let index = String(suffix(4))
|
||||
guard let _ = Int(index) else {
|
||||
return nil
|
||||
}
|
||||
return "KMI-\(index)"
|
||||
}
|
||||
|
||||
func diffSecondsTwoHHmmss(_ another: String) -> TimeInterval {
|
||||
guard let (hour, min, sec) = self.HHmmss else {
|
||||
return Double.greatestFiniteMagnitude
|
||||
|
||||
@@ -6,11 +6,205 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import KissMe
|
||||
|
||||
|
||||
enum RunMode: String {
|
||||
case simulator = "sim"
|
||||
case runner = "run"
|
||||
}
|
||||
|
||||
|
||||
struct Param: Codable {
|
||||
|
||||
/// simulator 일 경우에 참고하는 날짜 정보 (begin ~ end)
|
||||
///
|
||||
let beginDate: String // yyyyMMdd HHmmss
|
||||
let endDate: String // yyyyMMdd HHmmss
|
||||
}
|
||||
|
||||
|
||||
struct Model: Codable {
|
||||
let indexSets: [IndexSet]
|
||||
|
||||
struct IndexSet: Codable {
|
||||
let name: String
|
||||
let memo: String
|
||||
let config: String?
|
||||
let runner: String
|
||||
let weight: Double
|
||||
|
||||
func build(date: Date) -> (URL, [String])? {
|
||||
guard let _ = name.kmiIndex else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let command = URL.currentDirectory().appending(path: runner)
|
||||
|
||||
let day = date.yyyyMMdd
|
||||
let time = date.HHmmss
|
||||
var args: [String] = [name, day, time]
|
||||
|
||||
if let config = config {
|
||||
args.append(config)
|
||||
}
|
||||
return (command, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class KissMatrix {
|
||||
|
||||
func run() {
|
||||
|
||||
func printUsage() {
|
||||
let appName = (CommandLine.arguments[0] as NSString).lastPathComponent
|
||||
print("\(appName) [sim|run] model.json [yyyyMMdd] [HHmmss]")
|
||||
}
|
||||
|
||||
guard CommandLine.argc >= 3 else {
|
||||
printUsage()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let runDate: Date
|
||||
let mode = RunMode(rawValue: CommandLine.arguments[1])
|
||||
switch mode {
|
||||
case .runner:
|
||||
runDate = Date()
|
||||
case .simulator:
|
||||
guard CommandLine.argc == 5 else {
|
||||
printUsage()
|
||||
return
|
||||
}
|
||||
guard let (year, month, day) = CommandLine.arguments[3].yyyyMMdd else {
|
||||
print("Invalid [yyyyMMdd] argument")
|
||||
printUsage()
|
||||
return
|
||||
}
|
||||
guard let (hour, min, sec) = CommandLine.arguments[4].HHmmss else {
|
||||
print("Invalid [HHmmss] argument")
|
||||
printUsage()
|
||||
return
|
||||
}
|
||||
var date = Date(timeIntervalSince1970: 0)
|
||||
date.change(year: year, month: month, day: day)
|
||||
date.change(hour: hour, min: min, sec: sec)
|
||||
runDate = date
|
||||
default:
|
||||
printUsage()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let modelJson = CommandLine.arguments[2]
|
||||
guard let model = loadModel(modelJson) else { return }
|
||||
print("Loaded \(model.indexSets.count) index set from \(modelJson)")
|
||||
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
Task {
|
||||
let indexResult = try await withThrowingTaskGroup(of: KissIndexResult?.self, returning: [KissIndexResult].self) { taskGroup in
|
||||
for indexSet in model.indexSets {
|
||||
guard let (runUrl, args) = indexSet.build(date: runDate) else {
|
||||
print("Cannot get command from \(indexSet.name)")
|
||||
continue
|
||||
}
|
||||
|
||||
taskGroup.addTask {
|
||||
do {
|
||||
let result = try await self.runIndex(runUrl, args: args)
|
||||
return result
|
||||
} catch {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var taskResult = [KissIndexResult]()
|
||||
for try await result in taskGroup.compactMap( { $0 }) {
|
||||
taskResult.append(result)
|
||||
}
|
||||
return taskResult
|
||||
}
|
||||
|
||||
for result in indexResult {
|
||||
print(result)
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
|
||||
|
||||
private func runIndex(_ runUrl: URL, args: [String]) async throws -> KissIndexResult {
|
||||
assert(args.count >= 3)
|
||||
return try await withUnsafeThrowingContinuation { continuation in
|
||||
let kmi = args[0]
|
||||
|
||||
let output = Pipe()
|
||||
//let output = FileHandle(forWritingTo: subPath.appending(path: fileName))
|
||||
|
||||
let task = Process()
|
||||
task.currentDirectoryURL = URL.currentDirectory()
|
||||
task.executableURL = runUrl
|
||||
task.arguments = args
|
||||
task.standardOutput = output
|
||||
//task.standardError = error
|
||||
//task.qualityOfService = .userInitiated
|
||||
|
||||
print("curPath: \(task.currentDirectoryPath)")
|
||||
print("runPath: \(runUrl)")
|
||||
print("args: \(args)")
|
||||
|
||||
do {
|
||||
try task.run()
|
||||
task.waitUntilExit()
|
||||
} catch {
|
||||
print("run error \(error)")
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
|
||||
let data = output.fileHandleForReading.readDataToEndOfFile()
|
||||
if data.isEmpty {
|
||||
continuation.resume(throwing: GeneralError.emptyData(kmi))
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
try data.write(to: indexLogFile(kmi))
|
||||
|
||||
let indexResult = try JSONDecoder().decode(KissIndexResult.self, from: data)
|
||||
continuation.resume(returning: indexResult)
|
||||
} catch {
|
||||
print("jsonError \(kmi)")
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func indexLogFile(_ kmi: String) -> URL {
|
||||
let subPath = URL.currentDirectory().appending(path: "log/index")
|
||||
try? FileManager.default.createDirectory(at: subPath, withIntermediateDirectories: true)
|
||||
|
||||
let date = Date()
|
||||
let fileName = "\(date.yyyyMMdd_HHmmssSSSS_forFile)-\(kmi).log"
|
||||
return subPath.appending(path: fileName)
|
||||
}
|
||||
|
||||
|
||||
private func loadModel(_ jsonFile: String) -> Model? {
|
||||
do {
|
||||
let configUrl = URL.currentDirectory().appending(path: jsonFile)
|
||||
let data = try Data(contentsOf: configUrl, options: .uncached)
|
||||
let model = try JSONDecoder().decode(Model.self, from: data)
|
||||
return model
|
||||
} catch {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,8 @@
|
||||
children = (
|
||||
34F18FD62A3CCB5D0068C697 /* KissMeBatchTests.swift */,
|
||||
);
|
||||
path = KissMeBatchTests;
|
||||
name = KissMeBatchTests;
|
||||
path = ../../KissMeBatch/Tests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "KMI-0002 20230623 100000"
|
||||
argument = "KMI-0004 20230626 100000"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "341F5ECF2A0A8B9000962D48"
|
||||
BuildableName = "KissMeConsole"
|
||||
BlueprintName = "KissMeConsole"
|
||||
ReferencedContainer = "container:KissMeConsole.xcodeproj">
|
||||
BlueprintIdentifier = "349843442A24DBF700E85B08"
|
||||
BuildableName = "KissMeMatrix"
|
||||
BlueprintName = "KissMeMatrix"
|
||||
ReferencedContainer = "container:KissMeMatrix.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
@@ -40,25 +40,20 @@
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
viewDebuggingEnabled = "No"
|
||||
consoleMode = "1">
|
||||
<PathRunnable
|
||||
runnableDebuggingMode = "0"
|
||||
BundleIdentifier = "com.googlecode.iterm2"
|
||||
FilePath = "/Applications/iTerm.app">
|
||||
</PathRunnable>
|
||||
<MacroExpansion>
|
||||
viewDebuggingEnabled = "No">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "341F5ECF2A0A8B9000962D48"
|
||||
BuildableName = "KissMeConsole"
|
||||
BlueprintName = "KissMeConsole"
|
||||
ReferencedContainer = "container:KissMeConsole.xcodeproj">
|
||||
BlueprintIdentifier = "349843442A24DBF700E85B08"
|
||||
BuildableName = "KissMeMatrix"
|
||||
BlueprintName = "KissMeMatrix"
|
||||
ReferencedContainer = "container:KissMeMatrix.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}"
|
||||
argument = "sim model/golder.json 20230626 100000"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
@@ -73,10 +68,10 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "341F5ECF2A0A8B9000962D48"
|
||||
BuildableName = "KissMeConsole"
|
||||
BlueprintName = "KissMeConsole"
|
||||
ReferencedContainer = "container:KissMeConsole.xcodeproj">
|
||||
BlueprintIdentifier = "349843442A24DBF700E85B08"
|
||||
BuildableName = "KissMeMatrix"
|
||||
BlueprintName = "KissMeMatrix"
|
||||
ReferencedContainer = "container:KissMeMatrix.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
@@ -8,6 +8,7 @@
|
||||
THIS_PATH=`dirname "$0"`
|
||||
|
||||
${THIS_PATH}/build_any.sh KissMeConsole
|
||||
${THIS_PATH}/build_any.sh KissMeIndex
|
||||
#${THIS_PATH}/build_any.sh KissMeBatch
|
||||
#${THIS_PATH}/build_any.sh KissGram
|
||||
|
||||
|
||||
Reference in New Issue
Block a user