5 Commits

Author SHA1 Message Date
5440024038 feat: Renames EffectiveLength to EquivalentLength
All checks were successful
CI / Linux Tests (push) Successful in 9m34s
2026-01-29 11:25:20 -05:00
f005b43936 feat: Try to build image in ci instead of relying on just.
All checks were successful
CI / Linux Tests (push) Successful in 5m46s
2026-01-29 10:52:07 -05:00
f44b35ab3d feat: Try extractions/setup-just
Some checks failed
CI / Linux Tests (push) Failing after 7s
2026-01-29 10:46:00 -05:00
bbf9a8b390 feat: Setup just directly in ci workflow
Some checks failed
CI / Linux Tests (push) Failing after 10s
2026-01-29 10:43:49 -05:00
c52cee212f feat: Adds ci workflow.
Some checks failed
CI / Linux Tests (push) Failing after 5s
2026-01-29 10:36:29 -05:00
26 changed files with 179 additions and 110 deletions

31
.gitea/workflows/ci.yaml Normal file
View File

@@ -0,0 +1,31 @@
name: CI
on:
push:
branches:
- main
- dev
pull_request:
workflow_dispatch:
jobs:
ubuntu:
name: Linux Tests
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Setup QEMU
uses: docker/setup-qemu-action@v3
- name: Setup buildx
uses: docker/setup-buildx-action@v3
- name: Build test image
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile.test
push: false
load: true
tags: michael/ductcalc:test
- name: Run Tests
run: |
docker run --rm michael/ductcalc:test swift test

View File

@@ -120,11 +120,10 @@ let package = Package(
.target(name: "HTMLSnapshotTesting"), .target(name: "HTMLSnapshotTesting"),
.target(name: "PdfClient"), .target(name: "PdfClient"),
.product(name: "SnapshotTesting", package: "swift-snapshot-testing"), .product(name: "SnapshotTesting", package: "swift-snapshot-testing"),
],
resources: [
.copy("__Snapshots__")
] ]
// ,
// resources: [
// .copy("__Snapshots__")
// ]
), ),
.target( .target(
name: "ProjectClient", name: "ProjectClient",

View File

@@ -7,13 +7,13 @@ import ManualDCore
extension DatabaseClient { extension DatabaseClient {
@DependencyClient @DependencyClient
public struct EffectiveLengthClient: Sendable { public struct EffectiveLengthClient: Sendable {
public var create: @Sendable (EffectiveLength.Create) async throws -> EffectiveLength public var create: @Sendable (EquivalentLength.Create) async throws -> EquivalentLength
public var delete: @Sendable (EffectiveLength.ID) async throws -> Void public var delete: @Sendable (EquivalentLength.ID) async throws -> Void
public var fetch: @Sendable (Project.ID) async throws -> [EffectiveLength] public var fetch: @Sendable (Project.ID) async throws -> [EquivalentLength]
public var fetchMax: @Sendable (Project.ID) async throws -> EffectiveLength.MaxContainer public var fetchMax: @Sendable (Project.ID) async throws -> EquivalentLength.MaxContainer
public var get: @Sendable (EffectiveLength.ID) async throws -> EffectiveLength? public var get: @Sendable (EquivalentLength.ID) async throws -> EquivalentLength?
public var update: public var update:
@Sendable (EffectiveLength.ID, EffectiveLength.Update) async throws -> EffectiveLength @Sendable (EquivalentLength.ID, EquivalentLength.Update) async throws -> EquivalentLength
} }
} }
@@ -74,7 +74,7 @@ extension DatabaseClient.EffectiveLengthClient: TestDependencyKey {
} }
} }
extension EffectiveLength.Create { extension EquivalentLength.Create {
func toModel() throws -> EffectiveLengthModel { func toModel() throws -> EffectiveLengthModel {
try validate() try validate()
@@ -94,7 +94,7 @@ extension EffectiveLength.Create {
} }
} }
extension EffectiveLength { extension EquivalentLength {
struct Migrate: AsyncMigration { struct Migrate: AsyncMigration {
let name = "CreateEffectiveLength" let name = "CreateEffectiveLength"
@@ -173,20 +173,20 @@ final class EffectiveLengthModel: Model, @unchecked Sendable {
$project.id = projectID $project.id = projectID
} }
func toDTO() throws -> EffectiveLength { func toDTO() throws -> EquivalentLength {
try .init( try .init(
id: requireID(), id: requireID(),
projectID: $project.id, projectID: $project.id,
name: name, name: name,
type: .init(rawValue: type)!, type: .init(rawValue: type)!,
straightLengths: straightLengths, straightLengths: straightLengths,
groups: JSONDecoder().decode([EffectiveLength.Group].self, from: groups), groups: JSONDecoder().decode([EquivalentLength.Group].self, from: groups),
createdAt: createdAt!, createdAt: createdAt!,
updatedAt: updatedAt! updatedAt: updatedAt!
) )
} }
func applyUpdates(_ updates: EffectiveLength.Update) throws { func applyUpdates(_ updates: EquivalentLength.Update) throws {
if let name = updates.name, name != self.name { if let name = updates.name, name != self.name {
self.name = name self.name = name
} }

View File

@@ -77,7 +77,7 @@ extension DatabaseClient.Migrations: DependencyKey {
ComponentPressureLoss.Migrate(), ComponentPressureLoss.Migrate(),
EquipmentInfo.Migrate(), EquipmentInfo.Migrate(),
Room.Migrate(), Room.Migrate(),
EffectiveLength.Migrate(), EquivalentLength.Migrate(),
TrunkSize.Migrate(), TrunkSize.Migrate(),
] ]
} }

View File

@@ -4,7 +4,7 @@ import Foundation
// TODO: Not sure how to model effective length groups in the database. // TODO: Not sure how to model effective length groups in the database.
// thinking perhaps just have a 'data' field that encoded / decodes // thinking perhaps just have a 'data' field that encoded / decodes
// to swift types?? // to swift types??
public struct EffectiveLength: Codable, Equatable, Identifiable, Sendable { public struct EquivalentLength: Codable, Equatable, Identifiable, Sendable {
public let id: UUID public let id: UUID
public let projectID: Project.ID public let projectID: Project.ID
@@ -19,9 +19,9 @@ public struct EffectiveLength: Codable, Equatable, Identifiable, Sendable {
id: UUID, id: UUID,
projectID: Project.ID, projectID: Project.ID,
name: String, name: String,
type: EffectiveLength.EffectiveLengthType, type: EquivalentLength.EffectiveLengthType,
straightLengths: [Int], straightLengths: [Int],
groups: [EffectiveLength.Group], groups: [EquivalentLength.Group],
createdAt: Date, createdAt: Date,
updatedAt: Date updatedAt: Date
) { ) {
@@ -36,7 +36,7 @@ public struct EffectiveLength: Codable, Equatable, Identifiable, Sendable {
} }
} }
extension EffectiveLength { extension EquivalentLength {
public struct Create: Codable, Equatable, Sendable { public struct Create: Codable, Equatable, Sendable {
@@ -49,9 +49,9 @@ extension EffectiveLength {
public init( public init(
projectID: Project.ID, projectID: Project.ID,
name: String, name: String,
type: EffectiveLength.EffectiveLengthType, type: EquivalentLength.EffectiveLengthType,
straightLengths: [Int], straightLengths: [Int],
groups: [EffectiveLength.Group] groups: [EquivalentLength.Group]
) { ) {
self.projectID = projectID self.projectID = projectID
self.name = name self.name = name
@@ -70,9 +70,9 @@ extension EffectiveLength {
public init( public init(
name: String? = nil, name: String? = nil,
type: EffectiveLength.EffectiveLengthType? = nil, type: EquivalentLength.EffectiveLengthType? = nil,
straightLengths: [Int]? = nil, straightLengths: [Int]? = nil,
groups: [EffectiveLength.Group]? = nil groups: [EquivalentLength.Group]? = nil
) { ) {
self.name = name self.name = name
self.type = type self.type = type
@@ -107,8 +107,8 @@ extension EffectiveLength {
} }
public struct MaxContainer: Codable, Equatable, Sendable { public struct MaxContainer: Codable, Equatable, Sendable {
public let supply: EffectiveLength? public let supply: EquivalentLength?
public let `return`: EffectiveLength? public let `return`: EquivalentLength?
public var total: Double? { public var total: Double? {
guard let supply else { return nil } guard let supply else { return nil }
@@ -116,21 +116,21 @@ extension EffectiveLength {
return supply.totalEquivalentLength + `return`.totalEquivalentLength return supply.totalEquivalentLength + `return`.totalEquivalentLength
} }
public init(supply: EffectiveLength? = nil, return: EffectiveLength? = nil) { public init(supply: EquivalentLength? = nil, return: EquivalentLength? = nil) {
self.supply = supply self.supply = supply
self.return = `return` self.return = `return`
} }
} }
} }
extension EffectiveLength { extension EquivalentLength {
public var totalEquivalentLength: Double { public var totalEquivalentLength: Double {
straightLengths.reduce(into: 0.0) { $0 += Double($1) } straightLengths.reduce(into: 0.0) { $0 += Double($1) }
+ groups.totalEquivalentLength + groups.totalEquivalentLength
} }
} }
extension Array where Element == EffectiveLength.Group { extension Array where Element == EquivalentLength.Group {
public var totalEquivalentLength: Double { public var totalEquivalentLength: Double {
reduce(into: 0.0) { reduce(into: 0.0) {
@@ -141,7 +141,7 @@ extension Array where Element == EffectiveLength.Group {
#if DEBUG #if DEBUG
extension EffectiveLength { extension EquivalentLength {
public static func mock(projectID: Project.ID) -> [Self] { public static func mock(projectID: Project.ID) -> [Self] {
@Dependency(\.uuid) var uuid @Dependency(\.uuid) var uuid

View File

@@ -89,7 +89,7 @@ extension Project {
public let project: Project public let project: Project
public let componentLosses: [ComponentPressureLoss] public let componentLosses: [ComponentPressureLoss]
public let equipmentInfo: EquipmentInfo public let equipmentInfo: EquipmentInfo
public let equivalentLengths: [EffectiveLength] public let equivalentLengths: [EquivalentLength]
public let rooms: [Room] public let rooms: [Room]
public let trunks: [TrunkSize] public let trunks: [TrunkSize]
@@ -97,7 +97,7 @@ extension Project {
project: Project, project: Project,
componentLosses: [ComponentPressureLoss], componentLosses: [ComponentPressureLoss],
equipmentInfo: EquipmentInfo, equipmentInfo: EquipmentInfo,
equivalentLengths: [EffectiveLength], equivalentLengths: [EquivalentLength],
rooms: [Room], rooms: [Room],
trunks: [TrunkSize] trunks: [TrunkSize]
) { ) {

View File

@@ -226,10 +226,10 @@ extension SiteRoute.Api {
extension SiteRoute.Api { extension SiteRoute.Api {
public enum EffectiveLengthRoute: Equatable, Sendable { public enum EffectiveLengthRoute: Equatable, Sendable {
case create(EffectiveLength.Create) case create(EquivalentLength.Create)
case delete(id: EffectiveLength.ID) case delete(id: EquivalentLength.ID)
case fetch(projectID: Project.ID) case fetch(projectID: Project.ID)
case get(id: EffectiveLength.ID) case get(id: EquivalentLength.ID)
static let rootPath = "effectiveLength" static let rootPath = "effectiveLength"
@@ -240,12 +240,12 @@ extension SiteRoute.Api {
"create" "create"
} }
Method.post Method.post
Body(.json(EffectiveLength.Create.self)) Body(.json(EquivalentLength.Create.self))
} }
Route(.case(Self.delete(id:))) { Route(.case(Self.delete(id:))) {
Path { Path {
rootPath rootPath
EffectiveLength.ID.parser() EquivalentLength.ID.parser()
} }
Method.delete Method.delete
} }
@@ -261,7 +261,7 @@ extension SiteRoute.Api {
Route(.case(Self.get(id:))) { Route(.case(Self.get(id:))) {
Path { Path {
rootPath rootPath
EffectiveLength.ID.parser() EquivalentLength.ID.parser()
} }
Method.get Method.get
} }

View File

@@ -394,11 +394,11 @@ extension SiteRoute.View.ProjectRoute {
} }
public enum EquivalentLengthRoute: Equatable, Sendable { public enum EquivalentLengthRoute: Equatable, Sendable {
case delete(id: EffectiveLength.ID) case delete(id: EquivalentLength.ID)
case field(FieldType, style: EffectiveLength.EffectiveLengthType? = nil) case field(FieldType, style: EquivalentLength.EffectiveLengthType? = nil)
case index case index
case submit(FormStep) case submit(FormStep)
case update(EffectiveLength.ID, StepThree) case update(EquivalentLength.ID, StepThree)
static let rootPath = "effective-lengths" static let rootPath = "effective-lengths"
@@ -406,7 +406,7 @@ extension SiteRoute.View.ProjectRoute {
Route(.case(Self.delete(id:))) { Route(.case(Self.delete(id:))) {
Path { Path {
rootPath rootPath
EffectiveLength.ID.parser() EquivalentLength.ID.parser()
} }
Method.delete Method.delete
} }
@@ -424,7 +424,7 @@ extension SiteRoute.View.ProjectRoute {
Field("type") { FieldType.parser() } Field("type") { FieldType.parser() }
Optionally { Optionally {
Field("style", default: nil) { Field("style", default: nil) {
EffectiveLength.EffectiveLengthType.parser() EquivalentLength.EffectiveLengthType.parser()
} }
} }
} }
@@ -437,16 +437,16 @@ extension SiteRoute.View.ProjectRoute {
Route(.case(Self.update)) { Route(.case(Self.update)) {
Path { Path {
rootPath rootPath
EffectiveLength.ID.parser() EquivalentLength.ID.parser()
} }
Method.patch Method.patch
Body { Body {
FormData { FormData {
Optionally { Optionally {
Field("id", default: nil) { EffectiveLength.ID.parser() } Field("id", default: nil) { EquivalentLength.ID.parser() }
} }
Field("name", .string) Field("name", .string)
Field("type") { EffectiveLength.EffectiveLengthType.parser() } Field("type") { EquivalentLength.EffectiveLengthType.parser() }
Many { Many {
Field("straightLengths") { Field("straightLengths") {
Int.parser() Int.parser()
@@ -490,10 +490,10 @@ extension SiteRoute.View.ProjectRoute {
Body { Body {
FormData { FormData {
Optionally { Optionally {
Field("id", default: nil) { EffectiveLength.ID.parser() } Field("id", default: nil) { EquivalentLength.ID.parser() }
} }
Field("name", .string) Field("name", .string)
Field("type") { EffectiveLength.EffectiveLengthType.parser() } Field("type") { EquivalentLength.EffectiveLengthType.parser() }
} }
.map(.memberwise(StepOne.init)) .map(.memberwise(StepOne.init))
} }
@@ -505,10 +505,10 @@ extension SiteRoute.View.ProjectRoute {
Body { Body {
FormData { FormData {
Optionally { Optionally {
Field("id", default: nil) { EffectiveLength.ID.parser() } Field("id", default: nil) { EquivalentLength.ID.parser() }
} }
Field("name", .string) Field("name", .string)
Field("type") { EffectiveLength.EffectiveLengthType.parser() } Field("type") { EquivalentLength.EffectiveLengthType.parser() }
Many { Many {
Field("straightLengths") { Field("straightLengths") {
Int.parser() Int.parser()
@@ -525,10 +525,10 @@ extension SiteRoute.View.ProjectRoute {
Body { Body {
FormData { FormData {
Optionally { Optionally {
Field("id", default: nil) { EffectiveLength.ID.parser() } Field("id", default: nil) { EquivalentLength.ID.parser() }
} }
Field("name", .string) Field("name", .string)
Field("type") { EffectiveLength.EffectiveLengthType.parser() } Field("type") { EquivalentLength.EffectiveLengthType.parser() }
Many { Many {
Field("straightLengths") { Field("straightLengths") {
Int.parser() Int.parser()
@@ -567,22 +567,22 @@ extension SiteRoute.View.ProjectRoute {
} }
public struct StepOne: Codable, Equatable, Sendable { public struct StepOne: Codable, Equatable, Sendable {
public let id: EffectiveLength.ID? public let id: EquivalentLength.ID?
public let name: String public let name: String
public let type: EffectiveLength.EffectiveLengthType public let type: EquivalentLength.EffectiveLengthType
} }
public struct StepTwo: Codable, Equatable, Sendable { public struct StepTwo: Codable, Equatable, Sendable {
public let id: EffectiveLength.ID? public let id: EquivalentLength.ID?
public let name: String public let name: String
public let type: EffectiveLength.EffectiveLengthType public let type: EquivalentLength.EffectiveLengthType
public let straightLengths: [Int] public let straightLengths: [Int]
public init( public init(
id: EffectiveLength.ID? = nil, id: EquivalentLength.ID? = nil,
name: String, name: String,
type: EffectiveLength.EffectiveLengthType, type: EquivalentLength.EffectiveLengthType,
straightLengths: [Int] straightLengths: [Int]
) { ) {
self.id = id self.id = id
@@ -593,9 +593,9 @@ extension SiteRoute.View.ProjectRoute {
} }
public struct StepThree: Codable, Equatable, Sendable { public struct StepThree: Codable, Equatable, Sendable {
public let id: EffectiveLength.ID? public let id: EquivalentLength.ID?
public let name: String public let name: String
public let type: EffectiveLength.EffectiveLengthType public let type: EquivalentLength.EffectiveLengthType
public let straightLengths: [Int] public let straightLengths: [Int]
public let groupGroups: [Int] public let groupGroups: [Int]
public let groupLetters: [String] public let groupLetters: [String]

View File

@@ -84,8 +84,8 @@ extension PdfClient {
public let componentLosses: [ComponentPressureLoss] public let componentLosses: [ComponentPressureLoss]
public let ductSizes: DuctSizes public let ductSizes: DuctSizes
public let equipmentInfo: EquipmentInfo public let equipmentInfo: EquipmentInfo
public let maxSupplyTEL: EffectiveLength public let maxSupplyTEL: EquivalentLength
public let maxReturnTEL: EffectiveLength public let maxReturnTEL: EquivalentLength
public let frictionRate: FrictionRate public let frictionRate: FrictionRate
public let projectSHR: Double public let projectSHR: Double
@@ -99,8 +99,8 @@ extension PdfClient {
componentLosses: [ComponentPressureLoss], componentLosses: [ComponentPressureLoss],
ductSizes: DuctSizes, ductSizes: DuctSizes,
equipmentInfo: EquipmentInfo, equipmentInfo: EquipmentInfo,
maxSupplyTEL: EffectiveLength, maxSupplyTEL: EquivalentLength,
maxReturnTEL: EffectiveLength, maxReturnTEL: EquivalentLength,
frictionRate: FrictionRate, frictionRate: FrictionRate,
projectSHR: Double projectSHR: Double
) { ) {
@@ -134,7 +134,7 @@ extension PdfClient {
let rooms = Room.mock(projectID: project.id) let rooms = Room.mock(projectID: project.id)
let trunks = TrunkSize.mock(projectID: project.id, rooms: rooms) let trunks = TrunkSize.mock(projectID: project.id, rooms: rooms)
let equipmentInfo = EquipmentInfo.mock(projectID: project.id) let equipmentInfo = EquipmentInfo.mock(projectID: project.id)
let equivalentLengths = EffectiveLength.mock(projectID: project.id) let equivalentLengths = EquivalentLength.mock(projectID: project.id)
return .init( return .init(
project: project, project: project,

View File

@@ -2,7 +2,7 @@ import Elementary
import ManualDCore import ManualDCore
struct EffectiveLengthsTable: HTML, Sendable { struct EffectiveLengthsTable: HTML, Sendable {
let effectiveLengths: [EffectiveLength] let effectiveLengths: [EquivalentLength]
var body: some HTML<HTMLTag.table> { var body: some HTML<HTMLTag.table> {
table { table {
@@ -41,7 +41,7 @@ struct EffectiveLengthsTable: HTML, Sendable {
} }
struct EffectiveLengthGroupTable: HTML, Sendable { struct EffectiveLengthGroupTable: HTML, Sendable {
let groups: [EffectiveLength.Group] let groups: [EquivalentLength.Group]
var body: some HTML<HTMLTag.table> { var body: some HTML<HTMLTag.table> {
table { table {

View File

@@ -60,12 +60,12 @@ extension ProjectClient {
public struct FrictionRateResponse: Codable, Equatable, Sendable { public struct FrictionRateResponse: Codable, Equatable, Sendable {
public let componentLosses: [ComponentPressureLoss] public let componentLosses: [ComponentPressureLoss]
public let equivalentLengths: EffectiveLength.MaxContainer public let equivalentLengths: EquivalentLength.MaxContainer
public let frictionRate: FrictionRate? public let frictionRate: FrictionRate?
public init( public init(
componentLosses: [ComponentPressureLoss], componentLosses: [ComponentPressureLoss],
equivalentLengths: EffectiveLength.MaxContainer, equivalentLengths: EquivalentLength.MaxContainer,
frictionRate: FrictionRate? = nil frictionRate: FrictionRate? = nil
) { ) {
self.componentLosses = componentLosses self.componentLosses = componentLosses

View File

@@ -144,11 +144,11 @@ extension DatabaseClient {
// Internal container. // Internal container.
struct DesignFrictionRateResponse: Equatable, Sendable { struct DesignFrictionRateResponse: Equatable, Sendable {
typealias EnsuredTEL = (supply: EffectiveLength, return: EffectiveLength) typealias EnsuredTEL = (supply: EquivalentLength, return: EquivalentLength)
let designFrictionRate: Double let designFrictionRate: Double
let equipmentInfo: EquipmentInfo let equipmentInfo: EquipmentInfo
let telMaxContainer: EffectiveLength.MaxContainer let telMaxContainer: EquivalentLength.MaxContainer
func ensureMaxContainer() throws -> EnsuredTEL { func ensureMaxContainer() throws -> EnsuredTEL {
@@ -167,7 +167,7 @@ extension DatabaseClient {
func designFrictionRate( func designFrictionRate(
componentLosses: [ComponentPressureLoss], componentLosses: [ComponentPressureLoss],
equipmentInfo: EquipmentInfo, equipmentInfo: EquipmentInfo,
equivalentLengths: EffectiveLength.MaxContainer equivalentLengths: EquivalentLength.MaxContainer
) -> DesignFrictionRateResponse? { ) -> DesignFrictionRateResponse? {
guard let tel = equivalentLengths.total, guard let tel = equivalentLengths.total,
componentLosses.count > 0 componentLosses.count > 0

View File

@@ -4,8 +4,8 @@ import ManualDCore
struct DuctSizeSharedRequest { struct DuctSizeSharedRequest {
let equipmentInfo: EquipmentInfo let equipmentInfo: EquipmentInfo
let maxSupplyLength: EffectiveLength let maxSupplyLength: EquivalentLength
let maxReturnLenght: EffectiveLength let maxReturnLenght: EquivalentLength
let designFrictionRate: Double let designFrictionRate: Double
let projectSHR: Double let projectSHR: Double
} }

View File

@@ -1,7 +1,7 @@
import ManualDCore import ManualDCore
extension Project.Detail { extension Project.Detail {
var maxContainer: EffectiveLength.MaxContainer { var maxContainer: EquivalentLength.MaxContainer {
.init( .init(
supply: equivalentLengths.filter({ $0.type == .supply }) supply: equivalentLengths.filter({ $0.type == .supply })
.sorted(by: { $0.totalEquivalentLength > $1.totalEquivalentLength }) .sorted(by: { $0.totalEquivalentLength > $1.totalEquivalentLength })

View File

@@ -12,8 +12,8 @@ extension SiteRoute.View.ProjectRoute.EquivalentLengthRoute.StepThree {
} }
} }
var groups: [EffectiveLength.Group] { var groups: [EquivalentLength.Group] {
var groups = [EffectiveLength.Group]() var groups = [EquivalentLength.Group]()
for (n, group) in groupGroups.enumerated() { for (n, group) in groupGroups.enumerated() {
groups.append( groups.append(
.init( .init(
@@ -29,7 +29,7 @@ extension SiteRoute.View.ProjectRoute.EquivalentLengthRoute.StepThree {
} }
} }
extension EffectiveLength.Create { extension EquivalentLength.Create {
init( init(
form: SiteRoute.View.ProjectRoute.EquivalentLengthRoute.StepThree, form: SiteRoute.View.ProjectRoute.EquivalentLengthRoute.StepThree,
@@ -45,7 +45,7 @@ extension EffectiveLength.Create {
} }
} }
extension EffectiveLength.Update { extension EquivalentLength.Update {
init( init(
form: SiteRoute.View.ProjectRoute.EquivalentLengthRoute.StepThree, form: SiteRoute.View.ProjectRoute.EquivalentLengthRoute.StepThree,
projectID: Project.ID projectID: Project.ID

View File

@@ -488,7 +488,7 @@ extension SiteRoute.View.ProjectRoute.EquivalentLengthRoute {
switch step { switch step {
case .one(let stepOne): case .one(let stepOne):
return await ResultView { return await ResultView {
var effectiveLength: EffectiveLength? = nil var effectiveLength: EquivalentLength? = nil
if let id = stepOne.id { if let id = stepOne.id {
effectiveLength = try await database.effectiveLength.get(id) effectiveLength = try await database.effectiveLength.get(id)
} }
@@ -503,7 +503,7 @@ extension SiteRoute.View.ProjectRoute.EquivalentLengthRoute {
case .two(let stepTwo): case .two(let stepTwo):
return await ResultView { return await ResultView {
request.logger.debug("ViewController: Got step two...") request.logger.debug("ViewController: Got step two...")
var effectiveLength: EffectiveLength? = nil var effectiveLength: EquivalentLength? = nil
if let id = stepTwo.id { if let id = stepTwo.id {
effectiveLength = try await database.effectiveLength.get(id) effectiveLength = try await database.effectiveLength.get(id)
} }

View File

@@ -7,7 +7,7 @@ import Styleguide
struct EffectiveLengthForm: HTML, Sendable { struct EffectiveLengthForm: HTML, Sendable {
static func id(_ equivalentLength: EffectiveLength?) -> String { static func id(_ equivalentLength: EquivalentLength?) -> String {
let base = "equivalentLengthForm" let base = "equivalentLengthForm"
guard let equivalentLength else { return base } guard let equivalentLength else { return base }
return "\(base)_\(equivalentLength.id.uuidString.replacing("-", with: ""))" return "\(base)_\(equivalentLength.id.uuidString.replacing("-", with: ""))"
@@ -15,15 +15,15 @@ struct EffectiveLengthForm: HTML, Sendable {
let projectID: Project.ID let projectID: Project.ID
let dismiss: Bool let dismiss: Bool
let type: EffectiveLength.EffectiveLengthType let type: EquivalentLength.EffectiveLengthType
let effectiveLength: EffectiveLength? let effectiveLength: EquivalentLength?
var id: String { Self.id(effectiveLength) } var id: String { Self.id(effectiveLength) }
init( init(
projectID: Project.ID, projectID: Project.ID,
dismiss: Bool, dismiss: Bool,
type: EffectiveLength.EffectiveLengthType = .supply type: EquivalentLength.EffectiveLengthType = .supply
) { ) {
self.projectID = projectID self.projectID = projectID
self.dismiss = dismiss self.dismiss = dismiss
@@ -32,7 +32,7 @@ struct EffectiveLengthForm: HTML, Sendable {
} }
init( init(
effectiveLength: EffectiveLength effectiveLength: EquivalentLength
) { ) {
self.dismiss = true self.dismiss = true
self.type = effectiveLength.type self.type = effectiveLength.type
@@ -55,7 +55,7 @@ struct EffectiveLengthForm: HTML, Sendable {
struct StepOne: HTML, Sendable { struct StepOne: HTML, Sendable {
let projectID: Project.ID let projectID: Project.ID
let effectiveLength: EffectiveLength? let effectiveLength: EquivalentLength?
var route: String { var route: String {
let baseRoute = SiteRoute.View.router.path( let baseRoute = SiteRoute.View.router.path(
@@ -97,7 +97,7 @@ struct EffectiveLengthForm: HTML, Sendable {
struct StepTwo: HTML, Sendable { struct StepTwo: HTML, Sendable {
let projectID: Project.ID let projectID: Project.ID
let stepOne: SiteRoute.View.ProjectRoute.EquivalentLengthRoute.StepOne let stepOne: SiteRoute.View.ProjectRoute.EquivalentLengthRoute.StepOne
let effectiveLength: EffectiveLength? let effectiveLength: EquivalentLength?
var route: String { var route: String {
let baseRoute = SiteRoute.View.router.path( let baseRoute = SiteRoute.View.router.path(
@@ -152,7 +152,7 @@ struct EffectiveLengthForm: HTML, Sendable {
struct StepThree: HTML, Sendable { struct StepThree: HTML, Sendable {
let projectID: Project.ID let projectID: Project.ID
let effectiveLength: EffectiveLength? let effectiveLength: EquivalentLength?
let stepTwo: SiteRoute.View.ProjectRoute.EquivalentLengthRoute.StepTwo let stepTwo: SiteRoute.View.ProjectRoute.EquivalentLengthRoute.StepTwo
var route: String { var route: String {
@@ -254,10 +254,10 @@ struct StraightLengthField: HTML, Sendable {
struct GroupField: HTML, Sendable { struct GroupField: HTML, Sendable {
let style: EffectiveLength.EffectiveLengthType let style: EquivalentLength.EffectiveLengthType
let group: EffectiveLength.Group? let group: EquivalentLength.Group?
init(style: EffectiveLength.EffectiveLengthType, group: EffectiveLength.Group? = nil) { init(style: EquivalentLength.EffectiveLengthType, group: EquivalentLength.Group? = nil) {
self.style = style self.style = style
self.group = group self.group = group
} }
@@ -307,7 +307,7 @@ struct GroupField: HTML, Sendable {
struct GroupSelect: HTML, Sendable { struct GroupSelect: HTML, Sendable {
let style: EffectiveLength.EffectiveLengthType let style: EquivalentLength.EffectiveLengthType
var body: some HTML { var body: some HTML {
label(.class("select")) { label(.class("select")) {
@@ -328,13 +328,13 @@ struct GroupSelect: HTML, Sendable {
struct GroupTypeSelect: HTML, Sendable { struct GroupTypeSelect: HTML, Sendable {
let projectID: Project.ID let projectID: Project.ID
let selected: EffectiveLength.EffectiveLengthType let selected: EquivalentLength.EffectiveLengthType
var body: some HTML<HTMLTag.label> { var body: some HTML<HTMLTag.label> {
label(.class("select w-full")) { label(.class("select w-full")) {
span(.class("label")) { "Type" } span(.class("label")) { "Type" }
select(.name("type"), .id("type")) { select(.name("type"), .id("type")) {
for value in EffectiveLength.EffectiveLengthType.allCases { for value in EquivalentLength.EffectiveLengthType.allCases {
option( option(
.value("\(value.rawValue)"), .value("\(value.rawValue)"),
) { value.title } ) { value.title }
@@ -345,7 +345,7 @@ struct GroupTypeSelect: HTML, Sendable {
} }
} }
extension EffectiveLength.EffectiveLengthType { extension EquivalentLength.EffectiveLengthType {
var title: String { rawValue.capitalized } var title: String { rawValue.capitalized }

View File

@@ -5,9 +5,9 @@ import Styleguide
struct EffectiveLengthsTable: HTML, Sendable { struct EffectiveLengthsTable: HTML, Sendable {
let effectiveLengths: [EffectiveLength] let effectiveLengths: [EquivalentLength]
private var sortedLengths: [EffectiveLength] { private var sortedLengths: [EquivalentLength] {
effectiveLengths.sorted { effectiveLengths.sorted {
$0.totalEquivalentLength > $1.totalEquivalentLength $0.totalEquivalentLength > $1.totalEquivalentLength
} }
@@ -55,7 +55,7 @@ struct EffectiveLengthsTable: HTML, Sendable {
struct EffectiveLenghtRow: HTML, Sendable { struct EffectiveLenghtRow: HTML, Sendable {
let effectiveLength: EffectiveLength let effectiveLength: EquivalentLength
private var deleteRoute: SiteRoute.View { private var deleteRoute: SiteRoute.View {
.project( .project(

View File

@@ -7,14 +7,14 @@ struct EffectiveLengthsView: HTML, Sendable {
@Environment(ProjectViewValue.$projectID) var projectID @Environment(ProjectViewValue.$projectID) var projectID
let effectiveLengths: [EffectiveLength] let effectiveLengths: [EquivalentLength]
var supplies: [EffectiveLength] { var supplies: [EquivalentLength] {
effectiveLengths.filter({ $0.type == .supply }) effectiveLengths.filter({ $0.type == .supply })
.sorted { $0.totalEquivalentLength > $1.totalEquivalentLength } .sorted { $0.totalEquivalentLength > $1.totalEquivalentLength }
} }
var returns: [EffectiveLength] { var returns: [EquivalentLength] {
effectiveLengths.filter({ $0.type == .return }) effectiveLengths.filter({ $0.type == .return })
.sorted { $0.totalEquivalentLength > $1.totalEquivalentLength } .sorted { $0.totalEquivalentLength > $1.totalEquivalentLength }
} }

View File

@@ -8,7 +8,7 @@ struct FrictionRateView: HTML, Sendable {
@Environment(ProjectViewValue.$projectID) var projectID @Environment(ProjectViewValue.$projectID) var projectID
let componentLosses: [ComponentPressureLoss] let componentLosses: [ComponentPressureLoss]
let equivalentLengths: EffectiveLength.MaxContainer let equivalentLengths: EquivalentLength.MaxContainer
let frictionRate: FrictionRate? let frictionRate: FrictionRate?
private var availableStaticPressure: Double? { private var availableStaticPressure: Double? {

View File

@@ -233,7 +233,7 @@ extension ManualDClient {
func frictionRate( func frictionRate(
equipmentInfo: EquipmentInfo?, equipmentInfo: EquipmentInfo?,
componentLosses: [ComponentPressureLoss], componentLosses: [ComponentPressureLoss],
effectiveLength: EffectiveLength.MaxContainer effectiveLength: EquivalentLength.MaxContainer
) async throws -> FrictionRate? { ) async throws -> FrictionRate? {
guard let staticPressure = equipmentInfo?.staticPressure else { guard let staticPressure = equipmentInfo?.staticPressure else {
return nil return nil

View File

@@ -1,7 +1,7 @@
# TODO's # TODO's
- [x] Fix theme not working when selected upon signup. - [x] Fix theme not working when selected upon signup.
- [ ] Pdf generation - [x] Pdf generation
- [ ] Add postgres / mysql support - [ ] Add postgres / mysql support
- [ ] Opensource / license ?? - [ ] Opensource / license ??
- [ ] Figure out domain to host (currently thinking ductcalc.pro) - [ ] Figure out domain to host (currently thinking ductcalc.pro)

View File

@@ -78,10 +78,10 @@ struct ProjectRouteTests {
let p = Body { let p = Body {
FormData { FormData {
Optionally { Optionally {
Field("id", default: nil) { EffectiveLength.ID.parser() } Field("id", default: nil) { EquivalentLength.ID.parser() }
} }
Field("name", .string) Field("name", .string)
Field("type") { EffectiveLength.EffectiveLengthType.parser() } Field("type") { EquivalentLength.EffectiveLengthType.parser() }
Many { Many {
Field("straightLengths") { Field("straightLengths") {
Int.parser() Int.parser()

View File

@@ -86,7 +86,7 @@ struct ViewControllerTests {
let project = Project.mock let project = Project.mock
let rooms = Room.mock(projectID: project.id) let rooms = Room.mock(projectID: project.id)
let equipment = EquipmentInfo.mock(projectID: project.id) let equipment = EquipmentInfo.mock(projectID: project.id)
let tels = EffectiveLength.mock(projectID: project.id) let tels = EquivalentLength.mock(projectID: project.id)
let componentLosses = ComponentPressureLoss.mock(projectID: project.id) let componentLosses = ComponentPressureLoss.mock(projectID: project.id)
let trunks = TrunkSize.mock(projectID: project.id, rooms: rooms) let trunks = TrunkSize.mock(projectID: project.id, rooms: rooms)

36
docker/Dockerfile.test Normal file
View File

@@ -0,0 +1,36 @@
FROM docker.io/swift:6.2-noble
# Make sure all system packages are up to date, and install only essential packages.
RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \
&& apt-get -q update \
&& apt-get -q dist-upgrade -y \
&& apt-get -q install -y \
libjemalloc2 \
ca-certificates \
tzdata \
# If your app or its dependencies import FoundationNetworking, also install `libcurl4`.
libcurl4 \
# If your app or its dependencies import FoundationXML, also install `libxml2`.
# libxml2 \
sqlite3 \
&& rm -r /var/lib/apt/lists/*
# Set up a build area
WORKDIR /app
# First just resolve dependencies.
# This creates a cached layer that can be reused
# as long as your Package.swift/Package.resolved
# files do not change.
COPY ./Package.* ./
RUN swift package resolve \
$([ -f ./Package.resolved ] && echo "--force-resolved-versions" || true)
# Copy entire repo into container
COPY . .
ENV SWIFT_BACKTRACE=enable=no
ENV LOG_LEVEL=debug
CMD ["swift", "test"]

View File

@@ -14,7 +14,10 @@ run:
@swift run App serve --log debug @swift run App serve --log debug
build-docker file="docker/Dockerfile": build-docker file="docker/Dockerfile":
@podman build -f {{file}} -t {{docker_image}}:{{docker_tag}} . @docker build -f {{file}} -t {{docker_image}}:{{docker_tag}} .
run-docker: run-docker:
@podman run -it --rm -v $PWD:/app -p 8080:8080 {{docker_image}}:{{docker_tag}} @docker run -it --rm -v $PWD:/app -p 8080:8080 {{docker_image}}:{{docker_tag}}
test-docker: (build-docker "docker/Dockerfile.test")
@docker run --rm {{docker_image}}:{{docker_tag}} swift test