Compare commits

...

6 Commits

Author SHA1 Message Date
5cf3c8d0c1 Add image assets 2024-03-01 17:50:33 +01:00
7b3210169a Update training info in readme 2024-03-01 17:20:02 +01:00
e34845ab24 Add comment 2023-12-25 19:05:27 +01:00
400753a9a2 Allow custom data directory 2023-12-25 14:35:01 +01:00
1c57b538be Display environment on startup 2023-12-19 20:45:16 +01:00
5138bb543e Ignore more files 2023-12-19 20:45:02 +01:00
16 changed files with 46 additions and 42 deletions

13
.gitignore vendored
View File

@ -8,12 +8,11 @@
.DS_Store .DS_Store
Package.resolved Package.resolved
.swiftpm/ .swiftpm/
Public/caps.json
Public/changes.txt
Public/classifier.*
Public/count.*
Public/images/ Public/images/
Public/thumbnails/ Public/thumbnails/
Public/classifier.version Resources/config.json
Public/classifier.mlmodel Resources/logs/
Public/caps.json
Training/backup/
Training/config.json
Public/thumbnails
Public/count.js

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/assets/mstile-150x150.png?v=1"/>
<TileColor>#a36490</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
Public/assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

View File

@ -5,13 +5,13 @@
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta name="robots" content="noindex"/> <meta name="robots" content="noindex"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
<link rel="apple-touch-icon" sizes="180x180" href="/assets/icons/apple-touch-icon.png?v=1"/> <link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon.png?v=1"/>
<link rel="icon" type="image/png" sizes="32x32" href="/assets/icons/favicon-32x32.png?v=1"/> <link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png?v=1"/>
<link rel="icon" type="image/png" sizes="16x16" href="/assets/icons/favicon-16x16.png?v=1"/> <link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16x16.png?v=1"/>
<link rel="manifest" href="/assets/icons/site.webmanifest?v=1"/> <link rel="manifest" href="/assets/site.webmanifest?v=1"/>
<link rel="shortcut icon" href="/assets/icons/favicon.ico?v=1"/> <link rel="shortcut icon" href="/assets/favicon.ico?v=1"/>
<meta name="msapplication-TileColor" content="#da532c"> <meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-config" content="/assets/icons/browserconfig.xml?v=1"/> <meta name="msapplication-config" content="/assets/browserconfig.xml?v=1"/>
<meta name="theme-color" content="#ffffff"/> <meta name="theme-color" content="#ffffff"/>
<link href="grid.css?v=2" rel="stylesheet"/> <link href="grid.css?v=2" rel="stylesheet"/>
<meta name="author" content="Christoph Hagen"/> <meta name="author" content="Christoph Hagen"/>

View File

@ -51,31 +51,7 @@ Note: The data for the mosaic is currently not updated automatically, since Swif
## Classifier training ## Classifier training
The main server is running on Linux, which doesn't provide the CreateML framework required for classifier training. The main server is running on Linux, which doesn't provide the CreateML framework required for classifier training.
This has to be done on macOS using the `train.swift` script in the `Training` folder. This has to be done on macOS using the [Caps-Train](https://christophhagen.de/git/ch/Caps-Train) repository.
It will:
- Fetch the current image catalog by checking missing and changed images
- Train a classifier on the image set
- Increment the classifier version and upload the new model
- Update the list of caps which can be recognized using the classifier
- Create missing thumbnails for each cap for the cap grid
A configuration file is required to run the training, with a valid access token for the server:
```json
{
"imageDirectory": "../Public/images",
"classifierModelPath": "../Public/classifier.mlmodel",
"trainingIterations": 20,
"serverPath": "https://mydomain.com/caps",
"authenticationToken": "mysecretkey",
}
```
The configuration file `config.json` must be located in the folder from which the script is run.
```bash
swift train.swift
```
## Future work ## Future work
- Create thumbnails on the server using [JPEG](https://github.com/kelvin13/jpeg) - Create thumbnails on the server using [JPEG](https://github.com/kelvin13/jpeg)

View File

@ -3,6 +3,7 @@
"maxBodySize" : "2mb", "maxBodySize" : "2mb",
"logPath": "\/var\/log\/caps/metrics", "logPath": "\/var\/log\/caps/metrics",
"serveFiles": true, "serveFiles": true,
"dataDirectory" : "/ch/data/caps",
"writers" : [ "writers" : [
"auth_key_1" "auth_key_1"
] ]

View File

@ -20,6 +20,20 @@ struct Config: Codable {
/// Authentication tokens for remotes allowed to write /// Authentication tokens for remotes allowed to write
let writers: [String] let writers: [String]
/**
The folder where the data should be stored.
If the folder is set to `nil`, then the `Resources` folder is used.
*/
let dataDirectory: String?
func customDataDirectory(or publicDirectory: String) -> URL {
guard let dataDirectory else {
return URL(fileURLWithPath: publicDirectory)
}
return URL(fileURLWithPath: dataDirectory)
}
func logURL(possiblyRelativeTo resourcesDirectory: URL) -> URL { func logURL(possiblyRelativeTo resourcesDirectory: URL) -> URL {
guard let logPath else { guard let logPath else {
return resourcesDirectory.appendingPathComponent("logs") return resourcesDirectory.appendingPathComponent("logs")

View File

@ -5,6 +5,9 @@ import NIOCore
extension MultiThreadedEventLoopGroup: AsyncScheduler { extension MultiThreadedEventLoopGroup: AsyncScheduler {
func test() {
}
public func schedule(asyncJob: @escaping @Sendable () async throws -> Void) { public func schedule(asyncJob: @escaping @Sendable () async throws -> Void) {
_ = any().makeFutureWithTask(asyncJob) _ = any().makeFutureWithTask(asyncJob)
} }

View File

@ -31,7 +31,8 @@ func configure(_ app: Application) async throws {
app.http.server.configuration.port = config.port app.http.server.configuration.port = config.port
app.routes.defaultMaxBodySize = .init(stringLiteral: config.maxBodySize) app.routes.defaultMaxBodySize = .init(stringLiteral: config.maxBodySize)
server = CapServer(in: URL(fileURLWithPath: publicDirectory)) let dataDirectory = config.customDataDirectory(or: publicDirectory)
server = CapServer(in: dataDirectory)
provider = .init(observer: monitor, accessManager: config.writers) provider = .init(observer: monitor, accessManager: config.writers)
provider.asyncScheduler = asyncScheduler provider.asyncScheduler = asyncScheduler
@ -58,12 +59,12 @@ func configure(_ app: Application) async throws {
} else { } else {
try await serverStatus.update(.reducedFunctionality) try await serverStatus.update(.reducedFunctionality)
} }
print("[\(df.string(from: Date()))] Server started (\(server.capCount) caps)") print("[\(df.string(from: Date()))] Server started (\(app.environment.name), \(server.capCount) caps)")
} }
func shutdown() { func shutdown() {
print("[\(df.string(from: Date()))] Server shutdown") Task {
asyncScheduler.schedule { print("[\(df.string(from: Date()))] Server shutdown")
do { do {
try await asyncScheduler.shutdownGracefully() try await asyncScheduler.shutdownGracefully()
} catch { } catch {