wait for Future in for loop - sql

I'm having a future which is a result of an sql query, in it I'm looping over each returned row to add it to a list with a map to encode it to a json format later.
In this loop I'm executing another query depending on the result in each row of the outer query and add those rows again to a map.
Future<Results> mysqlAllTags = mysqlCon.query(query).then((results){
// Make new list as a part of the JsonObject
json.tags = new List();
// Loop throught the row of the result from Mysql data
return results.forEach((row){
// Create Map to put the word, value, rating and id into the JsonObject
Map data = new Map();
// Put the Mysql data into the Map
data["word"] = row.word.toString();
data["value"] = row.value.toString();
data["rating"] = row.rating.toString();
data["id_tag"] = row.id_tag.toString();
data["replacing"] = null;
// Add the Map to the userPages list
json.tags.add(data);
}).then((e){
for(var tag in json.tags){
//Map dataReplacing = getReplacing(userId, row.id_tag.toString());
String replacingTags = getReplacingSQL(tag['id_tag'].toString());
mysqlCon.query(replacingTags).then((result){
result.forEach((row1){
Map map = new Map();
map["word"] = row1.word.toString();
map["value"] = row1.value.toString();
map["id_tag"] = row1.id_replacing_tag.toString();
tag["replacing"] = map;
}).then((e){
print("then inner for called");
return null;
});
print("then inner for called");
return null;
});
}
print("outer for returned");
// Send the data in UTF8 to the client
result = Helpers.formatJsonAndEncodeUtf8('OK', session:_session, data: [json]);
return null;
}).catchError((error){
result = Helpers.formatJsonAndEncodeUtf8('ERROR 856284555 (Could not load tags)', session:_session);
});
}).catchError((error){
result = Helpers.formatJsonAndEncodeUtf8('ERROR 2346644555 (Could not load tags)', session:_session);
});
return Future.wait([mysqlAllTags]).then((e){
print("future returned");
return result;
});
The result looks like this:
outer for returned
=== TO CLIENT ===
{"status":[{"message":"OK","csrfToken":"99"}],"data":[{"tags":[{"word":"Melon","value":"11.0","rating":"1","id_tag":"37","replacing":null},........}]}
=================
future returned
then inner for called
then inner for called
then inner for called
then inner for called
then inner for called
then inner for called
then inner for called
then inner for called
How can I wait until all Futures in my for loop are finished?

I solved it by adding the result declaration in the Future.wait(futures).then() function. Thank you Günter Zöchbauer for your input.
Future<Results> mysqlAllTags = mysqlCon.query(query).then((results){
// Make new list as a part of the JsonObject
json.tags = new List();
// Loop throught the row of the result from Mysql data
return results.forEach((row){
// Create Map to put the word, value, rating and id into the JsonObject
Map data = new Map();
// Put the Mysql data into the Map
data["word"] = row.word.toString();
data["value"] = row.value.toString();
data["rating"] = row.rating.toString();
data["id_tag"] = row.id_tag.toString();
data["replacing"] = null;
// Add the Map to the userPages list
json.tags.add(data);
}).then((e){
var futures = []; // added
for(var tag in json.tags){
print("row ");
print(json.tags);
//Map dataReplacing = getReplacing(userId, row.id_tag.toString());
String replacingTags = getReplacingSQL(tag['id_tag'].toString());
// added `futures.add(...)`
futures.add(mysqlCon.query(replacingTags).then((result) {
result.forEach((row1){
Map map = new Map();
map["word"] = row1.word.toString();
map["value"] = row1.value.toString();
map["id_tag"] = row1.id_replacing_tag.toString();
tag["replacing"] = map;
}).then((e){
print("then inner for called");
return null;
});
print("then inner for called");
return null;
}));
}
print("outer for returned");
// Send the data in UTF8 to the client
return Future.wait(futures).then((e){
result = Helpers.formatJsonAndEncodeUtf8('OK', session:_session, data: [json]);
}); // added
}).catchError((error){
result = Helpers.formatJsonAndEncodeUtf8('ERROR 856284555 (Could not load tags)', session:_session);
});
}).catchError((error){
result = Helpers.formatJsonAndEncodeUtf8('ERROR 2346644555 (Could not load tags)', session:_session);
});
return Future.wait([mysqlAllTags]).then((e){
print("future returned");
return result;
});

I'm not sure if this fixes all issues, it's not easy to see in such code what exactly returns a future. (I added comments where I added something)
Future<Results> mysqlAllTags = mysqlCon.query(query).then((results){
// Make new list as a part of the JsonObject
json.tags = new List();
// Loop throught the row of the result from Mysql data
return results.forEach((row){
// Create Map to put the word, value, rating and id into the JsonObject
Map data = new Map();
// Put the Mysql data into the Map
data["word"] = row.word.toString();
data["value"] = row.value.toString();
data["rating"] = row.rating.toString();
data["id_tag"] = row.id_tag.toString();
data["replacing"] = null;
// Add the Map to the userPages list
json.tags.add(data);
}).then((e){
var futures = []; // added
for(var tag in json.tags){
print("row ");
print(json.tags);
//Map dataReplacing = getReplacing(userId, row.id_tag.toString());
String replacingTags = getReplacingSQL(tag['id_tag'].toString());
// added `futures.add(...)`
futures.add(mysqlCon.query(replacingTags).then((result) {
result.forEach((row1){
Map map = new Map();
map["word"] = row1.word.toString();
map["value"] = row1.value.toString();
map["id_tag"] = row1.id_replacing_tag.toString();
tag["replacing"] = map;
}).then((e){
print("then inner for called");
return null;
});
print("then inner for called");
return null;
}));
}
print("outer for returned");
// Send the data in UTF8 to the client
result = Helpers.formatJsonAndEncodeUtf8('OK', session:_session, data: [json]);
return Future.wait(futures); // added
}).catchError((error){
result = Helpers.formatJsonAndEncodeUtf8('ERROR 856284555 (Could not load tags)', session:_session);
});
}).catchError((error){
result = Helpers.formatJsonAndEncodeUtf8('ERROR 2346644555 (Could not load tags)', session:_session);
});
return Future.wait([mysqlAllTags]).then((e){
print("future returned");
return result;
});

Related

Automatically update my Gsheet with an SQL data base

I need to update a file automatically that already has data in it.
The document is filled with an SQL data base thanks to the code below
However, I want it to update itself everyday without deleting any data that are already in the document and only adding new ones (don't want any duplicates).
function readData(db, queryString) {
//connect to the database
var server = 'your-servername-OR-serverPublicIpAddress';
var username = 'your-sql-username';
var password = 'your-password';
var dbUrl = 'jdbc:sqlserver://' + server + ':1433;databaseName=' + db;
var conn = Jdbc.getConnection(dbUrl, username, password );
//query the data
var stmt = conn.createStatement();
var exec_query = stmt.executeQuery(queryString);
var metaData = exec_query.getMetaData();
var numCols = metaData.getColumnCount();
//save query data to an array
var result=[]; //initiate a blank array
//save the column header
header = []; //initiate the header row
for (var col = 0; col < numCols; col++) {
header.push(metaData.getColumnName(col + 1)); //add the name of each column to the header row
};
result.push(header);//after the header row is formed, put it to the result array
//save the data of each row
while (exec_query.next()) {
row_data = [];
for (var col = 0; col < numCols; col++) {
row_data.push(exec_query.getString(col + 1));//add data of each column to the row data
//Logger.log(row_data);
};
result.push(row_data); // add row data to result
//Logger.log(result);
};
exec_query.close();
return result
};
function pushDataToGoogleSheet(data, SheetName) {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheetByName(SheetName));
var lastRow = sheet.getLastRow();
sheet.getRange(lastRow+1, 1, data.length, data[0].length).setValues(data);
sheet.getDataRange().removeDuplicates();
};
function main() {
db = 'YOUR_DATABASE_NAME'
SQLquery = 'YOUR_SQL_QUERY'
raw_statistics = readData(db, SQLquery); //get raw statistics
pushDataToGoogleSheet(raw_statistics, 'YOUR_SHEET_NAME'); //push to google sheet
};
function main() {
db = 'YOUR_DATABASE_NAME'
SQLquery = 'YOUR_SQL_QUERY'
raw_statistics = readData(db, SQLquery); //get raw statistics
pushDataToGoogleSheet(raw_statistics, 'YOUR_SHEET_NAME'); //push to google sheet
};
However, in the pushDataToGoogleSheet it says that it can't define the length. So, I don't know if I put the right thing for data or not or if there is an issue in my code...
Do you have an idea ?
Thank you for your help !

Inserting Nested Objects EF Core 5

I have the following entities:
Batch
Samples
SampleContainers
SampleTests
A batch contains many samples. A sample contains many SampleContainers and many SampleTests.
I am trying to make a copy of batch and insert into database.
Attempt #1: get function in repository:
return await context.Set<TEntity>().FindAsync(id);
Controller:
var coc = await repository.Get(batchId);
coc.BatchStatusId = (int)Enums.BatchStatus.InProgress;
coc.IsTemplate = false;
coc.Id = 0;
var b = await repository.Add(coc);
Here only batch object was duplicated but related samples and containers were not duplicated/inserted.
Attempt #2: I changed my Get function as follows:
public async override Task<Batch> Get(int id)
{
return await context.Set<Batch>()
.Include(p => p.Samples)
.FirstOrDefaultAsync(p => p.Id == id);
}
This time batch was duplicated but samples and containers and tests were all updated with new batchId/FK (I wanted them all to be duplicated).
Attempt #3: following this, I implemented as follows:
public async Task<int> DuplicateBatch([FromBody]int batchId)
{
try
{
var coc = await repository.Get(batchId);
coc.BatchStatusId = (int)Enums.BatchStatus.InProgress;
coc.IsTemplate = false;
coc.Id = 0;
var samples = coc.Samples.ToList();
repository.DetachEntity(coc);
var b = await repository.Add(coc);
var allSampleTests = await sampleTestRepo.GetAll();
var allSampleContainers = await sampleContainersRepo.GetAll();
var sampletests = from st in allSampleTests
join s in samples on st.SampleId equals s.Id
select st;
var sampleContainers = from sc in allSampleContainers
join s in samples on sc.SampleId equals s.Id
select sc;
sampleRepo.DetachEntities(samples);
sampleTestRepo.DetachEntities(sampletests.ToList());
sampleContainersRepo.DetachEntities(sampleContainers.ToList());
foreach (var s in samples)
{
s.BatchId = b.Id;
var sample = await sampleRepo.Add(s);
foreach (var st in sampletests)
{
st.SampleId = sample.Id;
await sampleTestRepo.Add(st);
}
foreach(var sc in sampleContainers)
{
sc.SampleId = sample.Id;
await sampleContainersRepo.Add(sc);
}
}
return 1;
}
catch (Exception ex)
{
return 0;
}
}
This time I am facing the following exception as soon as I reach Detach function:
{"The property 'Batch.Id' is part of a key and so cannot be modified
or marked as modified. To change the principal of an existing entity
with an identifying foreign key, first delete the dependent and invoke
'SaveChanges', and then associate the dependent with the new
principal."}
This is how I did it, most of it is self explanatory.
public async Task<int> DuplicateBatch([FromBody]int batchId)
{
try
{
//STEP 1: Fetch the entities
var coc2 = await repository.Get(batchId);
var samples = coc2.Samples.ToList();
var allSampleTests = await sampleTestRepo.GetAll();
var allSampleContainers = await sampleContainersRepo.GetAll();
var sampletests = samples.SelectMany(st => st.SampleTests).ToList();
var samplecontainers = samples.SelectMany(st => st.SampleContainers).ToList();
//STEP 2: Detach
var coc = repository.DetachEntity(coc2);
var samplesDetached = sampleRepo.DetachEntities(samples);
var sampleTestsDetached = sampleTestRepo.DetachEntities(sampletests);
var sampleContianersDetached = sampleContainersRepo.DetachEntities(samplecontainers);
//STEP 3: Update object
coc2.BatchStatusId = (int)Enums.BatchStatus.InProgress;
coc2.IsTemplate = false;
var b = await repository.Add(coc);
return 1;
}
catch (Exception ex)
{
return 0;
}
}

Problemas with API Key

I'm having some difficulties trying to access the ontologias of AgroPortal, it says my api key is not valid but I created an account and it was given to me an api key.
I'm trying to do like I did with BioPortal since the API is the same but with the BioPortal it works, my code is like this:
function getAgroPortalOntologies() {
var searchString = "http://data.agroportal.lirmm.fr/ontologies?apikey=72574b5d-b741-42a4-b449-4c1b64dda19a&display_links=false&display_context=false";
// we cache results and try to retrieve them on every new execution.
var cache = CacheService.getPrivateCache();
var text;
if (cache.get("ontologies_fragments") == null) {
text = UrlFetchApp.fetch(searchString).getContentText();
splitResultAndCache(cache, "ontologies", text);
} else {
text = getCacheResultAndMerge(cache, "ontologies");
}
var doc = JSON.parse(text);
var ontologies = doc;
var ontologyDictionary = {};
for (ontologyIndex in doc) {
var ontology = doc[ontologyIndex];
ontologyDictionary[ontology.acronym] = {"name":ontology.name, "uri":ontology["#id"]};
}
return sortOnKeys(ontologyDictionary);
}
var result2 = UrlFetchApp.fetch("http://data.agroportal.lirmm.fr/annotator", options).getContentText();
And what I did with BioPortal is very similar, I did this:
function getBioPortalOntologies() {
var searchString = "http://data.bioontology.org/ontologies?apikey=df3b13de-1ff4-4396-a183-80cc845046cb&display_links=false&display_context=false";
// we cache results and try to retrieve them on every new execution.
var cache = CacheService.getPrivateCache();
var text;
if (cache.get("ontologies_fragments") == null) {
text = UrlFetchApp.fetch(searchString).getContentText();
splitResultAndCache(cache, "ontologies", text);
} else {
text = getCacheResultAndMerge(cache, "ontologies");
}
var doc = JSON.parse(text);
var ontologies = doc;
var ontologyDictionary = {};
for (ontologyIndex in doc) {
var ontology = doc[ontologyIndex];
ontologyDictionary[ontology.acronym] = {"name":ontology.name, "uri":ontology["#id"]};
}
return sortOnKeys(ontologyDictionary);
}
var result = UrlFetchApp.fetch("http://data.bioontology.org/annotator", options).getContentText();
Can someone help me?
Thanks, my regards.

Get resolved SPUser IDs from Sharepoint 2010 PeoplePicker

I try to get selected user IDs from people picker control as below:
function GetUserIdsFromPP() {
var xml = _picker.find('div#divEntityData');
var visiblefor = new Array();
xml.each(function (i, row) {
var data = $(this).children().first('div').attr('data');
var xmlDoc;
if (window.DOMParser) {
parser = new DOMParser();
xmlDoc = parser.parseFromString(data, "text/xml");
}
else // Internet Explorer
{
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = false;
xmlDoc.loadXML(data);
}
var uid = xmlDoc.getElementsByTagName('Value')[0].firstChild.nodeValue;
visiblefor.push(uid);
});
return visiblefor;
}
The problem is that sometimes XML doesn't contain <Key>SPUserID</Key><Value>1</Value> and I get FQUN (user login with domain name).
What is the better way to resolve selected SPUserIds from PeoplePicker control?
This is how resolve emails from people picker control on client side
function GetEmailsFromPicker() {
var xml = _picker.find('div#divEntityData');
var result = new Array();
xml.each(function (i, row) {
var data = $(this).children().first('div').attr('data');
var xmlDoc;
if (window.DOMParser) {
parser = new DOMParser();
xmlDoc = parser.parseFromString(data, "text/xml");
}
else // Internet Explorer
{
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = false;
xmlDoc.loadXML(data);
}
var emailIndex = -1;
for (var i = 0; i < xmlDoc.childNodes[0].childNodes.length; i++) {
var element = xmlDoc.childNodes[0].childNodes[i];
var key = element.childNodes[0].childNodes[0].nodeValue;
if (key == 'Email') {
var uid = xmlDoc.childNodes[0].childNodes[i].childNodes[1].childNodes[0].nodeValue;
result.push({ EMail: uid });
break;
}
}
});
return result;
}
Use the above answer, but...
Replace this with an appropriate Jquery or Javascript element name.
var xml = _picker.find('div#divEntityData');

JScript.NET private variables

I'm wondering about JScript.NET private variables. Please take a look on the following code:
import System;
import System.Windows.Forms;
import System.Drawing;
var jsPDF = function(){
var state = 0;
var beginPage = function(){
state = 2;
out('beginPage');
}
var out = function(text){
if(state == 2){
var st = 3;
}
MessageBox.Show(text + ' ' + state);
}
var addHeader = function(){
out('header');
}
return {
endDocument: function(){
state = 1;
addHeader();
out('endDocument');
},
beginDocument: function(){
beginPage();
}
}
}
var j = new jsPDF();
j.beginDocument();
j.endDocument();
Output:
beginPage 2
header 2
endDocument 2
if I run the same script in any browser, the output is:
beginPage 2
header 1
endDocument 1
Why it is so??
Thanks,
Paul.
Just a guess, but it appears that JScript.NET doesn't support closures the same way as EMCAScript, so the state variable in endDocument() isn't referencing the private member of the outer function, but rather an local variable (undeclared). Odd.
You don't have to use new when calling jsPDF here since you're using a singleton pattern. jsPDF is returning an object literal so even without new you'll have access to the beginPage and endDocument methods. To be perfectly honest I don't know what the specifications call for when using new on a function that returns an object literal so I'm not sure if JScript.NET is getting it wrong or the browser. But for now try either getting rid of the new before jsPDF() or change your function to this:
var jsPDF = function(){
var state = 0;
var beginPage = function(){
state = 2;
out('beginPage');
};
var out = function(text){
if(state == 2){
var st = 3;
}
MessageBox.Show(text + ' ' + state);
};
var addHeader = function(){
out('header');
};
this.endDocument = function(){
state = 1;
addHeader();
out('endDocument');
};
this.beginDocument: function(){
beginPage();
};
}
That will allow you to use the new keyword and create more than one jsPDF object.
I've come across the same problem. In the following code, the closure bound to fun should contain only one variable called result. As the code stands, the variable result in the function with one parameter seems to be different to the result variable in the closure.
If in this function the line
result = [];
is removed, then the result in the line
return result;
refers to the result in the closure.
var fun = function() {
var result = [];
// recursive descent, collects property names of obj
// dummy parameter does nothing
var funAux = function(obj, pathToObj, dummy) {
if (typeof obj === "object") {
for (var propName in obj) {
if (obj.hasOwnProperty(propName)) {
funAux(obj[propName], pathToObj.concat(propName), dummy);
}
}
}
else {
// at leaf property, save path to leaf
result.push(pathToObj);
}
}
return function(obj) {
// remove line below and `result' 3 lines below is `result' in closure
result = []; // does not appear to be bound to `result' above
funAux(obj, [], "dummy");
return result; // if result 2 lines above is set, result is closure is a different variable
};
}();