WIP: Begins work on pdf client.

This commit is contained in:
2026-01-17 12:59:46 -05:00
parent 3ec1ee2814
commit 0fe80d05c6
13 changed files with 501 additions and 24 deletions

View File

@@ -5,6 +5,7 @@ import Fluent
import FluentSQLiteDriver
import ManualDCore
import NIOSSL
import ProjectClient
import Vapor
import VaporElementary
@preconcurrency import VaporRouting
@@ -111,6 +112,8 @@ extension SiteRoute {
}
}
extension DuctSizes: Content {}
@Sendable
private func siteHandler(
request: Request,
@@ -118,6 +121,7 @@ private func siteHandler(
) async throws -> any AsyncResponseEncodable {
@Dependency(\.apiController) var apiController
@Dependency(\.viewController) var viewController
@Dependency(\.projectClient) var projectClient
switch route {
case .api(let route):
@@ -125,6 +129,11 @@ private func siteHandler(
case .health:
return HTTPStatus.ok
case .view(let route):
// FIX: Remove.
if route == .test {
let projectID = UUID(uuidString: "E796C96C-F527-4753-A00A-EBCF25630663")!
return try await projectClient.calculateDuctSizes(projectID)
}
return try await viewController.respond(route: route, request: request)
}
}

View File

@@ -16,7 +16,7 @@ extension DependencyValues {
@DependencyClient
public struct ManualDClient: Sendable {
public var ductSize: @Sendable (DuctSizeRequest) async throws -> DuctSizeResponse
public var frictionRate: @Sendable (FrictionRateRequest) async throws -> FrictionRateResponse
public var frictionRate: @Sendable (FrictionRateRequest) async throws -> FrictionRate
public var totalEquivalentLength: @Sendable (TotalEquivalentLengthRequest) async throws -> Int
public var rectangularSize:
@Sendable (RectangularSizeRequest) async throws -> RectangularSizeResponse

View File

@@ -28,7 +28,7 @@ extension ManualDClient: DependencyKey {
let totalComponentLosses = request.componentPressureLosses.total
let availableStaticPressure = request.externalStaticPressure - totalComponentLosses
let frictionRate = availableStaticPressure * 100.0 / Double(request.totalEffectiveLength)
return .init(availableStaticPressure: availableStaticPressure, frictionRate: frictionRate)
return .init(availableStaticPressure: availableStaticPressure, value: frictionRate)
},
totalEquivalentLength: { request in
let trunkLengths = request.trunkLengths.reduce(0) { $0 + $1 }

View File

@@ -0,0 +1,14 @@
/// Holds onto values returned when calculating the design
/// friction rate for a project.
public struct FrictionRate: Codable, Equatable, Sendable {
public let availableStaticPressure: Double
public let value: Double
public init(
availableStaticPressure: Double,
value: Double
) {
self.availableStaticPressure = availableStaticPressure
self.value = value
}
}

View File

@@ -0,0 +1,47 @@
import Dependencies
import DependenciesMacros
import ManualDCore
@DependencyClient
public struct PdfClient: Sendable {
public var markdown: @Sendable (Request) async throws -> String
}
extension PdfClient: TestDependencyKey {
public static let testValue = Self()
}
extension PdfClient {
public struct Request: Codable, Equatable, Sendable {
public let project: Project
public let componentLosses: [ComponentPressureLoss]
public let ductSizes: DuctSizes
public let equipmentInfo: EquipmentInfo
public let maxSupplyTEL: EffectiveLength
public let maxReturnTEL: EffectiveLength
public let designFrictionRate: FrictionRate
public let projectSHR: Double
public init(
project: Project,
componentLosses: [ComponentPressureLoss],
ductSizes: DuctSizes,
equipmentInfo: EquipmentInfo,
maxSupplyTEL: EffectiveLength,
maxReturnTEL: EffectiveLength,
designFrictionRate: FrictionRate,
projectSHR: Double
) {
self.project = project
self.componentLosses = componentLosses
self.ductSizes = ductSizes
self.equipmentInfo = equipmentInfo
self.maxSupplyTEL = maxSupplyTEL
self.maxReturnTEL = maxReturnTEL
self.designFrictionRate = designFrictionRate
self.projectSHR = projectSHR
}
}
}

View File

@@ -0,0 +1,42 @@
import ManualDCore
extension PdfClient.Request {
func toMarkdown() -> String {
var retval = """
# Duct Calc
**Name:** \(project.name)
**Address:** \(project.streetAddress)
\(project.city), \(project.state) \(project.zipCode)
## Equipment
| | Value |
|-----------------|---------------------------------|
| Static Pressure | \(equipmentInfo.staticPressure) |
| Heating CFM | \(equipmentInfo.heatingCFM) |
| Cooling CFM | \(equipmentInfo.coolingCFM) |
## Friction Rate
| | Value |
|-----------------|---------------------------------|
"""
for row in componentLosses {
retval = """
\(retval)
\(componentLossRow(row))
"""
}
return retval
}
func componentLossRow(_ row: ComponentPressureLoss) -> String {
return """
| \(row.name) | \(row.value) |
"""
}
}

View File

@@ -58,12 +58,12 @@ extension ProjectClient {
public let componentLosses: [ComponentPressureLoss]
public let equivalentLengths: EffectiveLength.MaxContainer
public let frictionRate: ManualDClient.FrictionRateResponse?
public let frictionRate: FrictionRate?
public init(
componentLosses: [ComponentPressureLoss],
equivalentLengths: EffectiveLength.MaxContainer,
frictionRate: ManualDClient.FrictionRateResponse? = nil
frictionRate: FrictionRate? = nil
) {
self.componentLosses = componentLosses
self.equivalentLengths = equivalentLengths

View File

@@ -15,7 +15,7 @@ extension ViewController.Request {
switch route {
case .test:
// let projectID = UUID(uuidString: "A9C20153-E2E5-4C65-B33F-4D8A29C63A7A")!
// let projectID = UUID(uuidString: "E796C96C-F527-4753-A00A-EBCF25630663")!
return await view {
await ResultView {
@@ -372,7 +372,7 @@ extension SiteRoute.View.ProjectRoute.FrictionRateRoute {
FrictionRateView(
componentLosses: losses,
equivalentLengths: lengths,
frictionRateResponse: frictionRate
frictionRate: frictionRate
)
}
@@ -430,7 +430,7 @@ extension SiteRoute.View.ProjectRoute.ComponentLossRoute {
FrictionRateView(
componentLosses: response.componentLosses,
equivalentLengths: response.equivalentLengths,
frictionRateResponse: response.frictionRate
frictionRate: response.frictionRate
)
}

View File

@@ -9,37 +9,33 @@ struct FrictionRateView: HTML, Sendable {
let componentLosses: [ComponentPressureLoss]
let equivalentLengths: EffectiveLength.MaxContainer
let frictionRateResponse: ManualDClient.FrictionRateResponse?
let frictionRate: FrictionRate?
private var availableStaticPressure: Double? {
frictionRateResponse?.availableStaticPressure
}
private var frictionRateDesignValue: Double? {
frictionRateResponse?.frictionRate
frictionRate?.availableStaticPressure
}
private var shouldShowBadges: Bool {
frictionRateDesignValue != nil || availableStaticPressure != nil
frictionRate != nil
}
private var badgeColor: String {
let base = "badge-info"
guard let frictionRateDesignValue else { return base }
if frictionRateDesignValue >= 0.18 || frictionRateDesignValue <= 0.02 {
guard let frictionRate = frictionRate?.value else { return base }
if frictionRate >= 0.18 || frictionRate <= 0.02 {
return "badge-error"
}
return base
}
private var showHighErrors: Bool {
guard let frictionRateDesignValue else { return false }
return frictionRateDesignValue >= 0.18
guard let frictionRate = frictionRate?.value else { return false }
return frictionRate >= 0.18
}
private var showLowErrors: Bool {
guard let frictionRateDesignValue else { return false }
return frictionRateDesignValue <= 0.02
guard let frictionRate = frictionRate?.value else { return false }
return frictionRate <= 0.02
}
private var showNoComponentLossesError: Bool {
@@ -47,7 +43,7 @@ struct FrictionRateView: HTML, Sendable {
}
private var showIncompleteSectionsError: Bool {
availableStaticPressure == nil || frictionRateDesignValue == nil
availableStaticPressure == nil || frictionRate?.value == nil
}
private var hasAlerts: Bool {
@@ -68,11 +64,11 @@ struct FrictionRateView: HTML, Sendable {
div(.class("space-y-2 justify-end font-bold text-lg")) {
if shouldShowBadges {
if let frictionRateDesignValue {
if let frictionRate = frictionRate?.value {
LabeledContent {
span { "Friction Rate Design Value" }
} content: {
Badge(number: frictionRateDesignValue, digits: 2)
Badge(number: frictionRate, digits: 2)
.attributes(.class("\(badgeColor) badge-lg"))
.bold()
}

View File

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