Skip to content

Commit

Permalink
Also use new architecture to draw paths from aliases.
Browse files Browse the repository at this point in the history
components/draw/block-adj.js :
Factor to progressGroupsSelect() so that flowName can be a parameter.
Split pathsAliasesResultLength out of pathsResultLength.
Change selections which included g.direct to use variable flowName instead.
Make flowName the datum of g.progress > g.{direct,alias}
Add pathsResultTypes[], pathsApiFields[], pathsApiResultType, to wrap the variations between direct and alias data result (including featurePathKeyFn, featureEltId, blocksFeatures).
Add pathsResultType param to pathsOfFeature().
Add resultBlockIds().

models/block-adj.js : Change result of paths() to a value containing both direct and alias promises (which can be undefined).
paths-progressive.js: appendResult() : if result is empty and not streaming, don't update the blockAdj value.
frontend/app/ :
 14fd2d9  20945 Jul  1 15:44  components/draw/block-adj.js
 791f466   6374 Jun 25 11:22  models/block-adj.js
 6292184  21208 Jun 25 17:39  services/data/paths-progressive.js
  • Loading branch information
Don-Isdale committed Jul 1, 2019
1 parent 6019f2d commit 814c932
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 54 deletions.
209 changes: 166 additions & 43 deletions frontend/app/components/draw/block-adj.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { selectAxis, blockAdjKeyFn, blockAdjEltId, featureEltIdPrefix, featureNa

/* global d3 */



/*----------------------------------------------------------------------------*/

/** Used for CSS selectors targeting <g> and <path>-s generated by this component. */
Expand All @@ -28,6 +30,21 @@ const trace_blockAdj = 1;
*/
const pathTransitionTime = 750;

/** Used for d3 attribute functions - return the datum of the element. */
function datumIdent(d) { return d; }

/** select the g.direct and g.alias within g.foreground > g.progress.
* @see flowNames[]
* @param flowName undefined, or an element of flowNames[]
*/
function progressGroupsSelect(flowName) {
/** if flowName is undefined, then select g.direct and g.alias. refn flowNames[] */
let classSelector = flowName ? '.' + flowName : '',
selector = foregroundSelector + '> g.progress > g' + classSelector,
g = d3.selectAll(selector);
return g;
}

/*----------------------------------------------------------------------------*/

/**
Expand Down Expand Up @@ -55,30 +72,64 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, {
*/
axes : Ember.computed.alias('blockAdj.axes'),

pathsResultLength : Ember.computed('blockAdj.pathsResult.[]', 'paths', function () {
pathsResultLength : Ember.computed('blockAdj.pathsResult.[]', 'paths', 'pathsAliasesResultLength', function () {
/** Trigger paths request - side-effect. In the streaming case, result when
* the stream ends is [], so paths{,Aliases}Result are used instead of the
* result of this promise.
*/
let pathsP = this.get('paths'),
/** this can now be factored more, using .fieldName */
pathsResult = this.get('blockAdj.pathsResult'),
pathsAliasesResult = this.get('blockAdj.pathsAliasesResult'),
length = pathsResult && pathsResult.length,
pathsAliasesLength = pathsAliasesResult && pathsAliasesResult.length;
pathsAliasesLength = this.get('pathsAliasesResultLength');
console.log('pathsResultLength', this, length, pathsAliasesLength);
if (length)
this.draw(pathsResult);
/* pathsAliasesResult is in a different form to pathsResult; passing it to
* draw() will require some mapping */
if (false && pathsAliasesLength)
this.draw(pathsAliasesResult);
this.draw(/*pathsApiResultType*/ pathsResultTypes.direct, pathsResult);
return length;
}),
pathsAliasesResultLength : Ember.computed('blockAdj.pathsAliasesResult.[]', 'paths', function () {
let pathsP = this.get('paths'),
pathsAliasesResult = this.get('blockAdj.pathsAliasesResult'),
pathsAliasesLength = pathsAliasesResult && pathsAliasesResult.length;
console.log('pathsAliasesResultLength', this, pathsAliasesLength);
/* pathsAliasesResult is in a different form to pathsResult; passing it to
* draw() requires some mapping, which is abstracted in
* pathsResultType e.g. pathsResultTypes.{direct,alias}
*/
pathsApiResultType.flowName = pathsResultTypes.alias.flowName;
if (pathsAliasesLength)
this.draw(pathsApiResultType /*pathsResultTypes.alias*/, pathsAliasesResult);
return pathsAliasesLength;
}),
paths : Ember.computed('blockAdj', 'zoomCounter', function () {
/** in the case of pathsViaStream, this promise will not resolve -
/** in the case of pathsViaStream, this promise will resolve with [] instead of the result -
* blockAdj.pathsResult is passed to draw() instead. */
let pathsP = this.get('blockAdj.paths');
pathsP.then((paths) => {
console.log('blockAdj.paths length', paths && paths.length);
if (paths && paths.length)
throttle(this, this.draw, paths, 200, false);
console.log('blockAdj.paths', pathsP);
pathsP.then(result => {
console.log('blockAdj.paths', result);
if (false)
flowNames.forEach(flowName => {
if (result[flowName])
result[flowName].then((paths) => {
/** pathsApiResultType could be identified as
* pathsResultTypes.pathsApi; it is an input format which may be used
* in multiple flows, so possibly .flowName should be separate from
* pathsResultTypes[].
*/
let pathsResultType = paths.length && paths[0].featureAObj ?
pathsApiResultType /*pathsResultTypes.pathsApi*/ : pathsResultTypes[flowName];
console.log('blockAdj.paths length', paths && paths.length, pathsResultType);
if (paths && paths.length)
throttle(this, this.draw, pathsResultType, paths, 200, false);
});
});
});
/** .direct and.alias are defined by the result of pathsP, not by pathsP, so
* this would need to change; no purpose for this yet. */
let resultP = (pathsP.direct && pathsP.alias) ?
Ember.RSVP.allSettled([pathsP.direct, pathsP.alias])
: (pathsP.direct || pathsP.alias);
return pathsP;
}),

Expand All @@ -103,7 +154,8 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, {
console.log('willDestroyElement', this.get('blockAdjId'));
let foreground = d3.selectAll(foregroundSelector);
let pS = foreground
.selectAll('g > g.progress > g.direct');
// delete both g.direct and g.alias
.selectAll('g > g.progress > g');
this.drawGroup(pS, false);

this._super(...arguments);
Expand All @@ -124,12 +176,12 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, {
ppM = ppS.merge(ppA),

pS = ppM
.selectAll('g > g.direct')
.data([1]),
.selectAll('g > g.direct, g > g.alias') // @see flowNames[]
.data(flowNames),
pA = pS
.enter()
.append('g')
.attr('class', 'direct'),
.attr('class', datumIdent),
pM = pS.merge(pA);
console.log('drawGroupContainer', pS.nodes(), pS.node());
return pM;
Expand Down Expand Up @@ -167,32 +219,29 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, {


/**
* @param pathsResultType e.g. pathsResultTypes.{Direct,Aliases}
* @param paths grouped by features
*/
draw (featurePaths) {
/* If result of promises for both direct and aliases, then use just [0]
* which is the direct result, because draw() does not yet handle paths aliases format.
*/
if (featurePaths.length && featurePaths[0].state) {
let direct = featurePaths[0];
featurePaths = direct.value;
}
draw (pathsResultType, featurePaths) {
if (featurePaths.length === 0)
return;
pathsResultType.typeCheck(featurePaths[0]);

/** blockAdjId is also contained in the result featurePaths
*/
let
blockAdjId = this.get('blockAdjId');

if (featurePaths[0].alignment.length) {
/** Looking at just the first result, check the results blockIds match the
* request blockAdjId. */
let blockIds = resultBlockIds(pathsResultType, featurePaths[0]);
if (blockIds.length) {
/** blockAdjId is the order of Stacked / axes, whereas
* featurePaths[0].alignment is in request order. */
const reversed = blockAdjId[0] > blockAdjId[1];
let a = featurePaths[0].alignment,
ok = (a[0].blockId === blockAdjId[0+reversed]) && (a[1].blockId === blockAdjId[1-reversed]);
const reversed = blockAdjId[0] > blockAdjId[1],
ok = (blockIds[0] === blockAdjId[0+reversed]) && (blockIds[1] === blockAdjId[1-reversed]);
if (! ok)
console.log('draw verify', blockAdjId, a);
console.log('draw verify', blockAdjId, reversed, blockIds);
}

// let axisApi = this.get('drawMap.oa.axisApi');
Expand All @@ -204,7 +253,7 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, {
});
}

let dpS = d3.selectAll(foregroundSelector + '> g.progress > g.direct');
let dpS = progressGroupsSelect(pathsResultType.flowName);

let baS = selectBlockAdj(dpS, blockAdjId);
console.log(baS.nodes(), baS.node());
Expand All @@ -214,7 +263,7 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, {
else
{
let gS = baS.selectAll("g." + className)
.data(featurePaths, featurePathKeyFn);
.data(featurePaths, pathsResultType.featurePathKeyFn);
gS.exit().remove();

let gA = gS.enter()
Expand All @@ -223,18 +272,16 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, {
.attr('class', className)
;
function featureGroupIdFn(featurePath) {
let a = featurePath.alignment,
id = [a[0].blockId, a[1].blockId];
return blockAdjEltId(id) + '_' + featureEltId(featurePath);
let id = resultBlockIds(pathsResultType, featurePath);
return blockAdjEltId(id) + '_' + pathsResultType.featureEltId(featurePath);
}


console.log('PathData', PathData);
let gSA = gS.merge(gA),
owner = Ember.getOwner(this),
pS = gSA
.selectAll("path." + className)
.data(pathsOfFeature(owner), locationPairKeyFn),
.data(pathsOfFeature(pathsResultType, owner), locationPairKeyFn),
pSE = pS.enter()
.append("path")
.attr("class", className)
Expand All @@ -251,7 +298,7 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, {
/** Update the "d" attribute of the <path>-s. */
updatePathsPosition() {
// based on draw().
let dpS = d3.selectAll(foregroundSelector + '> g.progress > g.direct');
let dpS = progressGroupsSelect(undefined);
let blockAdjId = this.get('blockAdjId');
if (trace_blockAdj > 1)
blockAdjId.forEach(function (blockId) {
Expand Down Expand Up @@ -383,26 +430,29 @@ function featurePathKeyFn (featureBlock)
* for each pair an element of pairs[] :
* pair.feature0 is in block pair.block0
* pair.feature1 is in block pair.block1
* (for the case of pathsResultTypes.direct) :
* pair.block0 === feature.alignment[0].blockId
* pair.block1 === feature.alignment[1].blockId
* i.e. the path goes from the first block in the request params to the 2nd block
* @param pathsResultType e.g. pathsResultTypes.{Direct,Aliases}
* @param feature 1 element of the result array passed to draw()
* @return [PathData, ...]
*/
function pathsOfFeature(owner) {
function pathsOfFeature(pathsResultType, owner) {
const PathData = owner.factoryFor('component:draw/path-data');
return function (feature) {
let blocksFeatures =
[0, 1].map(function (blockIndex) { return feature.alignment[blockIndex].repeats.features; });
let pairs =
[0, 1].map(function (blockIndex) { return pathsResultType.blocksFeatures(feature, blockIndex); }),
blocks = resultBlockIds(pathsResultType, feature),
pairs =
blocksFeatures[0].reduce(function (result, f0) {
let result1 = blocksFeatures[1].reduce(function (result, f1) {
let pair =
PathData.create({
feature0 : f0,
feature1 : f1,
block0 : feature.alignment[0].blockId,
block1 : feature.alignment[1].blockId
block0 : blocks[0],
block1 : blocks[1]
});
if (trace_blockAdj > 2)
console.log('PathData.create()', PathData, pair);
Expand All @@ -422,3 +472,76 @@ function locationPairKeyFn(locationPair)
}

/*----------------------------------------------------------------------------*/

const pathsApiFields = ['featureAObj', 'featureBObj'];
/** This type is created by paths-progressive.js : requestAliases() : receivedData() */
const pathsApiResultType = {
// fieldName may be pathsResult or pathsAliasesResult
typeCheck : function(resultElt) { if (! resultElt.featureAObj) {
console.log('pathsApiResultType : typeCheck', resultElt); } },
pathBlock : function (resultElt, blockIndex) { return resultElt[pathsApiFields[blockIndex]].blockId; },
/** direct.blocksFeatures() returns an array of features, so match that. See
* similar commment in alias.blocksFeatures. */
blocksFeatures : function (resultElt, blockIndex) { return [ resultElt[pathsApiFields[blockIndex]] ]; },
featureEltId :
function (resultElt)
{
let id = pathsApiResultType.featurePathKeyFn(resultElt);
id = featureNameClass(id);
return id;
},
featurePathKeyFn : function (resultElt) { return resultElt.featureA + '_' + resultElt.featureB; }

};

/** This is provision for using the API result type as <path> data type; not used currently because
* the various forms of result data are converted to path-data.
* These are the result types from :
* Block/paths -> apiLookupAliases() -> task.paths()
* Blocks/pathsViaStream -> pathsAggr.pathsDirect()
* getPathsAliasesViaStream() / getPathsAliasesProgressive() -> Blocks/pathsAliasesProgressive -> dbLookupAliases() -> pathsAggr.pathsAliases()
*/
const pathsResultTypes = {
direct : {
fieldName : 'pathsResult',
typeCheck : function(resultElt) { if (! resultElt._id) {
console.log('direct : typeCheck', resultElt); } },
pathBlock : function (resultElt, blockIndex) { return resultElt.alignment[blockIndex].blockId; },
blocksFeatures : function (resultElt, blockIndex) { return resultElt.alignment[blockIndex].repeats.features; },
featureEltId : featureEltId,
featurePathKeyFn : featurePathKeyFn
},

alias :
{
fieldName : 'pathsAliasesResult',
typeCheck : function(resultElt) { if (! resultElt.aliased_features) {
console.log('alias : typeCheck', resultElt); } },
pathBlock : function (resultElt, blockIndex) { return resultElt.aliased_features[blockIndex].blockId; },
/** There is currently only 1 element in .aliased_features[blockIndex], but
* pathsOfFeature() handles an array an produces a cross-product, so return
* this 1 element as an array. */
blocksFeatures : function (resultElt, blockIndex) { return [resultElt.aliased_features[blockIndex]]; },
featureEltId :
function (resultElt)
{
let id = pathsResultTypes.alias.featurePathKeyFn(resultElt);
id = featureNameClass(id);
return id;
},
featurePathKeyFn : function (resultElt) {
return resultElt.aliased_features.map(function (f) { return f.name; } ).join('_');
}
}
},
/** This matches the index values of services/data/flows-collate.js : flows */
flowNames = Object.keys(pathsResultTypes);
// add .flowName to each of pathsResultTypes, which could later require non-const declaration.
flowNames.forEach(function (flowName) { pathsResultTypes[flowName].flowName = flowName; } );


function resultBlockIds(pathsResultType, featurePath) {
let blockIds =
[0, 1].map(function (blockIndex) { return pathsResultType.pathBlock(featurePath, blockIndex); });
return blockIds;
}
14 changes: 6 additions & 8 deletions frontend/app/models/block-adj.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export default DS.Model.extend({
/**
* Depending on zoomCounter is just a stand-in for depending on the domain of each block,
* which is part of changing the axes (Stacked) to Ember components, and the dependent keys can be e.g. block0.axis.domain.
* @return promises of paths array from direct and/or aliases, in a hash {direct : promise, alias: promise}
*/
paths : Ember.computed('blockId0', 'blockId1', 'zoomCounter', function () {
let blockAdjId = this.get('blockAdjId'),
Expand All @@ -113,7 +114,7 @@ export default DS.Model.extend({
// expected .drop() to handle this, but get "TaskInstance 'taskGetPaths' was canceled because it belongs to a 'drop' Task that was already running. "
if (! task.get('isIdle')) {
console.log('paths taskGetPaths', task.numRunning, task.numQueued, blockAdjId);
result = Ember.RSVP.resolve([]);
result = { direct: Ember.RSVP.resolve([]) };
}
else
result = task.perform(blockAdjId);
Expand All @@ -123,11 +124,11 @@ export default DS.Model.extend({
* getPathsAliasesProgressive().
* Those functions may make a request to the backend server if a current result is not in hand,
* so this function is wrapped by taskGetPaths().
* @return promise of paths by either direct or alias connections.
* @return promise of paths by direct and/or alias connections.
*/
getPaths : function (blockAdjId) {
let
result,
result = {},
id = this.get('id');
let flowsService = this.get('flowsService'),
flows = flowsService.get('flows');
Expand All @@ -145,7 +146,7 @@ export default DS.Model.extend({
console.log('block-adj paths reject', err);
}
);
result = paths;
result.direct = paths;
}

if (flows.alias.visible) {
Expand All @@ -159,10 +160,7 @@ export default DS.Model.extend({
console.log('block-adj pathsResult reject', err);
}
);
if (result === undefined)
result = pathsAliases;
else
result = Ember.RSVP.allSettled([result, pathsAliases]);
result.alias = pathsAliases;
}

return result;
Expand Down
Loading

0 comments on commit 814c932

Please sign in to comment.