Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StoryQ 2: StoryQ in Codap v3 #9

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
958a374
Pass feature to addFeatureUnderConstruction.
tealefristoe Aug 26, 2024
aa8b21d
Stop including {} as children in headings.
tealefristoe Aug 26, 2024
4f6e118
Properly match cases to features.
tealefristoe Aug 27, 2024
536db6e
Fix text feedback without headers.
tealefristoe Aug 27, 2024
6e953e4
Unescape backslashes in patternMatches formulas.
tealefristoe Aug 27, 2024
e4ffda7
Always use all cases when updating ngram features.
tealefristoe Aug 28, 2024
8a9ad80
Bump version number.
tealefristoe Sep 11, 2024
ae519d5
Merge pull request #6 from concord-consortium/188150453-storyq-in-v3
tealefristoe Sep 16, 2024
8926f59
Merge branch 'master' into storyq-2
tealefristoe Sep 23, 2024
3ae5bd3
Merge branch 'master' into storyq-2
dougmartin Oct 3, 2024
b607da6
Convert to modern empty slate object.
tealefristoe Oct 5, 2024
8d39347
Merge pull request #13 from concord-consortium/188364196-text-crash
dougmartin Oct 7, 2024
8bce2dc
Merge branch 'master' into storyq-2
dougmartin Oct 7, 2024
9cd5b73
Merge branch 'master' into storyq-2
dougmartin Oct 7, 2024
87908ee
Merge branch 'master' into storyq-2
tealefristoe Oct 8, 2024
585ec6b
Merge branch 'master' into storyq-2
dougmartin Oct 9, 2024
fd9947f
Merge branch 'master' into storyq-2
dougmartin Oct 9, 2024
7c05a4c
Merge branch 'master' into storyq-2
tealefristoe Oct 9, 2024
57674fa
Merge branch 'master' into storyq-2
dougmartin Oct 15, 2024
d47ad56
build: Update display version to 2.16-v3-pre.1
dougmartin Oct 15, 2024
1984043
Merge pull request #23 from concord-consortium/update-to-v3-pre.1
dougmartin Oct 15, 2024
e4a8f3e
Merge master.
tealefristoe Oct 15, 2024
a18994a
Merge branch 'master' into storyq-2
tealefristoe Oct 16, 2024
de943dd
fix: Set model weight display precision to 2 decimal places [PT-18836…
dougmartin Oct 18, 2024
bd00d2e
Merge pull request #25 from concord-consortium/188364442-set-model-we…
dougmartin Oct 18, 2024
bc0fdff
build: Update display version to 2.16-v3-pre.3
dougmartin Oct 18, 2024
473cab4
Merge pull request #26 from concord-consortium/update-display-version…
dougmartin Oct 18, 2024
2b8bf4a
Merge branch 'master' into storyq-2
dougmartin Oct 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/feature_pane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const FeaturePane = observer(class FeaturePane extends Component<Feature_
else {
tFeatureUnderConstruction.name = this.props.domainStore.featureStore.constructNameFor(tFeatureUnderConstruction)
await this.props.domainStore.targetStore.addOrUpdateFeatureToTarget(tFeatureUnderConstruction)
await this.props.domainStore.featureStore.addFeatureUnderConstruction()
await this.props.domainStore.featureStore.addFeatureUnderConstruction(tFeatureUnderConstruction)
await this.props.domainStore.updateNonNtigramFeaturesDataset()
await this.props.domainStore.updateNgramFeatures()
tFeatureUnderConstruction.inProgress = false
Expand Down
2 changes: 1 addition & 1 deletion src/components/storyq.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const Storyq = observer(class Storyq extends Component<{}, {}> {
private domainStore: DomainStore
private notificationManager: NotificationManager
private kPluginName = kStoryQPluginName;
private kVersion = "2.16.0-v2";
private kVersion = "2.16.0-v3";
private kInitialDimensions = {
width: 429,
height: 420
Expand Down
8 changes: 5 additions & 3 deletions src/lib/codap-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,13 @@ export async function getAttributeNames(iDatasetName: string, iCollectionName: s
* @param iDatasetName
* @param iCollectionName
*/
export async function getCaseValues(iDatasetName: string,
iCollectionName: string): Promise<Case[]> {
export async function getCaseValues(
iDatasetName: string, iCollectionName: string, searchFormula?: string
): Promise<Case[]> {
const formula = searchFormula ?? `true`;
const tResult: any = await codapInterface.sendRequest({
action: 'get',
resource: `dataContext[${iDatasetName}].collection[${iCollectionName}].caseFormulaSearch[true]`
resource: `dataContext[${iDatasetName}].collection[${iCollectionName}].caseFormulaSearch[${formula}]`
}).catch(reason => console.log(`Unable to get cases in ${iDatasetName} because ${reason}`))
if (tResult.success) {
return tResult.values.map((iValue: any) => {
Expand Down
69 changes: 34 additions & 35 deletions src/managers/headings_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,40 +19,39 @@ export class HeadingsManager {
public setupHeadings(iNegLabel:string, iPosLabel:string, iBlankLabel:string,
iActual:string | null, iPredicted:string | null) {
function fillInHeading( iFirst:string | null, iSecond:string | null, iColor:string) {
let tFirstPhrase = !iFirst ? '{},{},' : `
{
"text": "${iActual} = ",
"color": "${iColor}",
"italic": true
},
{
"text": "${iFirst}",
"color": "${iColor}",
"italic": true,
"bold": true
},
{
"text": ", "
},
`;
let tSecondPhrase = !Boolean(iSecond) ? '{},{}' : `
{
"text": "${iPredicted} = ",
"color": "${iColor}",
"italic": true
},
{
"text": "${iSecond}",
"color": "${iColor}",
"italic": true,
"bold": true
}
`;
let tHeading = `{
"type": "paragraph",
"children": [${tFirstPhrase}${tSecondPhrase}]
}`;
return JSON.parse( tHeading);
const children = []
if (iFirst) {
children.push({
text: `${iActual} =`,
color: iColor,
italic: true
})
children.push({
text: iFirst,
color: iColor,
italic: true,
bold: true
})
children.push({ text: ", " })
}
if (iSecond) {
children.push({
text: `${iPredicted} = `,
color: iColor,
italic: true
})
children.push({
text: iSecond,
color: iColor,
italic: true,
bold: true
})
}
if (children.length === 0) children.push({ text: "" })
return {
type: "paragraph",
children
}
}
this.classLabels = {
negLabel: iNegLabel,
Expand All @@ -69,7 +68,7 @@ export class HeadingsManager {
posBlank: fillInHeading(iPosLabel, null, this.colors.green),
blankNeg: fillInHeading(null, iNegLabel, this.colors.orange),
blankPos: fillInHeading(null, iPosLabel, this.colors.blue),
blankBlank: {}
blankBlank: { text: "" }
}
}

Expand Down
16 changes: 8 additions & 8 deletions src/managers/text_feedback_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,10 @@ export default class TextFeedbackManager {
// Get the features and stash them in a set
tSelectedFeatureCases = await getSelectedCasesFrom(tFeatureDatasetName, tFeatureCollectionName)
tSelectedFeatureCases.forEach((iCase: any) => {
tFeaturesMap[Number(iCase.id)] = iCase.values.name;
// This used to use the caseId, but the featureIDs saved in the training cases are item ids.
// I'm using item ids here now, but it's possible they should both use case ids instead.
const itemId = iCase.children[0];
tFeaturesMap[Number(itemId)] = iCase.values.name;
});
}
}
Expand Down Expand Up @@ -276,7 +279,7 @@ export default class TextFeedbackManager {
tPredictedLabelAttributeName = this.domainStore.targetStore.targetPredictedLabelAttributeName,
tColumnFeatureNames = this.domainStore.featureStore.targetColumnFeatureNames,
tConstructedFeatureNames = this.domainStore.featureStore.features.map(iFeature => iFeature.name),
tFeaturesMap: {[index:number]:string} = {},
tFeaturesMap: Record<number, string> = {},
// Get all the selected cases in the target dataset. Some will be results and some will be texts
tSelectionListResult: any = await codapInterface.sendRequest({
action: 'get',
Expand Down Expand Up @@ -461,7 +464,6 @@ export default class TextFeedbackManager {
text: tGroup !== kProps[kProps.length - 1] ? '■ ' : '', // Don't add the square if we're in 'blankBlank'
color: tColor
}
// @ts-ignore
tClassItems[tGroup].push({
type: 'list-item',
children: [tSquare].concat(iHighlightFunc(iQuadruple.phrase, iQuadruple.nonNtigramFeatures, iSpecialFeatures))
Expand All @@ -474,16 +476,14 @@ export default class TextFeedbackManager {

// The phrases are all in their groups. Create the array of group objects
kProps.forEach(iProp => {
// @ts-ignore
let tPhrases = tClassItems[iProp];
const tPhrases = tClassItems[iProp];
if (tPhrases.length !== 0) {
let tHeadingItems = [
const tHeadingItems = [
// @ts-ignore
kHeadings[iProp],
{
type: 'bulleted-list',
// @ts-ignore
children: tClassItems[iProp]
children: tPhrases
}];
tItems = tItems.concat(tHeadingItems);
}
Expand Down
50 changes: 30 additions & 20 deletions src/stores/domain_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export class DomainStore {
parent: tFeatureCollectionName,
attrs: [
{name: 'model name', type: 'categorical', hidden: false},
{name: 'weight', hidden: false}
{name: 'weight', precision: 2, hidden: false}
]
}]
}
Expand All @@ -136,14 +136,19 @@ export class DomainStore {
}

async updateNonNtigramFeaturesDataset() {
interface UpdateRequest {
values: {
features: Feature[]
}
}
const this_ = this,
tFeatureStore = this.featureStore,
tNonNgramFeatures = tFeatureStore.features.filter(iFeature => iFeature.info.kind !== 'ngram'),
tTargetStore = this.targetStore,
caseUpdateRequests: { values: { features: Feature[] } }[] = [],
caseUpdateRequests: Record<string, UpdateRequest> = {},
tTargetDatasetName = tTargetStore.targetDatasetInfo.name,
tTargetCollectionName = tTargetStore.targetCollectionName
let resourceString: string = '',
let featureDatasetResourceString: string = '',
tFeatureItems: { values: { type: string }, id: string }[] = [],
tItemsToDelete: { values: object, id: string }[] = [],
tFeaturesToAdd: Feature[] = [],
Expand All @@ -152,7 +157,7 @@ export class DomainStore {
async function getExistingFeatureItems(): Promise<{ values: any, id: string }[]> {
const tItemsRequestResult: any = await codapInterface.sendRequest({
action: 'get',
resource: `${resourceString}.itemSearch[*]`
resource: `${featureDatasetResourceString}.itemSearch[*]`
})
if (tItemsRequestResult.success)
return tItemsRequestResult.values
Expand All @@ -170,42 +175,44 @@ export class DomainStore {
async function updateFrequenciesUsagesAndFeatureIDs() {
const tClassAttrName = this_.targetStore.targetClassAttributeName,
tChosenClassKey = this_.targetStore.targetChosenClassColumnKey,
tPosClassLabel = this_.targetStore.targetClassNames[tChosenClassKey],
tTargetCases = await this_.targetStore.updateTargetCases()
tPosClassLabel = this_.targetStore.targetClassNames[tChosenClassKey]

tNonNgramFeatures.forEach(iFeature => {
iFeature.numberInPositive = 0
iFeature.numberInNegative = 0
iFeature.usages = []
})
/**
* We go through each target case
* For each feature, if the case has the feature, we increment that feature's positive or negative count
* We go through each feature
* For each target case, if the case has the feature, we increment that feature's positive or negative count
* as appropriate
* If the feature is present,
* - we push the case's ID into the feature's usages
* - we add the feature's case ID to the array of feature IDs for that case
*/
tTargetCases.forEach((iCase) => {
tNonNgramFeatures.forEach((iFeature) => {
const countFeaturePromises = tNonNgramFeatures.map(async (iFeature) => {
const tTargetCases = await this_.targetStore.updateTargetCases(`\`${iFeature.name}\`=true`)
tTargetCases.forEach(iCase => {
if (iCase.values[iFeature.name]) {
if (iCase.values[tClassAttrName] === tPosClassLabel) {
iFeature.numberInPositive++
} else {
iFeature.numberInNegative++
}
iFeature.usages.push(iCase.id)
if (!caseUpdateRequests[iCase.id]) {
caseUpdateRequests[iCase.id] = {values: {features: []}}
const iCaseId = `${iCase.id}`
if (!caseUpdateRequests[iCaseId]) {
caseUpdateRequests[iCaseId] = { values: { features: [] } }
}
caseUpdateRequests[iCase.id].values.features.push(iFeature)
caseUpdateRequests[iCaseId].values.features.push(iFeature)
}
})
})
await Promise.all(countFeaturePromises)
}

if (await this.guaranteeFeaturesDataset()) {
resourceString = `dataContext[${tFeatureStore.featureDatasetInfo.datasetID}]`
featureDatasetResourceString = `dataContext[${tFeatureStore.featureDatasetInfo.datasetID}]`

tFeatureItems = (await getExistingFeatureItems()).filter(iItem => iItem.values.type !== 'unigram')
await updateFrequenciesUsagesAndFeatureIDs()
Expand All @@ -228,7 +235,7 @@ export class DomainStore {
tItemsToDelete.map(iItem => {
return {
action: 'delete',
resource: `${resourceString}.itemByID[${iItem.id}]`
resource: `${featureDatasetResourceString}.itemByID[${iItem.id}]`
}
})
)
Expand Down Expand Up @@ -258,7 +265,7 @@ export class DomainStore {
const tCreateResult: any = await codapInterface.sendRequest(
{
action: 'create',
resource: `${resourceString}.collection[${tFeatureStore.featureDatasetInfo.collectionName}].case`,
resource: `${featureDatasetResourceString}.collection[${tFeatureStore.featureDatasetInfo.collectionName}].case`,
values: tValues
}
)
Expand All @@ -267,7 +274,7 @@ export class DomainStore {
tFeaturesToAdd[iIndex].caseID = iValue.id
const tGetItemResult: any = await codapInterface.sendRequest({
action: 'get',
resource: `${resourceString}.itemByCaseID[${iValue.id}]`
resource: `${featureDatasetResourceString}.itemByCaseID[${iValue.id}]`
})
tFeaturesToAdd[iIndex].featureItemID = tGetItemResult.values.id
})
Expand All @@ -278,7 +285,7 @@ export class DomainStore {
tFeaturesToUpdate.map(iFeature => {
return {
action: 'update',
resource: `${resourceString}.itemByID[${iFeature.caseID}]`,
resource: `${featureDatasetResourceString}.itemByID[${iFeature.caseID}]`,
values: {
chosen: iFeature.chosen,
name: iFeature.name,
Expand All @@ -291,8 +298,10 @@ export class DomainStore {
}
// We've waited until now to update target cases with feature IDs so that we can be sure
// features do have caseIDs to stash in target cases
if (caseUpdateRequests.length > 0) {
const tValues = caseUpdateRequests.map((iRequest, iIndex) => {
const caseUpdateRequestKeys = Object.keys(caseUpdateRequests)
if (caseUpdateRequestKeys.length > 0) {
const tValues = caseUpdateRequestKeys.map(iIndex => {
const iRequest = caseUpdateRequests[iIndex]
return {
id: iIndex,
values: {featureIDs: JSON.stringify(iRequest.values.features.map(iFeature => iFeature.caseID))}
Expand All @@ -311,6 +320,7 @@ export class DomainStore {
if (this.featureStore.tokenMapAlreadyHasUnigrams())
return
const this_ = this
await this.targetStore.updateTargetCases()
const tNgramFeatures = this.featureStore.features.filter(iFeature => iFeature.info.kind === 'ngram')
await this.guaranteeFeaturesDataset()
for (const iNtgramFeature of tNgramFeatures) {
Expand Down
9 changes: 3 additions & 6 deletions src/stores/feature_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class FeatureStore {
return tDoneNgram || tDoneSearch || tDoneColumn
}

constructNameFor( iFeature: Feature) {
constructNameFor(iFeature: Feature) {
if (iFeature.info.kind === 'search') {
const tDetails = iFeature.info.details as SearchDetails,
tFirstPart = namingAbbreviations[tDetails.where],
Expand All @@ -77,11 +77,10 @@ export class FeatureStore {
return `${tFirstPart}: ${tSecondPart}`
} else if (iFeature.info.kind === 'ngram') {
return `single words with frequency ≥ ${iFeature.info.frequencyThreshold}${iFeature.info.ignoreStopWords ? '; ignoring stopwords': ''}`
} else if( iFeature.info.kind === 'column')
} else if (iFeature.info.kind === 'column')
return iFeature.name // already has column name stashed here
else
return ''

}

getDescriptionFor(iFeature: Feature) {
Expand Down Expand Up @@ -147,8 +146,7 @@ export class FeatureStore {
return Boolean(this.features.find(iFeature => iFeature.info.kind === 'ngram'))
}

addFeatureUnderConstruction() {
const tFeature = this.featureUnderConstruction
addFeatureUnderConstruction(tFeature: Feature) {
let tType
switch (tFeature.info.kind) {
case 'ngram':
Expand All @@ -160,7 +158,6 @@ export class FeatureStore {
default:
tType = 'constructed'
}
tFeature.name = this.constructNameFor(tFeature)
tFeature.inProgress = false
tFeature.chosen = true
tFeature.type = tType
Expand Down
6 changes: 4 additions & 2 deletions src/stores/target_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,12 @@ export class TargetStore {

}

async updateTargetCases() {
async updateTargetCases(formula?: string) {
const tTargetDatasetName = this.targetDatasetInfo.name,
tCollectionName = this.targetCollectionName,
tCaseValues = this.targetAttributeName !== '' ? await getCaseValues(tTargetDatasetName, tCollectionName) : []
tCaseValues = this.targetAttributeName !== ''
? await getCaseValues(tTargetDatasetName, tCollectionName, formula)
: []
runInAction(() => {
this.targetCases = tCaseValues
})
Expand Down
Loading