Skip to content

Commit

Permalink
feat: v2 export of DataSets and globalValues (#1680)
Browse files Browse the repository at this point in the history
  • Loading branch information
kswenson authored Dec 11, 2024
1 parent bad513e commit efe1872
Show file tree
Hide file tree
Showing 18 changed files with 224 additions and 118 deletions.
34 changes: 27 additions & 7 deletions v3/src/data-interactive/data-interactive-type-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IGlobalValue } from "../models/global/global-value"
import { getSharedCaseMetadataFromDataset } from "../models/shared/shared-data-utils"
import { kAttrIdPrefix, maybeToV2Id, toV2Id, toV3AttrId } from "../utilities/codap-utils"
import {
ICodapV2Attribute, ICodapV2CollectionV3, ICodapV2DataContextV3, v3TypeFromV2TypeString
ICodapV2Attribute, ICodapV2Case, ICodapV2CollectionV3, ICodapV2DataContextV3, v3TypeFromV2TypeString
} from "../v2/codap-v2-types"
import { DIAttribute, DIGetCaseResult, DIResources, DISingleValues } from "./data-interactive-types"
import { getCaseValues } from "./data-interactive-utils"
Expand Down Expand Up @@ -139,17 +139,36 @@ export function convertAttributeToV2FromResources(resources: DIResources) {
}
}

export function convertCollectionToV2(collection: ICollectionModel, dataContext?: IDataSet): ICodapV2CollectionV3 {
interface CCV2Options {
dataSet?: IDataSet
exportCases?: boolean
}
export function convertCollectionToV2(collection: ICollectionModel, options?: CCV2Options): ICodapV2CollectionV3 {
const { name, title, id, labels: _labels } = collection
const { dataSet, exportCases } = options || {}
const v2Id = toV2Id(id)
const labels = _labels ? getSnapshot(_labels) : undefined
const attrs = collection.attributes.map(attribute => {
if (attribute) return convertAttributeToV2(attribute, dataContext)
if (attribute) return convertAttributeToV2(attribute, dataSet)
}).filter(attr => !!attr)

let cases: Maybe<{ cases: ICodapV2Case[] }>
if (exportCases) {
cases = {
cases: collection.cases.map(aCase => {
const v2CaseId = toV2Id(aCase.__id__)
const values: ICodapV2Case["values"] = {}
collection.allDataAttributes.forEach(attr => {
values[attr.name] = dataSet?.getValue(aCase.__id__, attr.id) ?? ""
})
return { guid: v2CaseId, id: v2CaseId, values }
})
}
}
return {
// areParentChildLinksConfigured,
attrs,
// cases,
...cases,
// caseName,
// childAttrName,
// collapseChildren,
Expand All @@ -163,16 +182,17 @@ export function convertCollectionToV2(collection: ICollectionModel, dataContext?
}
}

export function convertDataSetToV2(dataSet: IDataSet, docId: number | string): ICodapV2DataContextV3 {
export function convertDataSetToV2(dataSet: IDataSet, exportCases = false): ICodapV2DataContextV3 {
const { name, title, id, description } = dataSet
const v2Id = toV2Id(id)
dataSet.validateCases()

const collections: ICodapV2CollectionV3[] =
dataSet.collections.map(collection => convertCollectionToV2(collection, dataSet))
dataSet.collections.map(collection => convertCollectionToV2(collection, { dataSet, exportCases }))

return {
type: "DG.DataContext",
document: docId,
document: 1,
guid: v2Id,
id: v2Id,
// flexibleGroupChangeFlag,
Expand Down
14 changes: 7 additions & 7 deletions v3/src/data-interactive/handlers/all-cases-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe("DataInteractive AllCasesHandler", () => {
}
const getCase = (result: GetAllCasesResult, index: number) =>
result.values.cases![index].case!
const checkCase = (c: any, attribute: string, value: string, children: number, parent?: number) => {
const checkCase = (c: any, attribute: string, value: number | string, children: number, parent?: number) => {
expect(c.values[attribute]).toBe(value)
expect(c.children.length).toBe(children)
expect(c.parent).toBe(parent)
Expand Down Expand Up @@ -49,11 +49,11 @@ describe("DataInteractive AllCasesHandler", () => {
const result3 = handler.get?.({ dataContext: dataset, collection: dataset?.childCollection }) as GetAllCasesResult
expect(result3.success).toBe(true)
expect(result3.values.cases?.length).toBe(6)
checkCase(getCase(result3, 0), "a3", "1", 0, c2c1.id)
checkCase(getCase(result3, 1), "a3", "5", 0, c2c1.id)
checkCase(getCase(result3, 2), "a3", "3", 0, c2c2.id)
checkCase(getCase(result3, 3), "a3", "2", 0, c2c3.id)
checkCase(getCase(result3, 4), "a3", "6", 0, c2c3.id)
checkCase(getCase(result3, 5), "a3", "4", 0, c2c4.id)
checkCase(getCase(result3, 0), "a3", 1, 0, c2c1.id)
checkCase(getCase(result3, 1), "a3", 5, 0, c2c1.id)
checkCase(getCase(result3, 2), "a3", 3, 0, c2c2.id)
checkCase(getCase(result3, 3), "a3", 2, 0, c2c3.id)
checkCase(getCase(result3, 4), "a3", 6, 0, c2c3.id)
checkCase(getCase(result3, 5), "a3", 4, 0, c2c4.id)
})
})
2 changes: 1 addition & 1 deletion v3/src/data-interactive/handlers/case-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe("DataInteractive CaseHandler", () => {
const caseId0 = dataset.items[0].__id__
const result = handler.update?.({ dataContext: dataset }, { id: toV2Id(caseId0), values: { a3: 10 } } as DIValues)
expect(result?.success).toBe(true)
expect(dataset.getAttributeByName("a3")?.value(0)).toBe("10")
expect(dataset.getAttributeByName("a3")?.value(0)).toBe(10)
expect((result as DISuccessResult).caseIDs?.includes(toV2Id(caseId0))).toBe(true)

// Update multiple cases
Expand Down
2 changes: 1 addition & 1 deletion v3/src/data-interactive/handlers/collection-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export const diCollectionHandler: DIHandler = {
if (!dataContext) return dataContextNotFoundResult
if (!collection) return collectionNotFoundResult

const v2Collection = convertCollectionToV2(collection, dataContext)
const v2Collection = convertCollectionToV2(collection, { dataSet: dataContext })
return {
success: true,
values: v2Collection
Expand Down
10 changes: 5 additions & 5 deletions v3/src/data-interactive/handlers/data-context-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { findTileFromNameOrId } from "../resource-parser-utils"
import { createCollection } from "./di-handler-utils"
import { attributeNotFoundResult, dataContextNotFoundResult, errorResult, fieldRequiredResult } from "./di-results"

const requestRequiedResult = fieldRequiredResult("Notify", "dataContext", "request")
const requestRequiredResult = fieldRequiredResult("Notify", "dataContext", "request")

export const diDataContextHandler: DIHandler = {
create(_resources: DIResources, _values?: DIValues) {
Expand Down Expand Up @@ -73,16 +73,16 @@ export const diDataContextHandler: DIHandler = {
const { dataContext } = resources
if (!dataContext) return dataContextNotFoundResult

return { success: true, values: convertDataSetToV2(dataContext, appState.document.key) }
return { success: true, values: convertDataSetToV2(dataContext) }
},

notify(resources: DIResources, values?: DIValues) {
const { dataContext } = resources
if (!dataContext) return dataContextNotFoundResult

if (!values) return requestRequiedResult
if (!values) return requestRequiredResult
const { caseIDs, operation, request } = values as DINotifyDataContext
if (!request) return requestRequiedResult
if (!request) return requestRequiredResult

const successResult = { success: true as const, values: {} }
if (request === "setAside") {
Expand All @@ -100,7 +100,7 @@ export const diDataContextHandler: DIHandler = {
} else {
addSetAsideCases(dataContext, v3CaseIds, false)
}

return successResult
}

Expand Down
68 changes: 34 additions & 34 deletions v3/src/models/data/attribute.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,52 +114,52 @@ describe("Attribute", () => {

attribute.addValue("1")
expect(attribute.length).toBe(1)
expect(attribute.value(0)).toBe("1")
expect(attribute.numeric(0)).toBe(1)
expect(attribute.value(0)).toBe(1)
expect(attribute.numValue(0)).toBe(1)

attribute.addValues([2, 3])
expect(attribute.length).toBe(3)
expect(attribute.value(1)).toBe("2")
expect(attribute.value(2)).toBe("3")
expect(attribute.numeric(1)).toBe(2)
expect(attribute.numeric(2)).toBe(3)
expect(attribute.value(1)).toBe(2)
expect(attribute.value(2)).toBe(3)
expect(attribute.numValue(1)).toBe(2)
expect(attribute.numValue(2)).toBe(3)
expect(attribute.getNumericCount()).toBe(3)

attribute.addValue(0, 0)
expect(attribute.length).toBe(4)
expect(attribute.value(0)).toBe("0")
expect(attribute.value(3)).toBe("3")
expect(attribute.value(0)).toBe(0)
expect(attribute.value(3)).toBe(3)
expect(attribute.isNumeric(0)).toBe(true)
expect(attribute.isNumeric(3)).toBe(true)
expect(attribute.numeric(0)).toBe(0)
expect(attribute.numeric(3)).toBe(3)
expect(attribute.numValue(0)).toBe(0)
expect(attribute.numValue(3)).toBe(3)

attribute.addValues(["-2", "-1"], 0)
expect(attribute.length).toBe(6)
expect(attribute.value(0)).toBe("-2")
expect(attribute.value(1)).toBe("-1")
expect(attribute.value(5)).toBe("3")
expect(attribute.numeric(0)).toBe(-2)
expect(attribute.numeric(1)).toBe(-1)
expect(attribute.numeric(5)).toBe(3)
expect(attribute.value(0)).toBe(-2)
expect(attribute.value(1)).toBe(-1)
expect(attribute.value(5)).toBe(3)
expect(attribute.numValue(0)).toBe(-2)
expect(attribute.numValue(1)).toBe(-1)
expect(attribute.numValue(5)).toBe(3)

attribute.setValue(2, 3)
expect(attribute.value(2)).toBe("3")
expect(attribute.numeric(2)).toBe(3)
expect(attribute.value(2)).toBe(3)
expect(attribute.numValue(2)).toBe(3)
attribute.setValue(10, 10)

attribute.setValues([0, 1], ["1", "2"])
expect(attribute.value(0)).toBe("1")
expect(attribute.value(1)).toBe("2")
expect(attribute.numeric(0)).toBe(1)
expect(attribute.numeric(1)).toBe(2)
expect(attribute.value(0)).toBe(1)
expect(attribute.value(1)).toBe(2)
expect(attribute.numValue(0)).toBe(1)
expect(attribute.numValue(1)).toBe(2)
attribute.setValues([10, 11], [10, 11])

attribute.setValues([0, 1], [0])
expect(attribute.value(0)).toBe("0")
expect(attribute.value(1)).toBe("2")
expect(attribute.numeric(0)).toBe(0)
expect(attribute.numeric(1)).toBe(2)
expect(attribute.value(0)).toBe(0)
expect(attribute.value(1)).toBe(2)
expect(attribute.numValue(0)).toBe(0)
expect(attribute.numValue(1)).toBe(2)
expect(attribute.getNumericCount()).toBe(6)
expect(attribute.type).toBe("numeric")

Expand All @@ -169,22 +169,22 @@ describe("Attribute", () => {

attribute.removeValues(2)
expect(attribute.length).toBe(5)
expect(attribute.value(2)).toBe("1")
expect(attribute.numeric(2)).toBe(1)
expect(attribute.value(2)).toBe(1)
expect(attribute.numValue(2)).toBe(1)

attribute.removeValues(0, 2)
expect(attribute.length).toBe(3)
expect(attribute.value(0)).toBe("1")
expect(attribute.numeric(0)).toBe(1)
expect(attribute.value(0)).toBe(1)
expect(attribute.numValue(0)).toBe(1)
attribute.removeValues(0, 0)
expect(attribute.length).toBe(3)
expect(attribute.value(0)).toBe("1")
expect(attribute.numeric(0)).toBe(1)
expect(attribute.value(0)).toBe(1)
expect(attribute.numValue(0)).toBe(1)

attribute.addValues(["a", "b"])
expect(attribute.value(3)).toBe("a")
expect(attribute.isNumeric(3)).toBe(false)
expect(attribute.numeric(3)).toBeNaN()
expect(attribute.numValue(3)).toBeNaN()
expect(attribute.type).toBe("categorical")

attribute.setUserType("numeric")
Expand All @@ -193,7 +193,7 @@ describe("Attribute", () => {
attribute.addValue()
expect(attribute.value(5)).toBe("")
expect(attribute.isNumeric(5)).toBe(false)
expect(attribute.numeric(5)).toBeNaN()
expect(attribute.numValue(5)).toBeNaN()

attribute.clearValues()
expect(attribute.strValues.length).toBe(6)
Expand Down
10 changes: 7 additions & 3 deletions v3/src/models/data/attribute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,17 +243,21 @@ export const Attribute = V2Model.named("Attribute").props({
return self.editable && !self.hasFormula
},
value(index: number) {
return self.strValues[index]
const numValue = self.numValues[index]
return !isNaN(numValue) ? numValue : self.strValues[index]
},
isNumeric(index: number) {
return !isNaN(self.numValues[index])
},
numeric(index: number) {
numValue(index: number) {
return self.numValues[index]
},
strValue(index: number) {
return self.strValues[index]
},
boolean(index: number) {
return ["true", "yes"].includes(self.strValues[index].toLowerCase()) ||
(!isNaN(this.numeric(index)) ? this.numeric(index) !== 0 : false)
(!isNaN(this.numValue(index)) ? this.numValue(index) !== 0 : false)
},
derive(name?: string) {
return { id: self.id, name: name || self.name, values: [] }
Expand Down
2 changes: 1 addition & 1 deletion v3/src/models/data/data-set-collections.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ describe("DataSet collections", () => {
const parentCase = { ...parentCases[0], aId: "4" }
data.setCaseValues([parentCase])
for (const caseId of data.collections[0].caseGroups[0].childItemIds) {
expect(data.getValue(caseId, "aId")).toBe("4")
expect(data.getValue(caseId, "aId")).toBe(4)
}
})

Expand Down
Loading

0 comments on commit efe1872

Please sign in to comment.