Improve CLI

This commit is contained in:
Christoph Hagen 2023-10-24 11:31:08 +02:00
parent 6aa1026478
commit f67a9a2de7
4 changed files with 33 additions and 28 deletions

View File

@ -1,6 +1,6 @@
{
"contentDirectory": "../Public",
"trainingIterations": 20,
"serverPath": "https://mydomain.com/caps",
"authenticationToken": "mysecretkey",
"folder": "../Public",
"iterations": 20,
"server": "https://mydomain.com/caps",
"authentication": "mysecretkey",
}

View File

@ -4,49 +4,54 @@ import Foundation
@main
struct CapTrain: AsyncParsableCommand {
private static let defaultIterations = 10
@Flag(name: .shortAndLong, help: "Resume the previous training session (default: false)")
var resume: Bool = false
@Argument(help: "The path to the configuration file")
var configPath: String?
@Option(name: .shortAndLong, help: "The path to the configuration file. The file must be a json object containing command line arguments. Command line options take precedence over configuration file options")
var configuration: String?
@Option(name: .shortAndLong, help: "The number of iterations to train")
@Option(name: .shortAndLong, help: "The number of iterations to train (default: 10)")
var iterations: Int?
@Option(name: .shortAndLong, help: "The url of the caps server")
var serverPath: String?
@Option(name: .shortAndLong, help: "The url of the caps server to retrieve images and upload the classifier")
var server: String?
@Option(name: .shortAndLong, help: "The authentication token for the server")
var authentication: String?
@Option(name: .shortAndLong, help: "The folder where the content (images, classifier, thumbnails) is stored")
@Option(name: .shortAndLong, help: "The path to the folder where the content (images, classifier, thumbnails) is stored")
var folder: String?
func run() async throws {
let configurationFile = try configurationFile()
guard let contentFolder = folder ?? configurationFile?.contentFolder,
let trainingIterations = iterations ?? configurationFile?.trainingIterations,
let serverPath = serverPath ?? configurationFile?.serverPath,
let authenticationToken = authentication ?? configurationFile?.authenticationToken
else {
throw TrainingError.missingArguments
let iterations = iterations ?? configurationFile?.iterations ?? CapTrain.defaultIterations
guard let contentFolder = folder ?? configurationFile?.folder else {
throw TrainingError.missingArguments("folder")
}
guard let serverPath = server ?? configurationFile?.server else {
throw TrainingError.missingArguments("server")
}
guard let authentication = authentication ?? configurationFile?.authentication else {
throw TrainingError.missingArguments("authentication")
}
let configuration = Configuration(
contentFolder: contentFolder,
trainingIterations: trainingIterations,
trainingIterations: iterations,
serverPath: serverPath,
authenticationToken: authenticationToken)
authenticationToken: authentication)
let creator = try ClassifierCreator(configuration: configuration, resume: resume)
try await creator.run()
}
private func configurationFile() throws -> ConfigurationFile? {
guard let configPath else {
guard let configuration else {
return nil
}
let configurationFileUrl = URL(fileURLWithPath: configPath)
let configurationFileUrl = URL(fileURLWithPath: configuration)
return try ConfigurationFile(at: configurationFileUrl)
}
}

View File

@ -2,13 +2,13 @@ import Foundation
struct ConfigurationFile {
let contentFolder: String?
let folder: String?
let trainingIterations: Int?
let iterations: Int?
let serverPath: String?
let server: String?
let authenticationToken: String?
let authentication: String?
}
extension ConfigurationFile: Decodable {

View File

@ -4,7 +4,7 @@ enum TrainingError: Error {
case invalidGetResponseData(Int)
case invalidResponse(URL, Int)
case missingArguments
case missingArguments(String)
case configurationFileMissing(URL)
case configurationFileUnreadable(URL, Error)
@ -54,8 +54,8 @@ extension TrainingError: CustomStringConvertible {
var description: String {
switch self {
case .missingArguments:
return "Missing arguments"
case .missingArguments(let argument):
return "Missing argument '\(argument)'"
case .configurationFileMissing(let url):
return "No configuration at \(url.absoluteURL.path)"
case .configurationFileUnreadable(let url, let error):