When I was searching for an answer to this, I only encountered developers looking for ways to update their apps' live tiles more and more frequently. I want the opposite, sort of.
See I'm developing a weather app, and I want it to update every hour but for a specific hourly range only. That is, I don't want the user to have the ability to update the tile once every hour because 1) people sleep and 2) the API I'm using is free only for the first 1,000 calls per day. In other words, users don't need to it update every hour and I can't afford to give them the option to anyway.
So is it possible to get, for example, the live tile to update every hour from 8am to 11pm, and to not make any calls from 12pm till 7am?
If you make the call to the API in your ScheduledAgent, simply wrap the call in an if block that checks the time. I had a similar need to update the tile once a day (it was counting down the days until Xmas).
This code is in my ScheduledAgent.cs file. It checks the date (should only trigger in December and before the 26th) and sets the countdown, then sends a toast notification only on Xmas morning. It should be a good example of how to restrict API calls to a set time of dat in your background task.
if (DateTime.Now.Month == 12 && DateTime.Now.Day < 26)
{
//number of days until the 25th
var countdown = ((new DateTime(DateTime.Now.Year, 12, 25).DayOfYear) - DateTime.Now.DayOfYear);
if (secondaryTile != null)
{
var imageString = "/Images/Tiles/" + countdown + ".png";
var newTileData = new StandardTileData
{
BackgroundImage = new Uri(imageString, UriKind.Relative)
};
secondaryTile.Update(newTileData);
}
var now = DateTime.Now;
if (now.Day == 25 && now.TimeOfDay.Hours == 9 && (now.TimeOfDay.Minutes > 14 && now.TimeOfDay.Minutes < 46))
{
var toast = new ShellToast { Title = "Xmas Countdown", Content = "Merry Xmas! Thank you for using 'Quick Xmas List' and have a safe holiday!" };
toast.Show();
}
}
Related
I'm trying to create a schedule heat map so that we can adjust staffing times and days. In order to make this as easy as possible, I've come up with a tabular structure that allows the user to input the employee's name, then select their shift start time and their shift end time from a drop-down, and then use checkboxes to indicate which days they will work, as shown:
The end result would be a heat map that counts the number of instances that a value exists in the range between the start time and end time, broken down by hour and by day. My original thought was to use COUNTIFS thusly: =COUNTIFS(Calculations!D:D, ">=9:00:00", Sheet9!D:D, "=TRUE") Where Calculations!D:D is the column of the selected Start Time, where ">=9:00:00" checks to see if the start time is greater than or equal to 9AM, and where Sheet9!D:D, "=TRUE" checks to see if the checkbox for that day is checked. So this example would check to see if someone is working at 9AM on Monday.
However, this didn't pan out since we're checking for any value greater than 9AM, and most employees won't be working more than 10 hours, so I'm getting false positives.
My next thought was to use a named range that would start at the Start Time value and then, if necessary, loop back through to the End Time (for example, if an employee started at 10PM and their shift ended at 7AM). Since this range would be dynamic (not all employees will work strictly 8 hours per day), I would need to check to see if a value exists within the range, however, I'm not sure how to A: Loop through or B: check to see if a value is in the dynamic range. I assume this will require Google Apps Script to pull off, but I'm not well-versed in it, and I've been beating my head against a wall trying to figure this out. Any help would be appreciated!
Oh, and here's a screenshot of the desired output, with a couple of values filled in:
I don't know if this is exactly what you wanted but I wanted to practice using times is Google Scripts so I had a crack at solving your problem. However, there are a couple of little bits I didn't have time to write that I've commented in the script.
If I've understood your problem correctly, you want to take a series of shifts life this:
And convert it into a heatmap like this:
(I've not done all your formatting, but I'm guessing you want to see how many people are available for every hour of the day)
In my code, I called the first sheet above "Roster" and the second sheet "RosterOverview" - note the capitalisation and lack of spaces.
Approach
I wrote two scripts. One (rostering) to check which days a staff member worked and a second to see the hours they worked and update the values in RosterOverview (updateCalendar).
For each member of staff, I took their working hours and working days. I checked to see if they worked on a certain day and, if they did, sent their working hours to a script called updateCalendar. This second script then looked to see if they worked a night shift i.e. over midnight or not. The script then adds a 1 to each hour window a staff member is rostered for.
The scripts
function rostering() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var rosterSheet = ss.getSheetByName("Roster");
var overviewSheet = ss.getSheetByName("RosterOverview");
var range = rosterSheet.getRange(2,2,4,9); //You will need to work out how to accurately get this range for your data - 2 and 2 are the starting row and column respectively i.e. B2; 4 indicates the number of rows; 9 indicates the number of columns
var times = range.getValues();
var calendarRange = overviewSheet.getRange("B2:I25"); //On RosterOverview, this is the range of cells for Monday-Monday, midnight-2300. You need 8 columns of data because the extra one is for Sunday night overflowing into Monday morning.
//You will need to write a bit of code yourself to make sure all the values in calendarRange are set to 0. I have set this manually and pull them below when I was testing.
var calendarValues = calendarRange.getValues();
//This section loops through each staff member's hours and days.
//It looks to see if the check box is ticked for each day of the week in turn.
//If a checkbox is ticked, it calls a second script: updateCalendar
//It's probably possible to write a separate loop to go through the days of the week
for (var i = 0; i < range.getHeight(); i++){
//if Monday shift
if(times[i][2] == true){
calendarValues = updateCalendar(times[i][0], times[i][1], calendarValues, 0)
}
//if Tuesday shift
if(times[i][3] == true){
calendarValues = updateCalendar(times[i][0], times[i][1], calendarValues, 1)
}
//if Wednesday shift
if(times[i][4] == true){
calendarValues = updateCalendar(times[i][0], times[i][1], calendarValues, 2)
}
//if Thursday shift
if(times[i][5] == true){
calendarValues = updateCalendar(times[i][0], times[i][1], calendarValues, 3)
}
//if Friday shift
if(times[i][6] == true){
calendarValues = updateCalendar(times[i][0], times[i][1], calendarValues, 4)
}
//if Saturday shift
if(times[i][7] == true){
calendarValues = updateCalendar(times[i][0], times[i][1], calendarValues, 5)
}
//if Sunday shift
if(times[i][8] == true){
calendarValues = updateCalendar(times[i][0], times[i][1], calendarValues, 6)
}
}
//Once ithas looped through al the staff member's shifts, it updates the RosterOverview
//sheet with how many people are on each hour
calendarRange.setValues(calendarValues);
}
function updateCalendar(startTime, endTime, roster, day){ //Let day be a digit: 0 for Monday, 1 for Tuesday.... Roster is the calendarValues array from the roster function
if(startTime > endTime){ //night shift over midnight
for (var j = startTime.getHours(); j < 24; j++){
roster[j][day] = roster[j][day] + 1;
}
for (var j = 0; j < endTime.getHours(); j++){
roster[j][day + 1] = roster[j][day + 1] + 1;
}
} else {
for (var j = startTime.getHours(); j < endTime.getHours(); j++){
roster[j][day] = roster[j][day] + 1;
}
}
return roster;
}
What you need to do
It's probably best to add a custom menu so you can run it from the sheet: see this Fundamentals codelab to learn how: https://developers.google.com/codelabs/apps-script-fundamentals-3#0
You need to make sure your Google Sheet and the Google script are set to the same timezone. To set the timezone for the script, from the script editor you want to click on [Use legacy editor] in the top right hand corner then go to File > Project properties. Once set, return to the new editor.
You will need to write a little bit of code that selects the range of data for your staff members' shifts and a bit of code that resets the RosterOverview spreadsheet values to 0s before you run the script again.
Let me know if you have any problems.
I'm trying to reflect Zoom schedules from 32 Zoom accounts on Google Calendar. I have shared 32 Calendar to 1 Master Calendar. I will get Scheduled Meetings through API and create Google Calendar Events.
function createEvent() {
var lstMeetings = getMeetings(); //Creating Zoom Meetings list
var lstAcc = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1].getRange(2, 1, 32, 4).getValues(); //Getting Calendar ID
var calendar, events_delete;
var now = new Date();
var end = new Date(now.getTime() + (180 * 24 * 60 * 60000));
var temp;
for (var i = 0; i < lstAcc.length; i++) {
Logger.log(lstMeetings[i].length);
for (var j = 0; j < lstMeetings[i].length; j++) {
var events = CalendarApp.getCalendarById(lstAcc[i][0]).createEvent(
lstMeetings[i][j].topic,
new Date(lstMeetings[i][j].start_time),
new Date(lstMeetings[i][j].end_time),
{description: lstMeetings[i][j].description}
);
Logger.log(events.getId());
}
}
}
I'm running into You have been creating or deleting too many calendars or calendar events in a short time. Please try again later. error.
Addition to the code, I would like to bulk delete all events and create new events reflect to the schedule on Zoom. How can I achieve that?
I'm thinking to switch to Webhook so that it would be easier to handle, but I'm not sure how to do that yet.
If you have any advice, please let me know.
Thank you!
You have been creating or deleting too many calendars or calendar events in a short time. Please try again later.
Means exactly that you have been doing to much. This is a free api there are limits to the number of creates and deletes you can make over a period of time. This limit is to my knowledge undocumented. I would start with one a minute and work your way up and down until you find the limits.
This is the closes thing i have found Avoid calendar use limits It doesnt say how many are allowed per minute or per hour.
Let's suppose I create a recurring event that starts on Monday at 9 AM and ends at 11 AM, this event repeats every day for 3 days.
Now I want (after I have created the events using recurrence) to change the start time of the event on Tuesday while leaving the other events unchanged, how could I do ?
I can easily get the recurrence rule for this eventSeries using the advanced calendar API, it returns something like
RRULE:FREQ=DAILY;COUNT=3
This rule can be modified at will to change all the events (and I can also update the events using patch but not just for a single event.
I tried the API tryout to get every instance of this eventSeries and I can indeed see every start and end times (*see below) but I didn't find any method to get that using the Calendar API in Google Apps Script.
I didn't find a way to modify a particular instance, i.e. to write back the modified values.
Since I can do this manually in the calendar web UI I guess I should be able to do it using a script but I really don't know how.
The code I use to get the event parameters is fairly simple :
...
for(var n = 1 ; n < data.length ; n++){
if(data[n][9] == ''){continue};
var advancedID = data[n][9].substring(0,data[n][9].indexOf('#'));
Logger.log(advancedID+'\n');
ChangeEventRecurrence(calID,advancedID);
}
}
function ChangeEventRecurrence(calID,advancedID){
var event = Calendar.Events.get(calID, advancedID);
Logger.log(event.recurrence+'\n'+JSON.stringify(event));
// here I should be able to get all the instances of this eventSeries and change them if needed
}
Here is a capture of an instance I get using the API tryout :
With CalendarApp, just specify the date range to look for the single event and loop through the result to then use .setTime() :
var cal = CalendarApp.getCalendarsByName('<YOUR_CAL>')[0];
var events = cal.getEvents(new Date("April 5, 2016 08:00:00 AM"), new Date("April 5, 2016 11:30:00 AM"));
events.forEach( function(e) {
//var e = events[0];
//if ( e.getTitle() === 'Your Title' )
e.setTime(new Date("April 5, 2016 11:00:00 AM"), new Date("April 5, 2016 01:00:00 PM"));
});
I can't tell if your "specific instance" means just a single event or every Tuesday event though, or whether you have a reason to even use the Advanced Calendar API for this case.
I'm trying to create a trigger that activates on weekdays only and at a specific time, but I don't know what I'm doing wrong. Here is my code.
function createTriggers() {
var days = [ScriptApp.WeekDay.MONDAY, ScriptApp.WeekDay.TUESDAY,
ScriptApp.WeekDay.WEDNESDAY, ScriptApp.WeekDay.THURSDAY,
ScriptApp.WeekDay.FRIDAY];
var d = new Date();
var time = d.toLocaleTimeString();
if (time == '3:05:00 PM EDT') {
for (var i = 0; i < days.length; i++) {
ScriptApp.newTrigger(Lunch1)
.timeBased().onWeekDay(days[i])
.everyMinutes().create();
}
}
}
I'm guessing that you mean you want to make it so a function is triggered (runs) every weekday at a certain time of the day. Here's what I would do.
First, in the Script Editor:
Go to Resources - Current Project's Triggers
Click Add a new trigger
Select the function that you want to be triggered
Change the next box to "Time-driven"
Change the next box to "Hour timer"
Then change the last box to "every hour"
Then at the very beginning of your function, add this code:
var d = new Date();
if (d.getDay() == 6 || d.getDay() == 0) return;
// more info here: http://www.w3schools.com/jsref/jsref_getday.asp
That will stop the rest of the script from running if it's a Saturday or Sunday.
Then, say you want the script to only run at one specific time each weekday, you could set the time-driven trigger to "Every minute" and then add another if statement after the one above:
if (d.getHours() != 15 && d.getMinutes() != 5) return;
That will stop the rest of the script from running if it's not exactly 15:05 (3:05 PM).
This is definitely not the most efficient way to do this, but it works.
Also, this is worth mentioning: I'm not sure if there's anything that would prevent you from triggering the script every minute. Google does impose quotas for what your scripts can do, but I didn't see anything about how many times a script can be triggered in a day. Here's the chart: https://developers.google.com/apps-script/guides/services/quotas
You should do something like this:
function createTriggers(func, hour) {
var weekdays = [ScriptApp.WeekDay.MONDAY, ScriptApp.WeekDay.TUESDAY,
ScriptApp.WeekDay.WEDNESDAY, ScriptApp.WeekDay.THURSDAY,
ScriptApp.WeekDay.FRIDAY];
for (var dayIndex in weekdays) {
var day = weekdays[dayIndex];
ScriptApp.newTrigger(func).timeBased()
.onWeekDay(day).atHour(hour).create();
}
}
This code creates a trigger for weekdays in a specific time calling the func function.
I have some dates in a Google Spreadsheet that I'm bringing in to a script like this:
var JCstartDateFix = Math.floor(Date.parse(JCstartDate) / 86400000) + 25570;
var todaysDateFix = Math.floor(Date.parse(todaysDate) / 86400000) + 25570;
How do I do the opposite of this at the end of the script to change it back into a mm/dd/yyyy formatted date?
Thanks in advance for any help on this.
Here's the whole script:
function projectedDate(JCstartDate, overallPercent, pace, todaysDate, HSstartDate, DaysInHS) {
//converts dates to a number of days
var JCstartDateFix = Math.floor(Date.parse(JCstartDate) / 86400000) + 25570;
var todaysDateFix = Math.floor(Date.parse(todaysDate) / 86400000) + 25570;
//This says that there's no projected date since the student hasn't started high school yet
if(HSstartDate == ""){
return "HS not started";
}
//This calculates grad date if the student's been here more than 8 months or if their percent is over 80.
else if(DaysInHS >= 200 || overallPercent >=80){
var percentPerDay = overallPercent/(DaysInHS);
var daysLeft = (100 - overallPercent) / percentPerDay;
if((todaysDateFix + daysLeft) > (JCstartDateFix +730)){
return "You are not on track to complete.";
}
else{
return (todaysDateFix + daysLeft);
}
}
//This calculates grad date if the student's been at JC less than 8 months
else{
if(JCstartDateFix + 600 - pace > JCstartDateFix + 730){
return "You are not on track to complete.";
}
else{
return (JCstartDateFix+600-pace);
}
}
}
I work in a school where students start at different times and work at their own pace. They have a 2 year limit to finish. So this script estimates their graduation date based on when they started and how fast they're going. It uses different formulas depending on how long they've been here. I'm happy with the dates I get on my spreadsheet, but if I format them from the spreadsheet, another script doesn't correctly pick up the text strings and gives a date in 1969 instead.
I think what I need to do is change the lines that return numbers so that those numbers are formatted as dates. I just don't know how. Thanks again!
The value you get with Date.parse() is in milliseconds, you divide it by the number of milliseconds in a day so I guess you obtain the number of days since the JS reference date, rounded to the lowest integer and then add a constant value of 25570.
What is the result supposed to be ?
It seems that it should be a number of day from the ref date but that's quite far in the future !! (about 70 years) Is this right ? could you clarify ?
Anyway, what you should do is to get a value in milliseconds again and use new Date(value in mSec) to get a date object. From there Utilities.formatDate will allow you to get any display format you want.
ref : http://www.w3schools.com/jsref/jsref_obj_date.asp
https://developers.google.com/apps-script/class_utilities#formatDate
As long as the value that you're setting in the spreadsheet is a Date object in apps script, it will appear as a date. The format will be under the control of the spreadsheet, of course, but it defaults to mm/dd/yyyy.
For example, you could just change your existing code to render Date objects. Then, when you call setValue() you will write a date out to the spreadsheet.
var JCstartDateFix = new Date(Math.floor((Date.parse(JCstartDate) / 86400000) + 25570)*86400000);
var todaysDateFix = new Date(Math.floor((Date.parse(todaysDate) / 86400000) + 25570)*86400000);