Collection Boolean isn't being set to false - Meteor - javascript

So in short, the app that i'm developing is a bus timetable app using Meteor, as a practice project.
inside my body.js, I have an interval that runs every second, to fetch the current time and compare to items in a collection.
To show relevant times, I have added an isActive boolean, whenever the current time = sartTime of the collection, it sets it to true, and that is working fine.
But when I do the same thing for endTime and try to set it to false, so I can hide that timeslot, it just doesn't work. Even consoles don't show up. What am I missing? I have recently just started doing meteor, so excuse the redundancies.
Worth noting that the times that I'm comparing to are times imported from an CSV file, so they have to be in the 00:00 AM/PM format.
Thank you guys so much for your time.
Body.js code:
Template.Body.onCreated(function appBodyOnCreated() {
Meteor.setInterval(() => {
var h = (new Date()).getHours();
const m = ((new Date()).getMinutes() <10?'0':'') + ((new Date()).getMinutes());
var ampm = h >= 12 ? ' PM' : ' AM';
ampmReac.set(ampm);
if (h > 12) {
h -= 12;
} else if (h === 0) {
h = 12;
}
const timeAsAString = `${h}${m}`;
const timeAsAStringFormat = `${h}:${m}`;
whatTimeIsItString.set(timeAsAStringFormat + ampm); // convert to a string
const timeAsANumber = parseInt(timeAsAString); // convert to a number
whatTimeIsIt.set(timeAsANumber); // update our reactive variable
if (Timetables.findOne({TimeStart: whatTimeIsItString.get()}).TimeStart == whatTimeIsItString.get())
{
var nowTimetable = Timetables.findOne({TimeStart: whatTimeIsItString.get() });
Timetables.update({_id : nowTimetable._id },{$set:{isActive : true}});
console.log('I am inside the START statement');
}
else if (Timetables.findOne({TimeEnd: whatTimeIsItString.get()}).TimeEnd == whatTimeIsItString.get())
{
var nowTimetable = Timetables.findOne({TimeEnd: whatTimeIsItString.get() });
Timetables.update({_id : nowTimetable._id },{$set:{isActive : false}});
console.log('I am inside the END statement');
}
}, 1000); //reactivate this function every second
});
})

Very probably it is just that your if / else blocks does what you ask it:
It tries to find a document in Timetables, with specified TimeStart. If so, it makes this document as "active".
If no document is previously found, i.e. there is no timeslot which TimeStart is equal to current time, then it tries to find a document with specified TimeEnd.
But your else if block is executed only if the previous if block does not find anything.
Therefore if you have a next timeslot which starts when your current timeslot ends, your if block gets executed for that next timeslot, but the else if block is never executed to de-activate your current timeslot.
An easy solution would be to transform your else if block in an independent if block, so that it is tested whether the previous one (i.e. TimeStart) finds something or not.

Ok so I got it to work eventually. My problem was that it was never going to the second IF statement.
What I have done is set up a whole new Meteor.interval(() >) function, and placed that second IF in there, as is.
I think the problem was that it was it checks the first IF statement and gets stuck there, no matter what the outcome of the parameters is.

Related

Struggle to commit more than 500 document in firestore batch

I spent the last 2 days trying to find a way to modify a virtually unlimited amount of data in my cloud function.
I am aware it's a topic already heavily discussed but despite trying every single solution I found, I always end up with a similar scenario so I guess it's not only the way iI treat my batches.
The goal here is to be able to scale up our activity and anticipate the growth of users so I'm running some stress tests.
I'm skipping the entire app idea because it would take a while but here is the problem :
1) I am feeding an array with the list of the desired actions for the batch :
tempFeeObject["star"]=FieldValue.increment(restartFee)
let docRefFees = db.collection(collectedFeesDb).doc(element); // log action
finalArray.push([docRefFees,tempFeeObject,"set"])
2) I try to resolve all the final arrays. In this case, for the stress test, we talk about 6006 documents.
Here is the code for it :
try
{
const batches = []
finalArray.forEach((data, i) => {
if (i % 400 === 0) {
let newBatch = db.batch();
batches.push(newBatch)
}
const batch = batches[batches.length - 1]
let tempCommit = {...data[1]}
Object.keys(tempCommit).forEach(key => {
if(data[1][key] && data[1][key].seconds && !Number.isNaN(data[1][key].seconds)){
let sec = 0
let nano = 0
if(Object.keys(data[1][key])[1].includes("nano")){
sec = parseInt(data[1][key][Object.keys(data[1][key])[0]],10)
nano = parseInt(data[1][key][Object.keys(data[1][key])[1]],10)
}else{
sec = parseInt(data[1][key][Object.keys(data[1][key])[1]],10)
nano = parseInt(data[1][key][Object.keys(data[1][key])[0]],10)
}
let milliseconds = (sec*1000)+(nano/1000000)
tempCommit[key] = admin.firestore.Timestamp.fromMillis(milliseconds)
}
});
if(data[2]==="set"){
batch.set(data[0], tempCommit, {merge: true});
}else if(data[2]==="delete"){
batch.delete(data[0]);
}
})
await Promise.all(batches.map(batch => batch.commit()))
console.log(`${finalArray.length} documents updated`)
return {
result:"success"
}
}
catch (error) {
console.log(`***ERROR: ${error}`)
return {
result:"error"
}
}
The middle part might seem confusing but it is made to recreate the timestamp (i had errors when instead of a timestamp I had the array of values instead. And I test the array to find the seconds and nanoseconds because I also had cases where my script found seconds either in the first or second position).
So my script checks each key of the committed document and recreates the timestamp if he finds one.
3) Results :
The results are my main problem :
On one side it is working since my database is being updated with the correct values.
But it is not happening the way it should.
Here are the function logs :
4:04:01.790 PM
autoStationEpochMakerTestNet
Function execution started <=== Function START
4:04:02.576 PM
autoStationEpochMakerTestNet
Auto epoch function treating : 1 actions
4:04:02.577 PM
autoStationEpochMakerTestNet
atation : Arcturian Space Station has 1 actions
4:04:02.582 PM
autoStationEpochMakerTestNet
Function execution took 791 ms. Finished with status: ok <== function END
4:04:02.682 PM
autoStationEpochMakerTestNet
list used : 1 <== But the functions called by the previous one are still printing
4:04:04.926 PM
autoStationEpochMakerTestNet
***ERROR: Error: 4 DEADLINE_EXCEEDED: Deadline exceeded
4:04:05.726 PM
autoStationEpochMakerTestNet
result : { result: 'error' }
4:04:05.726 PM
autoStationEpochMakerTestNet
Arcturian Space Station function epoch generator failed. testmode = true
4:07:02.107 PM
autoStationEpochMakerTestNet
Function execution started <== Second function Trigger START
4:07:02.156 PM
autoStationEpochMakerTestNet
6006 documents updated <== Receiving logs from what i assume is the previous function trigger
4:07:02.158 PM
autoStationEpochMakerTestNet
result : { result: 'success' }
4:07:02.210 PM
autoStationEpochMakerTestNet
Auto epoch function treating : 0 actions <== As expected there is no more action to treat since it was actually processed on the previous call
4:07:02.212 PM
autoStationEpochMakerTestNet
Function execution took 104 ms. Finished with status: ok
So my comprehension of what is happening is as follows :
The function triggers and builds the batches but is somehow instantly finished without waiting for the function or returning anything.
Meantimes there are still logs coming from the function including this one :
***ERROR: Error: 4 DEADLINE_EXCEEDED: Deadline exceeded
So I feel my batch array isn't working as expected.
Then on the following function triggers (scheduled function, every 3 min). All the functions seem to finish to execute properly.
Once again, my database is being updated as it should be.
A few elements might be noticing:
Some of the data being committed are timestamps which I think counts for more than 1 action ?
In this case, one document has more than 2000 fields updated (simple int value)
await promise.all() is new to me so it's also one of my leads.
I set the function ram at 2Go and timeout at 540sec.
Also here is the code for the main function (the scheduled one, which is here to dispatch actions and wait for the answer) :
Object.keys(finalObject).forEach(async (stationName) => {
if(finalObject[stationName].finalList.length>0){
let epochDbUsed = stationsEpochDb[stationName]
let resultGenerator = await generateEpochsForStation(finalObject[stationName].finalList,finalObject[stationName].actionsToRemove,actionsDb,epochDbUsed,testMode)
if(resultGenerator.result==="success"){
await db.collection(logsDb).add({
"user":"cloudFunction",
"type": `createdEpochs${stationName}`,
"station":stationName,
"testMode":testMode,
"actionQty":actionCount,
"note":`New epoch has been created on ${stationName}. testmode : ${testMode}`,
"date":FieldValue.serverTimestamp()
});
}else{
console.log(`${stationName} function epoch generator failed. testmode = ${testMode}`)
await db.collection(logsDb).add({
"user":"cloudFunction",
"type": `createdEpochs${stationName}Failure`,
"station":stationName,
"testMode":testMode,
"actionQty":actionCount,
"note":`New epoch has been created on ${stationName}. testmode : ${testMode}, FAILED`,
"date":FieldValue.serverTimestamp()
});
}
}
});
return null
I am not sure if the return null at the end could mess things up, since its a scheduled function, I'm not sure what I'm supposed to return (actually never really thought of that, and realizing as I write, I'm going to look that up after posting that :) ).
Well, I think this is enough data to understand the situation, feel free to ask for any more details of course.
Thanks a lot to whoever will take the time to help :)
Edit 1 :
I think I might have found a solution :
const _datawait = [];
finalArray.forEach( data => {
let docRef = data[0];
_datawait.push( docRef.set(data[1],{merge: true}) );
})
const _dataloaded = await Promise.all( _datawait );
console.log(`${actionCount} documents updated`)
// update date from last epoch
await updateLastEpochDateEnd(stationEpochDb,lastEpoch.epochId)
// add epoch
await db.collection(stationEpochDb).add(finalEpoch)
return _dataloaded
Doing so wait for all requests stored in the finalArray ([docref],[data]) to receive their promise before returning anything.
Took me a while to get there but it feels good to see the logs in order :
8:46:59.311 PM
stationRestarter
Function execution started
8:46:59.355 PM
stationRestarter
docID : PO3UstEZt1lV1IHwxBIR
8:47:10.667 PM
stationRestarter
6004 documents updated
8:47:11.201 PM
stationRestarter
Function execution took 11890 ms. Finished with status: ok
I'm done for today, I will update tomorrow to see if this is a final solution :)

A string of form nnnn-nnnn is displayed in a spreadsheet as a date or otherwise incorrectly

I have a script I have been using in my test environment to programmically create a tracking number by parsing the year from timestamp and padding the response index.
function setTrackingNumber(ss, lastRowInx, createDateColumn) //This block generates and stores a tracking number in Column AU on the backend
{
var padTrackNo = "" + lastRowInx;
var trackSize = 4;
var trackingNumberColumn = createDateColumn-3; //trackingNumberColumn is currently in AU (Column 47) Calculating using it's relative position to createDateColumn Position
if (ss.getRange(lastRowInx, trackingNumberColumn).getValue() == "") // so that subsequent edits to Google Form don't overwrite original tracking number
{
if (padTrackNo > trackSize)
{
var padTrackNo = pad(padTrackNo, trackSize);
}
else {} //do nothing
var shortYear = setShortYear(ss, lastRowInx, createDateColumn);
var trackingNumber = shortYear + "-" + padTrackNo;
var createTrackingNumber = ss.getRange(lastRowInx, trackingNumberColumn);
createTrackingNumber.setValue(trackingNumber);
}
else {} //should do nothing
return;
}//This is the end of the setTrackingNumber function
function setShortYear(ss, lastRowInx, createDateColumn)
{
var newCreateDate = ss.getRange(lastRowInx,createDateColumn).getValue();
var strDate = "" + newCreateDate;
var splitDate = strDate.split(" ");
var trimYear = splitDate[3];
var shortYear = trimYear;
return shortYear;
}//This is the end of the shortYear function
function pad(padTrackNo, trackSize)
{
while (padTrackNo.length < trackSize)
{
padTrackNo = "0"+padTrackNo;
}
return padTrackNo;
}//This is the end of pad function
That gets me test result which is as expected ex. 2016-0005. However when we added it to another production sheet it seemed to work with test data and then production data showed up like a date 3/1/2016. production result - first cell.
I thought it must just be formatting the string as a date because of the numbers so I tried formatted the column as plain text but that just changed the date to a plain text version of the date.
I thought this might be similar to needing to specify the format like I did in this question Appending initial timestamp from Google Form to end of record in order to permanently store create date onFormSubmit at #SandyGood 's suggestion so I tried setting the number format as [0000-0000] by changing
createTrackingNumber.setValue(trackingNumber);
to
createTrackingNumber.setValue(trackingNumber).setNumberFormat("0000-0000");
which resulted in the [production result - second cell] which again doesn't match the expected result.
Oddly, some submissions seem to work just fine like [production result - third cell]. Over the past 3 days and approximately 10 records it has been fine, then hinky, then fine, they hinky, then fine again. I am not really sure what else to try to debug this odd behaviour.
Note: I had to parse the date as a string as I was having trouble getting it to parse the date correctly from the create date which is taken from initial timestamp.
To my understanding, "2016-0005" is not a number but a string, so the cell containing it should be formatted as plain text. With a script, this can be done by
range.setNumberFormat('#STRING#')
(source), and this must be done before you set the value to the cell. Like this:
createTrackingNumber.setNumberFormat('#STRING#').setValue(trackingNumber);

Comparing 2 javascript numeric values in IF condition

I set up a validation function in javascript to validate the values from the HTML page:
<script>
function validate() {
var tterr = document.sngo2a.ttime; //This variable captures the value in field ttime
var ttserr = document.sngo2a.sngottime; //This variable captures the value in field sngottime
var errortimecheck = 0;
if(ttserr.value > tterr.value)
{
errortimecheck = 1;
var sentence31 = "ERROR!! \n\nTravel time in Stop-&-Go cannot be greater than the \nTotal travel time";
alert(sentence31);
alert(ttserr.value);
alert(tterr.value);
}
else
{
errortimecheck = 0;
}
}
</script>
I'm getting the following values from the html page:
ttime = 10
sngottime = 7
I then expected not to see any alert messages. However, I'm getting the alert message "ERROR!! Travel time in........."
To make things even more confusing, when I replaced sngottime from 7 to 1. The logic runs fine.
When displaying the values for tterr.value and ttserr.value, they seem to be displayed correctly.
Can anyone help me out in figuring out the issue?
I'm going to make an assumption here that your ttime and sngottime are strings.
This means that JavaScript will evaluate them in Lexographic order (alphabetic order).
So, alphabetically, your evaluation will check the following:
Does 7 appear later than 10 alphabetically
It does, therefor go into your first block of code and present the alert
In the case of changing sngottime to 1:
Does 1 appear later than 10 alphabetically?
It doesn't! Go to the else part of your If statement
To remedy this, explicitly cast your values to Integers (or any other numeric type):
if(parseInt(ttserr.value) > parseInt(tterr.value))
{
errortimecheck = 1;
var sentence31 = "ERROR!! \n\nTravel time in Stop-&-Go cannot be greater than the \nTotal travel time";
alert(sentence31);
alert(ttserr.value);
alert(tterr.value);
}
else
{
errortimecheck = 0;
}

Show Div When Dates Match Using .Each

I have a list of job postings and would like to display a div that say 'New' when the date is equal to today's date.
To create this I have created a javascript code that will execute on a loop for each set of outer div's, but I am having trouble correctly running the .each function.
Here is the link to a JSFiddle: http://jsfiddle.net/jeremyccrane/2p9f7/
Here is the HTML Code:
<div class="outer">
<div class="job-date">07-Feb-13</div>
<div class="new" style="display:none;">NEW</div>
<div class="value"></div>
</div>
<div class="outer">
<div class="job-date">12-Feb-13</div>
<div class="new" style="display:none;">NEW</div>
<div class="value"></div>
</div>
Here is the Javascript code:
$( ".outer" ).each(function(i) {
var jd = $(".job-date").text();
j = jd.substr(0,2);
var today = new Date();
var dd = ( '0' + (today.getDate()) ).slice( -2 )
$('.value').html(dd + "/" + j);
if(dd === j) {
$('.new').show();
} else {
$('.new').show();
}
return false;
});
Not too sure what you think the problem is but I could see a couple of issues. Mainly, when you do for example:
$('.new').show();
you're calling show() on ALL matching elements of class new, not just the one 'under' the outer you're in. Try this:
$('.new', this).show();
You were setting the new to show whichever way your date comparison went too. I had a stab at updating your fiddle below:
$( ".outer" ).each(function(i) {
var jd = $(".job-date", this).text();
j = jd.substr(0,2);
var today = new Date();
var dd = ( '0' + (today.getDate()) ).slice( -2 )
$('.value', this).html(dd + "/" + j);
if(dd === j) {
$('.new', this).show();
} else {
// $('.new', this).show();
// do something different here
}
});
This code appears to do what you want:
$(".outer").each(function(i) {
var jd = $(this).find(".job-date").text();
var j = jd.substr(0,2);
var today = new Date();
var dd = ('0' + (today.getDate())).slice(-2);
$(this).find('.value').html(dd + "/" + j);
if(dd == j) {
$(this).find('.new').show();
} else {
$(this).find('.new').hide();
}
return false;
});
http://jsfiddle.net/Dk4dQ/
Your main problem was that you were missing all of the $(this).find() calls. You are iterating through containers. Using $(this).find() will get you the controls within those containers. The plain $() calls will find all matching elements in the document, which is not what you want.
The this in this case refers to the container. Wrapping it in the jQuery function lets us use the jQuery find method to get it's children.
Also, as the others mentioned, you were show()ing the div regardless of success or failure, which again is not what you want.
You have 2 problems here. First of all, you're triggering show() regardless of whether the dates match or not:
if(dd === j) {
$('.new').show();
} else {
$('.new').show(); // <-- shouldn't show in this case
}
The second problem is that you're showing everything with the class "new", when you only want to show particular divs. You'll need to give unique IDs to each "new" div, and then fix your code to only show the divs with that particular ID.
You don't need a complicated iterator function. All you need is:
$(".outer .job-date" ).filter(function() {
return (Date.parse($(this).text()) / 86400000 | 0) == (new Date() / 86400000 | 0);
}).next().show();
filter seems like a better choice for what you're doing than each. First we just want the set of elements that have a date equal to today.
To do that we can take the dates and get rid of the time part. Dates are represented in terms of number of milliseconds since the epoch. By dividing by the number of milliseconds in a day (86400000, or 60*60*24*1000) and then truncating any decimal part (javascript idiom: |0) we are comparing the number of full days since the epoch.
Now with a set of elements containing today's date, next() advances every matched element to the next sibling (the hidden div with class new), and show() shows it.

FullCalendar - is it possible to change the position of the end time?


			
				
Well looking at the code, I couldn't find a very simple way to do this. The reason is, when you add the end time it is added to the start time span, so here is the solution I came up with:
$('#cal').fullCalendar({
'events': test,
'timeFormat': 'H:mm - {H:mm}'
});
$('.fc-event-time').each(function() {
var obj = $(this);
var parent = $(this).parent();
var time = $(this).text();
var start = time.split(' - ')[0];
var finish = time.split(' - ')[1];
obj.text(start);
parent.append('<span class="fc-event-time" style="float: right;">' + finish + '</span>');
});
See it working here
To start with, add the 'timeFormat' to the calendar (I'll assume you already have it to be displaying the finish time), give the times a separator - (this will allow splitting of the two times)
Note any time inside the {} will be the finish time.
Next get jQuery to loop through each element with the class fc-event-time (which is added to the time by fullCalendar).
Now we need to get hold of the parent of the time so we can add an element to it later.
Next get the time and separate on - (space dash space)
Now set the original holder that had both start and finish so it is just the start.
Now add another span element to the parent, which is the header of the event, but float it to the right and add the finish time.
As per justkt's comments, it made me think, the method posted above would only run on page load, so when you changed the view, it wouldn't run again.
To solve that, add a callback when the events are rendered:
$('#cal').fullCalendar({
'events': test,
'eventRender' : renderEvents,
'timeFormat': 'H:mm - {H:mm}',
});
function renderEvents(){
$('.fc-event-time').each(function() {
var obj = $(this);
if (!obj.hasClass('finish-moved')) {
var parent = $(this).parent();
var time = $(this).text();
var start = time.split(' - ')[0];
var finish = time.split(' - ')[1];
obj.text(start);
parent.append('<span class="fc-event-time finish-moved" style="float: right;">' + finish + '</span>');
obj.addClass('finish-moved');
}
});
}
See it working here
This needed a little more logic adding to it, event render appears to be called for each event, so when the finish time span is added, the next time the loop runs it would try to split finish time, so stop that add a class finish-moved so that it isn't affected again.
Another update as per justkt's comments again:
When the callback is called it will pass the object it is affecting, so you can 'operate' on just that, instead of running multiple loops:
$('#cal').fullCalendar({
'events': test,
'eventRender': renderEvents,
'timeFormat': 'H:mm - {H:mm}',
});
function renderEvents(e, a) {
var startObj = $(a).find('.fc-event-time');
var time = $(startObj).text().split(' - ');
$(startObj).text(time[0]);
$(a).find('.fc-event-inner').append('<span class="fc-event-time" style="float: right;">' + time[1] + '</span>');
}
See it working here
It is a very similar principal, just just operates on the event which has called the callback so should be less resource hungry.

Categories

Resources