diff --git a/package-lock.json b/package-lock.json index 0fb6f782..76e5e89b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1945,15 +1945,16 @@ } }, "@speckle/viewer": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@speckle/viewer/-/viewer-2.11.4.tgz", - "integrity": "sha512-SMsjDFlaK2QTTLH3n6LymgTLoRiEmy4jA650PhTRQvuFx1iExUOxv+DcMmu+6li4Rd/6RD3uICMbyzZ2jqcKcA==", + "version": "2.12.5", + "resolved": "https://registry.npmjs.org/@speckle/viewer/-/viewer-2.12.5.tgz", + "integrity": "sha512-/uJ4Tc10RgWrSOndCNpQvTx6kfEShrxfdoMoW3/PUvqCsIpb/XycrgvS5KaRhEutQ+4HL/KM2ZvhxM41uWV1Iw==", "requires": { - "@speckle/objectloader": "^2.11.4", + "@speckle/objectloader": "^2.12.5", "@types/flat": "^5.0.2", "camera-controls": "^1.33.1", "flat": "^5.0.2", "hold-event": "^0.1.0", + "js-logger": "1.6.1", "lodash-es": "^4.17.21", "rainbowvis.js": "^1.0.1", "string-to-color": "^2.2.2", @@ -1962,6 +1963,26 @@ "tree-model": "1.0.7" }, "dependencies": { + "@speckle/objectloader": { + "version": "2.12.5", + "resolved": "https://registry.npmjs.org/@speckle/objectloader/-/objectloader-2.12.5.tgz", + "integrity": "sha512-6y2DGNJyFg2PVrmWjbgjDzytH0Vy1j8uQb0EX/xEDfXGj+LVYxRyNyUs7mW+wxS2cTmwjZqhk9LhwVzwGaaM7w==", + "requires": { + "@babel/core": "^7.17.9", + "@speckle/shared": "^2.12.5", + "core-js": "^3.21.1", + "regenerator-runtime": "^0.13.7" + } + }, + "@speckle/shared": { + "version": "2.12.5", + "resolved": "https://registry.npmjs.org/@speckle/shared/-/shared-2.12.5.tgz", + "integrity": "sha512-m0lAbY6q0cRcO6DzpfGI9/hlnid8Xjtj6JEZgjCKmxLhCequnYqa8f52dJn/kcxh4iGIEU2FsTyFK51cOhgFzA==", + "requires": { + "lodash": "^4.17.0", + "pino": "^8.7.0" + } + }, "three": { "version": "0.140.2", "resolved": "https://registry.npmjs.org/three/-/three-0.140.2.tgz", @@ -2769,29 +2790,6 @@ "webpack-merge": "^5.7.3", "webpack-virtual-modules": "^0.4.2", "whatwg-fetch": "^3.6.2" - }, - "dependencies": { - "@vue/vue-loader-v15": { - "version": "npm:vue-loader@15.10.1", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.1.tgz", - "integrity": "sha512-SaPHK1A01VrNthlix6h1hq4uJu7S/z0kdLUb6klubo738NeQoLbS6V9/d8Pv19tU0XdQKju3D1HSKuI8wJ5wMA==", - "dev": true, - "requires": { - "@vue/component-compiler-utils": "^3.1.0", - "hash-sum": "^1.0.2", - "loader-utils": "^1.1.0", - "vue-hot-reload-api": "^2.3.0", - "vue-style-loader": "^4.1.0" - }, - "dependencies": { - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", - "dev": true - } - } - } } }, "@vue/cli-shared-utils": { @@ -2959,6 +2957,27 @@ "pretty": "^2.0.0" } }, + "@vue/vue-loader-v15": { + "version": "npm:vue-loader@15.10.1", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.1.tgz", + "integrity": "sha512-SaPHK1A01VrNthlix6h1hq4uJu7S/z0kdLUb6klubo738NeQoLbS6V9/d8Pv19tU0XdQKju3D1HSKuI8wJ5wMA==", + "dev": true, + "requires": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + } + } + }, "@vue/vue2-jest": { "version": "27.0.0", "resolved": "https://registry.npmjs.org/@vue/vue2-jest/-/vue2-jest-27.0.0.tgz", @@ -3823,9 +3842,9 @@ "dev": true }, "camera-controls": { - "version": "1.36.2", - "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-1.36.2.tgz", - "integrity": "sha512-wF8xeLKtCxZMdKf37gQQta+iSSjTBmmI6xYxq5fESj7CR4UTiYHFuTuXzDogCf8ONYyWc6iunyMTc8ta6HtJAA==" + "version": "1.38.2", + "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-1.38.2.tgz", + "integrity": "sha512-EfzbovxLssyWpJVG9uKcazSDDIEcd1hUsPhPF/OWWnICsKY9WbLY/2S4UPW73HHbvnVeR/Z9wsWaQKtANy/2Yg==" }, "caniuse-api": { "version": "3.0.0", @@ -8339,6 +8358,11 @@ } } }, + "js-logger": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/js-logger/-/js-logger-1.6.1.tgz", + "integrity": "sha512-yTgMCPXVjhmg28CuUH8CKjU+cIKL/G+zTu4Fn4lQxs8mRFH/03QTNvEFngcxfg/gRDiQAOoyCKmMTOm9ayOzXA==" + }, "js-message": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", diff --git a/package.json b/package.json index 2dff6c57..b3873c3c 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@speckle/objectloader": "^2.7.1", - "@speckle/viewer": "2.11.4", + "@speckle/viewer": "2.12.5", "@types/three": "^0.136.1", "@vue/vue2-jest": "^27.0.0", "axios": "^0.24.0", diff --git a/src/components/assessment/Menu2.vue b/src/components/assessment/Menu2.vue index 19e529cc..a4edc288 100644 --- a/src/components/assessment/Menu2.vue +++ b/src/components/assessment/Menu2.vue @@ -47,14 +47,16 @@ export default class Menu2 extends Vue { @Prop() objectGroups!: string[]; @Prop() defaultGroup!: string; - groupKey = 0; - defaultGroupSet = false; - objectGroup = this.defaultGroup ? this.defaultGroup : "Object Type"; - @Watch("defaultGroup") - defaultGroupChange() { - this.objectGroup = this.defaultGroup; + _objectGroup = ""; + get objectGroup() { + return this.defaultGroup; + } + set objectGroup(val: string) { + this._objectGroup = val; } + groupKey = 0; + get loadedTypes() { return this.types ? this.types : []; } @@ -77,7 +79,7 @@ export default class Menu2 extends Vue { @Emit("groupSelected") groupSelected() { this.groupKey++; - return this.objectGroup; + return this._objectGroup; } } diff --git a/src/components/core/Footer.vue b/src/components/core/Footer.vue index 93721eeb..5cce4bec 100644 --- a/src/components/core/Footer.vue +++ b/src/components/core/Footer.vue @@ -16,9 +16,21 @@ - created by Arup + created by Arup
- powered by Speckle + powered by Speckle
diff --git a/src/components/core/VersionUpdateDialog.vue b/src/components/core/VersionUpdateDialog.vue index dfc5308b..b460340b 100644 --- a/src/components/core/VersionUpdateDialog.vue +++ b/src/components/core/VersionUpdateDialog.vue @@ -58,8 +58,11 @@ export default class VersionUpdateDialog extends Vue { features: { title: string; subtitle: string }[] = [ { - title: "Object parameter grouping", - subtitle: `You can now group your objects for material selection by any parameter on your model.`, + title: "Report Object v2", + subtitle: `The way that we store your carbon reports has now changed. The update allows for faster loading, carbon heat maps, and gives you better access to your carbon report data in other apps that support Speckle connectors.`, + }, { + title: "Carbon Heat Map", + subtitle: `View a carbon heat map of your model after running a new report` } ]; diff --git a/src/components/shared/Renderer.vue b/src/components/shared/Renderer.vue index 07ca38a0..ff5336b3 100644 --- a/src/components/shared/Renderer.vue +++ b/src/components/shared/Renderer.vue @@ -93,7 +93,7 @@ function instanceOfStringPropertyInfo( } @Component({ - components: { RendererLightingOptions } + components: { RendererLightingOptions }, }) export default class extends Vue { @Prop() objecturls!: string[]; @@ -126,9 +126,12 @@ export default class extends Vue { } @Watch("colors") - onObjectColorChanged(value: Color[]) { + async onObjectColorChanged(value: Color[]) { if (value.length === 0 || this.gradientColorProperty) this.resetColors(); - else this.setColors(value); + else { + await this.resetColors(); + this.setColors(value); + } } @Watch("selectedIds") @@ -136,12 +139,12 @@ export default class extends Vue { this.setSelect(); } - @Watch("gradientColorProperty") + @Watch("gradientColorProperty", { deep: true }) async onGradientChange(value: GradientColor) { if (value) { const propertyData = this.viewer.getObjectProperties(); const data = propertyData.find((v) => v.key === value.property); - if (data) this.viewer.setColorFilter(data); + if (data) await this.viewer.setColorFilter(data); } } @@ -156,7 +159,6 @@ export default class extends Vue { this.renderStream(this.objecturls); } async beforeDestroy() { - await this.viewer.cancelLoad(this.objecturls[0], true); await this.viewer.unloadAll(); } @Watch("objecturls") @@ -169,6 +171,7 @@ export default class extends Vue { async renderStream(objecturls: string[]) { if (this.$store.state.speckleViewer.viewer) { this.viewer = this.$store.state.speckleViewer.viewer; + await this.viewer.unloadAll(); this.domElement = this.$store.state.speckleViewer.container; @@ -190,6 +193,8 @@ export default class extends Vue { showStats: false, }); + await this.viewer.unloadAll(); + this.$store.commit("setSpeckleViewer", { viewer: this.viewer, container: this.domElement, @@ -335,7 +340,7 @@ export default class extends Vue { objectIds: c[1], color: c[0], })); - const res = await this.viewer.setUserObjectColors( + await this.viewer.setUserObjectColors( groups as [{ objectIds: string[]; color: string }] ); } diff --git a/src/components/viewAssessment/MaterialBreakdownCard.vue b/src/components/viewAssessment/MaterialBreakdownCard.vue index 92be6184..687469e1 100644 --- a/src/components/viewAssessment/MaterialBreakdownCard.vue +++ b/src/components/viewAssessment/MaterialBreakdownCard.vue @@ -8,7 +8,7 @@ diff --git a/src/models/graphql/StreamData.interface.ts b/src/models/graphql/StreamData.interface.ts index 469951fd..53c0d484 100644 --- a/src/models/graphql/StreamData.interface.ts +++ b/src/models/graphql/StreamData.interface.ts @@ -1,6 +1,9 @@ import { ProjectDataComplete } from "../newAssessment"; import { CarbonA5 } from "../newAssessment/speckleObject.interface"; import { SpeckleObjectComplete } from "../newAssessment"; +import { ChartData } from "../chart"; +import { Color } from "../renderer"; +import { IdMapper } from "../../views/utils/add-params/addParams"; export interface StreamData { data: { @@ -18,7 +21,20 @@ export interface ReportObj { }; } -export interface HTTPStreamDataParent extends ParentSpeckleObjectData { +export type HTTPStreamDataParent = HTTPStreamDataParentV1 | HTTPStreamDataParentV2; + +export interface HTTPStreamDataParentV1 extends ParentSpeckleObjectDataV1 { + __closure: { [childId: string]: 1 }; +} + +export function instanceOfHttpStreamDataParentV1(object: any): object is HTTPStreamDataParentV1 { + return "__closure" in object && !("version" in object); +} +export function instanceOfHttpStreamDataParentV2(object: any): object is HTTPStreamDataParentV2 { + return "__closure" in object && "version" in object && object.version === "2.0.0"; +} + +export interface HTTPStreamDataParentV2 extends ParentSpeckleObjectDataV2 { __closure: { [childId: string]: 1 }; } @@ -28,7 +44,9 @@ export interface ChildSpeckleObjectData { act: SpeckleObjectComplete; } -export interface ParentSpeckleObjectData { +export type ParentSpeckleObjectData = ParentSpeckleObjectDataV1 | ParentSpeckleObjectDataV2; + +export interface ParentSpeckleObjectDataV1 { constructionCarbonA5: CarbonA5; id: string; productStageCarbonA1A3: number; @@ -41,3 +59,19 @@ export interface ParentSpeckleObjectData { volume: number; selectedObjectGroup: string; } + +export interface ParentSpeckleObjectDataV2 extends ParentSpeckleObjectDataV1 { + version: "2.0.0"; + materials: ChartData[]; + materialsColors: Color[]; + transportColors: Color[]; + idMapper: IdMapper; + "@children": { + referencedId: string; + speckle_type: "reference"; + }[]; + "@model": { + referencedId: string; + speckle_type: "reference"; + }[]; +} diff --git a/src/models/graphql/index.ts b/src/models/graphql/index.ts index 0cdae46e..c27300af 100644 --- a/src/models/graphql/index.ts +++ b/src/models/graphql/index.ts @@ -3,7 +3,7 @@ export { StreamReferenceBranches, BranchItem, } from "./StreamReferenceBranches.interface"; -export { StreamData, HTTPStreamDataParent } from "./StreamData.interface"; +export * from "./StreamData.interface"; export { ActReportData } from "./ActReportData.interface"; export { DeleteStreamData } from "./DeleteStreamData.interface"; export { CheckContainsBranch } from "./CheckContainsBranch.interface"; diff --git a/src/models/landing/BranchData.interface.ts b/src/models/landing/BranchData.interface.ts index d8ed0d0c..f461c8ad 100644 --- a/src/models/landing/BranchData.interface.ts +++ b/src/models/landing/BranchData.interface.ts @@ -1,4 +1,4 @@ -import { LoadStreamOut } from "@/views/utils/viewAssessmentUtils"; +import { LoadStreamOut } from "@/views/utils/process-report-object"; export interface BranchData { id: string; diff --git a/src/models/newAssessment/speckleObject.interface.ts b/src/models/newAssessment/speckleObject.interface.ts index e93163e0..281090ff 100644 --- a/src/models/newAssessment/speckleObject.interface.ts +++ b/src/models/newAssessment/speckleObject.interface.ts @@ -1,5 +1,8 @@ import { MaterialFull } from "@/store/utilities/material-carbon-factors"; import { TransportType } from "."; +import { ChartData } from "../chart"; +import { IABreakdown } from "@/views/utils/process-report-object"; +import { Color } from "../renderer"; export type ObjectsObj = { [id: string]: SpeckleObject }; @@ -7,7 +10,7 @@ export interface SpeckleObject { id: string; speckle_type: string; formData?: ObjectFormData; - reportData?: ReportData; + reportData?: ReportDataChild; } export interface ObjectFormData { @@ -21,7 +24,7 @@ export interface SpeckleObjectFormComplete { id: string; speckle_type: string; formData: ObjectFormDataComplete; - reportData?: ReportData; + reportData?: ReportDataChild; } export interface ObjectFormDataComplete { @@ -35,7 +38,7 @@ export interface SpeckleObjectComplete { id: string; speckle_type: string; formData: ObjectFormDataComplete; - reportData: ReportData; + reportData: ReportDataChild; } export interface ReportProp { @@ -45,19 +48,26 @@ export interface ReportProp { export type ReportPassdown = ReportProp | false; -export interface ReportData { +export interface ReportDataBase { transportCarbonA4: number; productStageCarbonA1A3: number; constructionCarbonA5: CarbonA5; } +export interface ReportDataChild extends ReportDataBase { + totalCarbon: number; +} + export interface CarbonA5 { value: number; waste: number; site: number; } -export interface ReportTotals extends ReportData { +export interface ReportTotals extends ReportDataBase { totalCO2: number; volume: number; + materials: ChartData[]; + materialsColors: Color[]; + transportColors: Color[]; } diff --git a/src/models/renderer/Gradient.model.ts b/src/models/renderer/Gradient.model.ts index 3a078702..ab97825d 100644 --- a/src/models/renderer/Gradient.model.ts +++ b/src/models/renderer/Gradient.model.ts @@ -1,7 +1,4 @@ export type GradientColor = Gradient | null; export interface Gradient { property: string; - minValue: number; - maxValue: number; - colors: string[]; } diff --git a/src/store/index.ts b/src/store/index.ts index 646658e1..92236e7f 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,11 +1,10 @@ import Vue from "vue"; import Vuex from "vuex"; import * as speckleUtil from "./speckle/speckleUtil"; +import { LoadStreamOut, getChildren } from "@/views/utils/process-report-object"; import { - loadStream, - LoadStreamOut, loadParent, - getChildren, + loadStream, } from "@/views/utils/viewAssessmentUtils"; import { Login, @@ -31,7 +30,10 @@ import { } from "@/models/newAssessment"; import { BECName } from "@/models/shared"; -import { ParentSpeckleObjectData } from "@/models/graphql/StreamData.interface"; +import { + ParentSpeckleObjectData, + ParentSpeckleObjectDataV2, +} from "@/models/graphql/StreamData.interface"; import { filterOnlyReportBranches } from "./utilities/filters"; import { StreamNameBranches, @@ -39,17 +41,23 @@ import { StreamReferenceObjects, } from "@/models/graphql"; import { BranchItem } from "@/models/graphql/StreamReferenceBranches.interface"; +import { + AddParamsModel, + IChildObject, + IdMapper, + IParamsParent, +} from "@/views/utils/add-params/addParams"; import { ExcelData, exportToMaterial } from "@/views/utils/ExcelImportUtils"; Vue.use(Vuex); export default new Vuex.Store({ state: { - version: "0.10.0 \u00DF", + version: "0.11.0 \u00DF", speckleFolderName: "actcarbonreport", speckleViewer: { viewer: undefined, - container: undefined + container: undefined, }, servers: { arup: { @@ -414,12 +422,13 @@ export default new Vuex.Store({ }, async loadActReportData( context, - { streamId, branchName }: LoadActReportDataInput + { streamId, branchName, loadChildren }: LoadActReportDataInput ) { return await loadStream( context, streamId, - `${context.state.speckleFolderName}/${branchName}` + `${context.state.speckleFolderName}/${branchName}`, + loadChildren ); }, async carbonStreams(context) { @@ -436,46 +445,34 @@ export default new Vuex.Store({ setDarkMode({ commit }) { commit("setDarkMode"); }, - async getObjectDetails( context, { streamid, objecturl }: ObjectDetailsInput - ) { + ): Promise { const objectid = objecturl.split("/")[objecturl.split("/").length - 1]; - const response = await fetch( + const parent: IParamsParent = await fetch( `${context.state.selectedServer.url}/objects/${streamid}/${objectid}/single`, { + method: "GET", headers: { - Accept: "text/plain", + "Content-Type": "application/json", Authorization: `Bearer ${context.state.token.token}`, }, } - ); - const rawObj = await response.text(); - const rootObj = JSON.parse(rawObj); + ).then((d) => d.json()); - const childrenIds = Object.keys(rootObj.__closure).sort( - (a, b) => rootObj.__closure[a] - rootObj.__closure[b] + const children = await getChildren( + context.state.selectedServer.url, + context.state.token.token, + streamid, + parent ); - const childrenObjects = await fetch( - `${context.state.selectedServer.url}/api/getobjects/${streamid}`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${context.state.token.token}`, - }, - body: JSON.stringify({ objects: JSON.stringify(childrenIds) }), - } - ) - .then((res) => res.json()) - .then((data) => { - return data; - }); - - return childrenObjects; + return { + parent, + children, + }; }, async uploadReport( context, @@ -485,11 +482,32 @@ export default new Vuex.Store({ reportTotals, projectData, branchName, + newModel, selectedObjectGroup, }: UploadReportInput ) { + if (newModel) { + const formData = new FormData(); + formData.append( + "batch1", + new Blob([JSON.stringify([newModel.parent, ...newModel.children])]) + ); + await fetch(`${context.state.selectedServer.url}/objects/${streamid}`, { + method: "POST", + headers: { + Authorization: `Bearer ${context.state.token.token}`, + }, + body: formData, + }); + } + branchName = `${context.state.speckleFolderName}/${branchName}`; + const objectIds = await speckleUtil.getStreamObjects(context, streamid); + const modelId = newModel + ? newModel.parent.id + : objectIds.data.stream.branch.commits.items[0].referencedObject; + // TODO: ADD ERROR HANDLING const uploadObjectsRes: UploadObjectsRes = await speckleUtil.uploadObjects(context, streamid, objects); @@ -497,8 +515,9 @@ export default new Vuex.Store({ const formData = new FormData(); // below line means that some objects may be given duplicate strings and the report won't save properly const objectid = `${new Date().getTime().toString()}-act`; - const objectData: ParentSpeckleObjectData = { + const objectData: ParentSpeckleObjectDataV2 = { id: objectid, + version: "2.0.0", speckleType: "act-totals", speckle_type: "act-totals", transportCarbonA4: reportTotals.transportCarbonA4, @@ -506,10 +525,25 @@ export default new Vuex.Store({ constructionCarbonA5: reportTotals.constructionCarbonA5, totalCO2: reportTotals.totalCO2, volume: reportTotals.volume, + materials: reportTotals.materials, + materialsColors: reportTotals.materialsColors, + transportColors: reportTotals.transportColors, projectData, totalChildrenCount: 0, + idMapper: newModel ? newModel.idMapper : ({} as IdMapper), + "@children": children.map((child) => ({ + speckle_type: "reference", + referencedId: child, + })), + "@model": [ + { + speckle_type: "reference", + referencedId: modelId, + }, + ], selectedObjectGroup }; + children.push(modelId); // add model id to children array so it gets added to __closure properly formData.append( "batch1", new Blob([ @@ -594,7 +628,12 @@ export default new Vuex.Store({ branches.map(async (branch): Promise => { const fullBranchName = `${context.state.speckleFolderName}/${branch.name}`; - const data = await loadStream(context, streamid, fullBranchName); + const data = await loadStream( + context, + streamid, + fullBranchName, + false + ); return { branch, data, @@ -742,6 +781,16 @@ export default new Vuex.Store({ modules: {}, }); +export interface GetParentObjectInput { + streamid: string; + objecturl: string; +} + +export interface GetObjectDetailsOut { + parent: IParamsParent; + children: IChildObject[]; +} + export interface Region { key: string; name: string; @@ -815,6 +864,7 @@ export interface GetActReportBranchInfoInput { export interface LoadActReportDataInput { streamId: string; branchName: string; + loadChildren?: boolean; } export interface CheckContainsChlidReportInput { @@ -880,10 +930,11 @@ export interface UploadReportInput { reportTotals: ReportTotals; projectData: ProjectDataComplete; branchName: string; + newModel: AddParamsModel | undefined; selectedObjectGroup: string; } -interface ObjectDetailsInput { +export interface ObjectDetailsInput { streamid: string; objecturl: string; } diff --git a/src/views/Assessment.vue b/src/views/Assessment.vue index e5f54118..533d2c75 100644 --- a/src/views/Assessment.vue +++ b/src/views/Assessment.vue @@ -1,6 +1,6 @@