From b0ee5cd3042481a5489da051f2fbc01d2d1a8c3e Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 27 Sep 2021 17:26:52 +0200 Subject: [PATCH] Continued iterating on calculations, added failing test for bugs found --- .../buildComputation/removeSchemaFields.js | 7 +++-- .../computeComputation/computeCalculations.js | 3 ++ .../tests/computeAction.testFn.js | 13 ++++++++- .../writeAlteredProperties.js | 9 ++++-- app/imports/api/properties/Damages.js | 2 ++ .../subSchemas/inlineCalculationField.js | 1 + .../server/publications/singleCharacter.js | 4 +-- .../ui/properties/forms/AttributeForm.vue | 15 ++++------ .../properties/forms/shared/ComputedField.vue | 29 +++++++++++++++++++ 9 files changed, 67 insertions(+), 16 deletions(-) create mode 100644 app/imports/ui/properties/forms/shared/ComputedField.vue diff --git a/app/imports/api/engine/computation/buildComputation/removeSchemaFields.js b/app/imports/api/engine/computation/buildComputation/removeSchemaFields.js index 885d3abf..bf0a0af7 100644 --- a/app/imports/api/engine/computation/buildComputation/removeSchemaFields.js +++ b/app/imports/api/engine/computation/buildComputation/removeSchemaFields.js @@ -4,10 +4,13 @@ import { unset } from 'lodash'; export default function removeSchemaFields(schemas, prop){ schemas.forEach(schema => { schema._schemaKeys.forEach(key => { - // Skip object and array keys + // Skip object and array keys, except the errors array if ( schema.getQuickTypeForKey(key) === 'object' || - schema.getQuickTypeForKey(key) === 'objectArray' + ( + schema.getQuickTypeForKey(key) === 'objectArray' && + key.slice(-6)!== 'errors' + ) ) return; // Unset other computed only keys applyFnToKey(prop, key, unset) diff --git a/app/imports/api/engine/computation/computeComputation/computeCalculations.js b/app/imports/api/engine/computation/computeComputation/computeCalculations.js index b78bc5fc..2b06135c 100644 --- a/app/imports/api/engine/computation/computeComputation/computeCalculations.js +++ b/app/imports/api/engine/computation/computeComputation/computeCalculations.js @@ -29,6 +29,9 @@ function evaluateCalculation(calculation, scope){ } else { calculation.errors = context.errors } + // remove the working fields + delete calculation._parseLevel; + delete calculation._parsedCalculation; } function embedInlineCalculations(inlineCalcObj){ diff --git a/app/imports/api/engine/computation/computeComputation/tests/computeAction.testFn.js b/app/imports/api/engine/computation/computeComputation/tests/computeAction.testFn.js index 2fc425bc..6d6395ac 100644 --- a/app/imports/api/engine/computation/computeComputation/tests/computeAction.testFn.js +++ b/app/imports/api/engine/computation/computeComputation/tests/computeAction.testFn.js @@ -6,13 +6,16 @@ import clean from '../../utility/cleanProp.testFn.js'; export default function(){ const computation = buildComputationFromProps(testProperties); computeCreatureComputation(computation); - + const prop = computation.propsById['actionId']; assert.equal(prop.summary.value, 'test summary 3 without referencing anything 7'); assert.equal(prop.description.value, 'test description 12 with reference 0.25 prop'); assert.equal(prop.uses.value, 7); assert.equal(prop.usesLeft, 2); + const rolled = computation.propsById['rolledDescriptionId']; + assert.equal(rolled.summary.value, 'test roll gets compiled {1d4 + 4} properly'); + const itemConsumed = prop.resources.itemsConsumed[0]; assert.equal(itemConsumed.quantity.value, 3); assert.equal(itemConsumed.available, 27); @@ -60,6 +63,14 @@ var testProperties = [ }, usesUsed: 5, }), + clean({ + _id: 'rolledDescriptionId', + type: 'action', + ancestors: [{id: 'charId'}], + summary: { + text: 'test roll gets compiled {1d4 + (2 + 2)} properly', + }, + }), clean({ _id: 'numItemsConumedId', type: 'attribute', diff --git a/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js b/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js index 408d1a34..0d7bcbe6 100644 --- a/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js +++ b/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js @@ -1,12 +1,12 @@ import { Meteor } from 'meteor/meteor' -import { isEqual, forOwn } from 'lodash'; +import { isEqual } from 'lodash'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import propertySchemasIndex from '/imports/api/properties/computedOnlyPropertySchemasIndex.js'; export default function writeAlteredProperties(computation){ let bulkWriteOperations = []; // Loop through all properties on the memo - forOwn(computation.propsById, changed => { + computation.props.forEach(changed => { let schema = propertySchemasIndex[changed.type]; if (!schema){ console.warn('No schema for ' + changed.type); @@ -36,6 +36,9 @@ function addChangedKeysToOp(op, keys, original, changed) { // and compile an operation that sets all those keys for (let key of keys){ if (!isEqual(original[key], changed[key])){ + console.log('not equal: ', key); + console.log(original[key]) + console.log(changed[key]) if (!op) op = newOperation(original._id, changed.type); let value = changed[key]; if (value === undefined){ @@ -83,6 +86,7 @@ function addUnsetOp(op, key){ // compensation without needing to roll back changes, which causes multiple // expensive redraws of the character sheet function writePropertiesSequentially(bulkWriteOps){ + console.log({opsLength: bulkWriteOps.length}); bulkWriteOps.forEach(op => { let updateOneOrMany = op.updateOne || op.updateMany; CreatureProperties.update(updateOneOrMany.filter, updateOneOrMany.update, { @@ -91,6 +95,7 @@ function writePropertiesSequentially(bulkWriteOps){ bypassCollection2: true, }); }); + console.log('finished writing ops'); } // This is more efficient on the database, but significantly less efficient diff --git a/app/imports/api/properties/Damages.js b/app/imports/api/properties/Damages.js index 8b4881f9..bc790b13 100644 --- a/app/imports/api/properties/Damages.js +++ b/app/imports/api/properties/Damages.js @@ -9,6 +9,7 @@ const DamageSchema = createPropertySchema({ type: 'fieldToCompute', optional: true, defaultValue: '1d8 + strength.modifier', + parseLevel: 'compile', }, // Who this damage applies to target: { @@ -31,6 +32,7 @@ const ComputedOnlyDamageSchema = createPropertySchema({ amount: { type: 'computedOnlyField', optional: true, + parseLevel: 'compile', }, }); diff --git a/app/imports/api/properties/subSchemas/inlineCalculationField.js b/app/imports/api/properties/subSchemas/inlineCalculationField.js index f9f67242..50e31224 100644 --- a/app/imports/api/properties/subSchemas/inlineCalculationField.js +++ b/app/imports/api/properties/subSchemas/inlineCalculationField.js @@ -38,6 +38,7 @@ function computedOnlyInlineCalculationField(field){ }, [`${field}.inlineCalculations.$`]: { type: Object, + parseLevel: 'compile', }, // The part between bracers {} [`${field}.inlineCalculations.$.calculation`]: { diff --git a/app/imports/server/publications/singleCharacter.js b/app/imports/server/publications/singleCharacter.js index ce1257fb..69fff4f7 100644 --- a/app/imports/server/publications/singleCharacter.js +++ b/app/imports/server/publications/singleCharacter.js @@ -15,7 +15,7 @@ let schema = new SimpleSchema({ Meteor.publish('singleCharacter', function(creatureId){ schema.validate({ creatureId }); - this.autorun(function (){ + this.autorun(function (computation){ let userId = this.userId; let creatureCursor creatureCursor = Creatures.find({ @@ -24,7 +24,7 @@ Meteor.publish('singleCharacter', function(creatureId){ let creature = creatureCursor.fetch()[0]; try { assertViewPermission(creature, userId) } catch(e){ return [] } - if (creature.computeVersion !== VERSION){ + if (creature.computeVersion !== VERSION && computation.firstRun){ try { computeCreature(creatureId) } diff --git a/app/imports/ui/properties/forms/AttributeForm.vue b/app/imports/ui/properties/forms/AttributeForm.vue index 23e48e88..0fdcfafe 100644 --- a/app/imports/ui/properties/forms/AttributeForm.vue +++ b/app/imports/ui/properties/forms/AttributeForm.vue @@ -1,18 +1,17 @@