WIP: Using project detail for duct size calculations, fix trunk sizes querying database for room data, it's now eagerly loaded.

This commit is contained in:
2026-01-27 08:59:36 -05:00
parent 1663c0a514
commit 066b3003d0
4 changed files with 192 additions and 58 deletions

View File

@@ -41,7 +41,17 @@ extension DatabaseClient.Projects: TestDependencyKey {
.with(\.$equipment) .with(\.$equipment)
.with(\.$equivalentLengths) .with(\.$equivalentLengths)
.with(\.$rooms) .with(\.$rooms)
.with(\.$trunks, { $0.with(\.$rooms) }) .with(
\.$trunks,
{ trunk in
trunk.with(
\.$rooms,
{
$0.with(\.$room)
}
)
}
)
.filter(\.$id == id) .filter(\.$id == id)
.first() .first()
else { else {
@@ -51,7 +61,7 @@ extension DatabaseClient.Projects: TestDependencyKey {
// TODO: Different error ?? // TODO: Different error ??
guard let equipmentInfo = model.equipment else { return nil } guard let equipmentInfo = model.equipment else { return nil }
let trunks = try await model.trunks.toDTO(on: database) let trunks = try model.trunks.toDTO()
return try .init( return try .init(
project: model.toDTO(), project: model.toDTO(),

View File

@@ -41,7 +41,8 @@ extension DatabaseClient.TrunkSizes: TestDependencyKey {
type: request.type type: request.type
) )
try await model.save(on: database) try await model.save(on: database)
try await roomProxies.append(model.toDTO(on: database)) // TODO: This `model.toDTO()` may not work now, need to check.
try await roomProxies.append(model.toDTO())
} }
return try .init( return try .init(
@@ -60,23 +61,30 @@ extension DatabaseClient.TrunkSizes: TestDependencyKey {
fetch: { projectID in fetch: { projectID in
try await TrunkModel.query(on: database) try await TrunkModel.query(on: database)
.with(\.$project) .with(\.$project)
.with(\.$rooms) .with(\.$rooms, { $0.with(\.$room) })
.filter(\.$project.$id == projectID) .filter(\.$project.$id == projectID)
.all() .all()
.toDTO(on: database) .toDTO()
}, },
get: { id in get: { id in
guard let model = try await TrunkModel.find(id, on: database) else { guard
let model =
try await TrunkModel
.query(on: database)
.with(\.$rooms, { $0.with(\.$room) })
.filter(\.$id == id)
.first()
else {
return nil return nil
} }
return try await model.toDTO(on: database) return try await model.toDTO()
}, },
update: { id, updates in update: { id, updates in
guard guard
let model = let model =
try await TrunkModel try await TrunkModel
.query(on: database) .query(on: database)
.with(\.$rooms) .with(\.$rooms, { $0.with(\.$room) })
.filter(\.$id == id) .filter(\.$id == id)
.first() .first()
else { else {
@@ -84,7 +92,7 @@ extension DatabaseClient.TrunkSizes: TestDependencyKey {
} }
try updates.validate() try updates.validate()
try await model.applyUpdates(updates, on: database) try await model.applyUpdates(updates, on: database)
return try await model.toDTO(on: database) return try model.toDTO()
} }
) )
} }
@@ -201,10 +209,10 @@ final class TrunkRoomModel: Model, @unchecked Sendable {
self.type = type.rawValue self.type = type.rawValue
} }
func toDTO(on database: any Database) async throws -> TrunkSize.RoomProxy { func toDTO() throws -> TrunkSize.RoomProxy {
guard let room = try await RoomModel.find($room.id, on: database) else { // guard let room = try await RoomModel.find($room.id, on: database) else {
throw NotFoundError() // throw NotFoundError()
} // }
return .init( return .init(
room: try room.toDTO(), room: try room.toDTO(),
registers: registers registers: registers
@@ -251,18 +259,22 @@ final class TrunkModel: Model, @unchecked Sendable {
self.name = name self.name = name
} }
func toDTO(on database: any Database) async throws -> TrunkSize { func toDTO() throws -> TrunkSize {
let rooms = try await withThrowingTaskGroup(of: TrunkSize.RoomProxy.self) { group in // let rooms = try await withThrowingTaskGroup(of: TrunkSize.RoomProxy.self) { group in
for room in self.rooms { // for room in self.rooms {
group.addTask { // group.addTask {
try await room.toDTO(on: database) // try await room.toDTO(on: database)
} // }
} // }
//
return try await group.reduce(into: [TrunkSize.RoomProxy]()) { // return try await group.reduce(into: [TrunkSize.RoomProxy]()) {
$0.append($1) // $0.append($1)
} // }
//
// }
let rooms = try rooms.reduce(into: [TrunkSize.RoomProxy]()) {
$0.append(try $1.toDTO())
} }
return try .init( return try .init(
@@ -340,17 +352,17 @@ final class TrunkModel: Model, @unchecked Sendable {
extension Array where Element == TrunkModel { extension Array where Element == TrunkModel {
func toDTO(on database: any Database) async throws -> [TrunkSize] { func toDTO() throws -> [TrunkSize] {
try await withThrowingTaskGroup(of: TrunkSize.self) { group in // try await withThrowingTaskGroup(of: TrunkSize.self) { group in
for model in self { // for model in self {
group.addTask { // group.addTask {
try await model.toDTO(on: database) // try await model.toDTO(on: database)
} // }
} // }
return try await group.reduce(into: [TrunkSize]()) { return try reduce(into: [TrunkSize]()) {
$0.append($1) $0.append(try $1.toDTO())
}
} }
} }
// }
} }

View File

@@ -5,6 +5,14 @@ import ManualDCore
extension DatabaseClient { extension DatabaseClient {
func calculateDuctSizes(
details: Project.Detail
) async throws -> (DuctSizes, DuctSizeSharedRequest) {
let (rooms, shared) = try await calculateRoomDuctSizes(details: details)
let (trunks, _) = try await calculateTrunkDuctSizes(details: details)
return (.init(rooms: rooms, trunks: trunks), shared)
}
func calculateDuctSizes( func calculateDuctSizes(
projectID: Project.ID projectID: Project.ID
) async throws -> (DuctSizes, DuctSizeSharedRequest, [Room]) { ) async throws -> (DuctSizes, DuctSizeSharedRequest, [Room]) {
@@ -24,6 +32,16 @@ extension DatabaseClient {
) )
} }
func calculateRoomDuctSizes(
details: Project.Detail
) async throws -> ([DuctSizes.RoomContainer], DuctSizeSharedRequest) {
@Dependency(\.manualD) var manualD
let shared = try sharedDuctRequest(details: details)
let rooms = try await manualD.calculateRoomSizes(rooms: details.rooms, sharedRequest: shared)
return (rooms, shared)
}
func calculateRoomDuctSizes( func calculateRoomDuctSizes(
projectID: Project.ID projectID: Project.ID
) async throws -> ([DuctSizes.RoomContainer], DuctSizeSharedRequest) { ) async throws -> ([DuctSizes.RoomContainer], DuctSizeSharedRequest) {
@@ -40,6 +58,20 @@ extension DatabaseClient {
) )
} }
func calculateTrunkDuctSizes(
details: Project.Detail
) async throws -> ([DuctSizes.TrunkContainer], DuctSizeSharedRequest) {
@Dependency(\.manualD) var manualD
let shared = try sharedDuctRequest(details: details)
let trunks = try await manualD.calculateTrunkSizes(
rooms: details.rooms,
trunks: details.trunks,
sharedRequest: shared
)
return (trunks, shared)
}
func calculateTrunkDuctSizes( func calculateTrunkDuctSizes(
projectID: Project.ID projectID: Project.ID
) async throws -> ([DuctSizes.TrunkContainer], DuctSizeSharedRequest) { ) async throws -> ([DuctSizes.TrunkContainer], DuctSizeSharedRequest) {
@@ -57,6 +89,32 @@ extension DatabaseClient {
) )
} }
func sharedDuctRequest(details: Project.Detail) throws -> DuctSizeSharedRequest {
guard
let dfrResponse = designFrictionRate(
componentLosses: details.componentLosses,
equipmentInfo: details.equipmentInfo,
equivalentLengths: details.maxContainer
)
else {
throw ProjectClientError("Project not complete.")
}
guard let projectSHR = details.project.sensibleHeatRatio else {
throw ProjectClientError("Project sensible heat ratio not set.")
}
let ensuredTEL = try dfrResponse.ensureMaxContainer()
return .init(
equipmentInfo: dfrResponse.equipmentInfo,
maxSupplyLength: ensuredTEL.supply,
maxReturnLenght: ensuredTEL.return,
designFrictionRate: dfrResponse.designFrictionRate,
projectSHR: projectSHR
)
}
func sharedDuctRequest(_ projectID: Project.ID) async throws -> DuctSizeSharedRequest { func sharedDuctRequest(_ projectID: Project.ID) async throws -> DuctSizeSharedRequest {
guard let dfrResponse = try await designFrictionRate(projectID: projectID) else { guard let dfrResponse = try await designFrictionRate(projectID: projectID) else {
@@ -107,25 +165,36 @@ extension DatabaseClient {
} }
func designFrictionRate( func designFrictionRate(
projectID: Project.ID componentLosses: [ComponentPressureLoss],
) async throws -> DesignFrictionRateResponse? { equipmentInfo: EquipmentInfo,
guard let equipmentInfo = try await equipment.fetch(projectID) else { equivalentLengths: EffectiveLength.MaxContainer
return nil ) -> DesignFrictionRateResponse? {
} guard let tel = equivalentLengths.total,
componentLosses.count > 0
else { return nil }
let equivalentLengths = try await effectiveLength.fetchMax(projectID) let availableStaticPressure = equipmentInfo.staticPressure - componentLosses.total
guard let tel = equivalentLengths.total else { return nil }
let componentLosses = try await componentLoss.fetch(projectID)
guard componentLosses.count > 0 else { return nil }
let availableStaticPressure =
equipmentInfo.staticPressure - componentLosses.total
return .init( return .init(
designFrictionRate: (availableStaticPressure * 100) / tel, designFrictionRate: (availableStaticPressure * 100) / tel,
equipmentInfo: equipmentInfo, equipmentInfo: equipmentInfo,
telMaxContainer: equivalentLengths telMaxContainer: equivalentLengths
) )
}
func designFrictionRate(
projectID: Project.ID
) async throws -> DesignFrictionRateResponse? {
guard let equipmentInfo = try await equipment.fetch(projectID) else {
return nil
}
return try await designFrictionRate(
componentLosses: componentLoss.fetch(projectID),
equipmentInfo: equipmentInfo,
equivalentLengths: effectiveLength.fetchMax(projectID)
)
} }
} }

View File

@@ -48,29 +48,72 @@ extension ProjectClient: DependencyKey {
extension DatabaseClient { extension DatabaseClient {
// fileprivate func makePdfRequest(_ projectID: Project.ID) async throws -> PdfClient.Request {
// @Dependency(\.manualD) var manualD
//
// guard let project = try await projects.get(projectID) else {
// throw ProjectClientError("Project not found. id: \(projectID)")
// }
// let frictionRateResponse = try await manualD.frictionRate(projectID: projectID)
// guard let frictionRate = frictionRateResponse.frictionRate else {
// throw ProjectClientError("Friction rate not found. id: \(projectID)")
// }
// let (ductSizes, sharedInfo, rooms) = try await calculateDuctSizes(projectID: projectID)
//
// return .init(
// project: project,
// rooms: rooms,
// componentLosses: frictionRateResponse.componentLosses,
// ductSizes: ductSizes,
// equipmentInfo: sharedInfo.equipmentInfo,
// maxSupplyTEL: sharedInfo.maxSupplyLength,
// maxReturnTEL: sharedInfo.maxReturnLenght,
// frictionRate: frictionRate,
// projectSHR: sharedInfo.projectSHR
// )
// }
fileprivate func makePdfRequest(_ projectID: Project.ID) async throws -> PdfClient.Request { fileprivate func makePdfRequest(_ projectID: Project.ID) async throws -> PdfClient.Request {
@Dependency(\.manualD) var manualD @Dependency(\.manualD) var manualD
guard let project = try await projects.get(projectID) else { guard let projectDetails = try await projects.detail(projectID) else {
throw ProjectClientError("Project not found. id: \(projectID)") throw ProjectClientError("Project not found. id: \(projectID)")
} }
let frictionRateResponse = try await manualD.frictionRate(projectID: projectID)
let (ductSizes, shared) = try await calculateDuctSizes(details: projectDetails)
let frictionRateResponse = try await manualD.frictionRate(details: projectDetails)
guard let frictionRate = frictionRateResponse.frictionRate else { guard let frictionRate = frictionRateResponse.frictionRate else {
throw ProjectClientError("Friction rate not found. id: \(projectID)") throw ProjectClientError("Friction rate not found. id: \(projectID)")
} }
let (ductSizes, sharedInfo, rooms) = try await calculateDuctSizes(projectID: projectID)
return .init( return .init(
project: project, details: projectDetails,
rooms: rooms,
componentLosses: frictionRateResponse.componentLosses,
ductSizes: ductSizes, ductSizes: ductSizes,
equipmentInfo: sharedInfo.equipmentInfo, shared: shared,
maxSupplyTEL: sharedInfo.maxSupplyLength, frictionRate: frictionRate
maxReturnTEL: sharedInfo.maxReturnLenght,
frictionRate: frictionRate,
projectSHR: sharedInfo.projectSHR
) )
} }
} }
extension PdfClient.Request {
init(
details: Project.Detail,
ductSizes: DuctSizes,
shared: DuctSizeSharedRequest,
frictionRate: FrictionRate
) {
self.init(
project: details.project,
rooms: details.rooms,
componentLosses: details.componentLosses,
ductSizes: ductSizes,
equipmentInfo: details.equipmentInfo,
maxSupplyTEL: shared.maxSupplyLength,
maxReturnTEL: shared.maxReturnLenght,
frictionRate: frictionRate,
projectSHR: shared.projectSHR
)
}
}