I'm getting in trouble to migrate my website from PHP to Node.js.
I'm actually using SQLite3 along with the Spatialite extension to show some layers on an OpenLayers map. Users choose which layers they want to show using checkboxs in a menu [ thematics => rubrics => layers ], something like :
MY THEMATIC
MY RUBRIC
ONE LAYER
ANOTHER LAYER
STILL ANOTHER LAYER
ANOTHER RUBRIC ...
ANOTHER THEMATIC ...
A JSON file describes the content of this menu :
{
"MY THEMATIC": {
"MY RUBRIC": [
"ONE LAYER", "ANOTHER LAYER", "STILL ANOTHER LAYER"
],
"ANOTHER RUBRIC": ...
},
"ANOTHER THEMATIC"
}
Each layer is linked to another JSON file describing the layer ( layer and legend color, which table in the database, stroke width, etc... )
When generating the menu, I have to check if layers are valid ( database exists, table exists, query must return at least one row ! ).
If a layer is not valid, it isn't shown in the menu.
If all layers in a rubric are not valid, the rubric doesn't appear in the menu.
If all rubrics are not showing any layer, the thematic doesn't appear too.
My problem is that I'm actually using SQLite3 in a blocking way in PHP but database queries are asynchronous in Node.js. So I've tried to do this with callbacks, working, but code is really ugly and difficult to read/understand. I looked at promises but I didn't get it to work. Here is the algorithmic PHP version of my source code generating the menu :
$out = Array(); // Output array to fill in
$thematics = json_decode("..."); // My JSON file read and stored in a PHP array
foreach ($thematics as $thematicName => $rubrics) {
$out[$thematicName] = Array();
$flagThematic = true; // Flag controlling if the thematic contains at least one rubric
foreach ($rubrics as $rubricName => $layers) {
if ($rubricName == "rubric name to exclude") {
continue;
}
$out[$thematicName][$rubricName] = Array();
$flagRubric = true; // Flag controlling if the rubric contains at least one valid layer
foreach ($layers as $layer) {
// isValid
// This function query the database to check the layer validity
// THIS is the asynchronous one in node.js
if (!isValid($layer)) {
continue;
}
$out[$thematicName][$rubricName][] = ["..."]; // add layer info such as legend, name ...
$flagRubric = false;
}
if ($flagRubric === true) {
// No layer in this rubric, deleting the rubric
unset($out[$thematicName][$rubricName]);
} else {
$flagThematic = false;
}
}
if ($flagThematic === true) {
// No rubric in this thematic, deleting the thematic
unset($out[$thematicName]);
}
}
return $out;
Do you have any advice to help me to turn this code into Node.js ?
Thanks in advance.
EDIT
I tried something like that, using bluebird (https://github.com/petkaantonov/bluebird) :
function generateMenu(callback) {
var out = {};
Promise.map(Object.keys(thematics), function (thematicName) {
out[thematicName] = {};
return {
rubrics: thematics[thematicName],
thematicName: thematicName
};
}).map(function (res) {
Promise.map(Object.keys(res.rubrics), function (rubricName) {
if (rubricName === "rubric name to exclude") {
return null; // don't really know how to do here, I think I'm doing it wrong
}
out[res.thematicName][rubricName] = {};
return {
layers: them[res.thematicName][rubricName],
thematicName: thematicName,
rubricName: rubricName
};
}).map(function (res) {
// Doing more stuff with layers then call the main callback
callback(out);
});
});
But I think I'm totally going wrong, I'm thinking about flags and cie as if I was using PHP. I guess I didn't get the "node logic" programming.
Related
Is there any way to calculate the extent of a KML layer loaded from the web using the KMLLayer({ url: "my file" }) method in ArcGIS Online? The KMLs loaded from AGOL have a valid fullExtent property, but ones loaded from other sources seem to default to the entire world, which is not useful.
Here is an example:
app.kml=new KMLLayer({ url: "my file" });
app.map.add(app.kml);
app.kml.load().then(function() { app.mapView.extent=app.kml.fullExtent; console.log(app.kml) });
It is live at:
http://viseyes.org/visualeyes/test.htm?kml=https://www.arcgis.com/sharing/rest/content/items/a8efe6f4c12b462ebedc550de8c73e22/data
The console prints out the KMLLayer object, and the fullExtent field seems to be not set right.
I agree, it does not seem like the fullExtent property is what you would expect. I think there are two workarounds:
Write some code to query the layerView to get the extent:
view.whenLayerView(kmlLayer).then(function(layerView) {
watchUtils.whenFalseOnce(layerView, "updating", function() {
var kmlFullExtent = queryExtent(layerView);
view.goTo(kmlFullExtent);
});
});
function queryExtent(layerView) {
var polygons = layerView.allVisiblePolygons;
var lines = layerView.allVisiblePolylines;
var points = layerView.allVisiblePoints;
var images = layerView.allVisibleMapImages;
var kmlFullExtent = polygons
.concat(lines)
.concat(points)
.concat(images)
.map(
graphic => (graphic.extent ? graphic.extent : graphic.geometry.extent)
)
.reduce((previous, current) => previous.union(current));
return kmlFullExtent;
}
Example here.
-- or --
Call the utility service again and use the "lookAtExtent" property:
view.whenLayerView(kmlLayer).then(function(layerView) {
watchUtils.whenFalseOnce(layerView, "updating", function() {
// Query the arcgis utility and use the "lookAtExtent" property -
esriRequest('https://utility.arcgis.com/sharing/kml?url=' + kmlLayer.url).then((response) => {
console.log('response', response.data.lookAtExtent);
view.goTo(new Extent(response.data.lookAtExtent));
});
});
});
Example here.
I have a tricky requirement which im not able to fix.
I have a button and if i click on that button, Im getting below data from a REST web service ..
{
"results": [{
"Reqdate": "\/Date(1520899200000)\/",
"Tooltip": "13.03.2018 Blood Donation Approved ",
"Legendid": "GROUP_LEVEL1"
},{
"Reqdate": "\/Date(1523836800000)\/",
"Tooltip": "16.04.2018 Privilege Leave Sent ",
"Legendid": "BADVALUE_LIGHT",
},{
"Reqdate": "\/Date(1524528000000)\/",
"Tooltip": "24.04.2018 Privilege Leave Sent ",
"Legendid": "BADVALUE_LIGHT",
}]
}
If im getting the above data for the first time. Im going to display one section. Now again if i click on the same button, If i get the same data in the service then i have to hide the section. But im not able to check the same data for the second time.
Im trying to fix this issue by storing the data in a variable like below..
var firstTimeResults = data.results;
Now im not able to check if the same data is coming for the second time. Actually im not able to produce the sample code too. Im sorry for that
Can someone please help me to fix this issue.
I am not fully understand your problem but I think you can store your data as JSON and check if the same data is return from the second time onwards?
Code example
// Variable declaration
var firstTimeResults = null;
// When data return
var dataJson = JSON.stringify(data.results);
if (firstTimeResults === null) {
firstTimeResults = dataJson;
}
else {
// check to see if the data is the same as the firstTimeResults
var isSame = firstTimeResults === dataJson;
if (isSame) {
// Same
} else {
// Not the same
}
}
You can simply try with Lodash library.
It makes JavaScript easier to work with arrays, numbers, objects, strings, etc.
https://lodash.com/
var firstResult = null;
var secondResult;
var showSection;
function getData() {
// here goes your API request to get the data
};
function onButtonClick() {
this.getData().then(function(value) {
if !(firstResult) {
firstResult = value;
showSection = true;
return;
}
secondResult = value;
if (_.isEqual(firstResult, secondResult)) {
showSection = false;
}
})
};
Hope this will be helpful to you!
I'm looking for a way to use Trello data in a powerup I am developing. I can get the powerup working as long as I have everything I need during TrelloPowerUp.initialize(), but the data I need is only accessible through Promises. This includes card, board, and powerup data stored through the API. The data Trello gives me access to includes the board, card, plugin IDs, and the command. Since the powerup is expected to return an array, I can't figure out a way to access any other kind of data.
Here is a simple concrete example. In it I would like to check the name of a card and display a badge accordingly.
In the example,
t grants access to the client library
card grants access to the following data:
{
"context":{
"board":"55db14fd3e104ac8b105bd75",
"card":"563b532e4e998440d0d88e62",
"command":"card-badges",
"plugin":"564ddf493f184b88ea5ddc0e"
},
"locale":"en-US"
}
Example
Here is the code to initialize card-badges. Notice that the function should return an array.
TrelloPowerUp.initialize({
'card-badges': function(t, card) {
var badge_text,
badge_color;
// returns a promise with the card name
t.card('name').then(function (name) {
if (name == "foo") {
badge_text = "contingent text";
badge_color = "contingent color";
}
});
return [{
text: badge_text,
icon: './images/icon.png',
color: badge_color
}]
}
});
Obviously this code doesn't work. The initialize function seems to be set up without regard to promises. Even the dynamic option, which takes a function as a parameter, is expected to return an array.
None of this makes sense to me, since storing and retrieving powerup data in cards is also done via promises (t.set(), t.get()). Since I don't even seem to be able to access powerup data, I feel like I am missing something in my assessment.
Is there a way to get access to the data available in promises while initializing powerups?
The trick is to return a promise to trello. The promise should return the data Trello is looking for. E.g.
TrelloPowerUp.initialize({
'card-badges': function(t, card) {
// returns a promise with the card name
var promise = t.card('name').then(function (name) {
var badge_text,
badge_color;
if (name == "foo") {
badge_text = "contingent text";
badge_color = "contingent color";
}
return [{
text: badge_text,
icon: './images/icon.png',
color: badge_color
}]
});
return promise;
}
});
Imagine I have two models:
var Movie = sequelize.define('movies', {
/* model definition */
})
var Genre = sequelize.define('genres', {
/* model definition */
});
Movie.hasMany(Genre);
Genre.hasMany(Movie);
If I wanted to stipulate that a Movie MUST have at least one Genre, how would I go about doing that?
I've looked in the obvious places. My initial idea was to build(), validate() and save(), however looking at the source .validate() only accommodates fields defined in the model definition.
e.g.
Genre.find({where:{'name':'horror})
.success(function (horrorGenre) {
var movie = Movie.build({..});
movie.addGenre(horrorGenre);
if (! movie.validate()) { // This doesn't consider related data
movie.save();
}
});
So I figure I need to implement some kind of custom validation mechanism, but I'm not entirely sure where to start.
NOTE I'm maintaining my own fork of Sequelize, so this is more of a question of how I might go about modifying the Sequelize source to do what I want it to do versus throwing together a hacky solid implementation.
you can try to search for genre objects in database and call addGenre for movie
Genre.findall({where:{'name':["genre1","genre1"]})
.success(function (genres) {
if(genres.length==0){
console.log("Genres were not found!");
// exit somehow maybe res.json(200,{"msg","not ok"});
}
var movie = Movie.build({..});
var queryChainer = new Sequelize.Utils.QueryChainer;
for(var i = 0 ; i != genres.length ; i++){
queryChainer.add(movie.addGenre(genres[i].id));
}
queryChainer.run().success(function(){}).error(function(){});
});
this way you will know that at least 1 genre will be added to submitted movie!
I have a Single Page Application that is working pretty well so far but I have run into an issue I am unable to figure out. I am using breeze to populate a list of projects to be displayed in a table. There is way more info than what I actually need so I am doing a projection on the data. I want to add a knockout computed onto the entity. So to accomplish this I registered and entity constructor like so...
metadataStore.registerEntityTypeCtor(entityNames.project, function () { this.isPartial = false; }, initializeProject);
The initializeProject function uses some of the values in the project to determine what the values should be for the computed. For example if the Project.Type == "P" then the rowClass should = "Red".
The problem I am having is that all the properties of Project are null except for the ProjNum which happens to be the key. I believe the issue is because I am doing the projection because I have registered other initializers for other types and they work just fine. Is there a way to make this work?
EDIT: I thought I would just add a little more detail for clarification. The values of all the properties are set to knockout observables, when I interrogate the properties using the javascript debugger in Chrome the _latestValue of any of the properties is null. The only property that is set is the ProjNum which is also the entity key.
EDIT2: Here is the client side code that does the projection
var getProjectPartials = function (projectObservable, username, forceRemote) {
var p1 = new breeze.Predicate("ProjManager", "==", username);
var p2 = new breeze.Predicate("ApprovalStatus", "!=", "X");
var p3 = new breeze.Predicate("ApprovalStatus", "!=", "C");
var select = 'ProjNum,Title,Type,ApprovalStatus,CurrentStep,StartDate,ProjTargetDate,CurTargDate';
var isQaUser = cookies.getCookie("IsQaUser");
if (isQaUser == "True") {
p1 = new breeze.Predicate("QAManager", "==", username);
select = select + ',QAManager';
} else {
select = select + ',ProjManager';
}
var query = entityQuery
.from('Projects')
.where(p1.and(p2).and(p3))
.select(select);
if (!forceRemote) {
var p = getLocal(query);
if (p.length > 1) {
projectObservable(p);
return Q.resolve();
}
}
return manager.executeQuery(query).then(querySucceeded).fail(queryFailed);
function querySucceeded(data) {
var list = partialMapper.mapDtosToEntities(
manager,
data.results,
model.entityNames.project,
'ProjNum'
);
if (projectObservable) {
projectObservable(list);
}
log('Retrieved projects using breeze', data, true);
}
};
and the code for the partialMapper.mapDtosToEntities function.
var defaultExtension = { isPartial: true };
function mapDtosToEntities(manager,dtos,entityName,keyName,extendWith) {
return dtos.map(dtoToEntityMapper);
function dtoToEntityMapper(dto) {
var keyValue = dto[keyName];
var entity = manager.getEntityByKey(entityName, keyValue);
if (!entity) {
extendWith = $.extend({}, extendWith || defaultExtension);
extendWith[keyName] = keyValue;
entity = manager.createEntity(entityName, extendWith);
}
mapToEntity(entity, dto);
entity.entityAspect.setUnchanged();
return entity;
}
function mapToEntity(entity, dto) {
for (var prop in dto) {
if (dto.hasOwnProperty(prop)) {
entity[prop](dto[prop]);
}
}
return entity;
}
}
EDIT3: Looks like it was my mistake. I found the error when I looked closer at initializeProject. Below is what the function looked like before i fixed it.
function initializeProject(project) {
project.rowClass = ko.computed(function() {
if (project.Type == "R") {
return "project-list-item info";
} else if (project.Type == "P") {
return "project-list-item error";
}
return "project-list-item";
});
}
the issue was with project.Type I should have used project.Type() since it is an observable. It is a silly mistake that I have made too many times since starting this project.
EDIT4: Inside initializeProject some parts are working and others aren't. When I try to access project.ProjTargetDate() I get null, same with project.StartDate(). Because of the Null value I get an error thrown from the moment library as I am working with these dates to determine when a project is late. I tried removing the select from the client query and the call to the partial entity mapper and when I did that everything worked fine.
You seem to be getting closer. I think a few more guard clauses in your initializeProject method would help and, when working with Knockout, one is constantly battling the issue of parentheses.
Btw, I highly recommend the Knockout Context Debugger plugin for Chrome for diagnosing binding problems.
Try toType()
You're working very hard with your DTO mapping, following along with John's code from his course. Since then there's a new way to get projection data into an entity: add toType(...) to the end of the query like this:
var query = entityQuery
.from('Projects')
.where(p1.and(p2).and(p3))
.select(select)
.toType('Project'); // cast to Project
It won't solve everything but you may be able to do away with the dto mapping.
Consider DTOs on the server
I should have pointed this out first. If you're always cutting this data down to size, why not define the client-facing model to suit your client. Create DTO classes of the right shape(s) and project into them on the server before sending data over the wire.
You can also build metadata to match those DTOs so that Project on the client has exactly the properties it should have there ... and no more.
I'm writing about this now. Should have a page on it in a week or so.