How to query in RTC 7.0.2 for a formal project, to get each day effort (time tracking) or time spent each day for a task?
That might have changed since 2015, but this thread mentioned
Time tracking is not a field/an attribute. Time tracking entries are added to a work item using a reference/link. I know that others have done this in the past.
The article which deals with this topic is "The Work Item Time Tracking API" from Ralph
The following code update or create time tracking entries, but, in the process, access and read existing time tracking first, which is of interest here.
public void updateOrCreateTimeSheetEntry(WorkItemWorkingCopy workingCopy,
ITimeCode timeCode, Timestamp startDateTimeStamp,
Duration workDuration, Identifier workType,
IProgressMonitor monitor) throws TeamRepositoryException {
// set the active work item from the working copy
setWorkItem(workingCopy.getWorkItem());
// Find a matching time sheet if it exists.
ITimeSheetEntry timeSheet = findTimeSheetEntry(timeCode,
startDateTimeStamp, monitor);
if (timeSheet == null) {
// There is no time sheet for this entry
// Create a new one and create the link
timeSheet = createTimeSheet();
workingCopy.getReferences()
.add(WorkItemEndPoints.WORK_TIME,
IReferenceFactory.INSTANCE
.createReferenceToItem(timeSheet));
// Add the entry to the map to hold the data
addEntry(timeSheet, monitor);
} else {
// There is a time sheet, we need to update it
// Get the workingCopy of the time sheet
timeSheet = (ITimeSheetEntry) timeSheet.getWorkingCopy();
// remove the time spent from current time
setTotalDuration(new Duration(getTotalDuration().longValue()
- timeSheet.getTimeSpent().longValue()));
}
// Set the new data
timeSheet.setStartDate(startDateTimeStamp);
timeSheet.setTimeCodeId(timeCode.getTimeCodeId());
// TODO: If I leave this out it fails....
timeSheet.setTimeCode(timeCode.getTimeCodeLabel());
timeSheet.setTimeSpent(workDuration);
timeSheet.setWorkType(workType);
// add the new time back
setTotalDuration(getTotalDuration().add(workDuration));
// Update the value
// Note: it is important to set the duration value, of the work item
// otherwise the work item is not marked as dirty and in need to update
// in the repository and the save process will not save the time sheet
getWorkItem().setDuration(getTotalDuration().longValue());
workingCopy.getDependentItems().add(timeSheet);
}
You can make a query to fetch work items for a given date, used a stored query, and list your work items that way.
Related
I have a sheet that is pulling data from an API every minute. Each row contains information on an individual NFT that shows what has been done to the NFT for a given day. The data resets once per day at a different time for each row/NFT. The script I have archives each row just before it resets to log what happened with it during that 24-hour period.
The issue is, the script does not always pull the live time updates in my helper cells. I have been logging debugs for the last day and I can see that sometimes it pulls live data, sometimes it pulls the same data for up to 4 minutes. In other words, the countdown clock for a row may show 10 minutes on the first script run, then in the sheet it changes to 9 minutes, but the script will pull 10 minutes again. This may continue again at 8 and 7 minutes remaining, where the script still says 10 minutes. Finally, at 6 minutes, the script will get live data again and show 6 minutes. My script begins with a Spreadsheet flush as well.
I have only found this similar issue reported a year ago, but I did not see it resolved anywhere:
Script pulling old data
I agree with that poster's assumption, it seems Google Script is pulling from a cached version of the sheet. The randomness of this bug is causing major problems with my archive, and so I'm trying to figure out if it is possible to force a fresh read of the sheet, or else if there is known information about this delay so that I can account for this with proper error handling (for example, currently the helper cells continuously flag a row to be archived for 4 minutes, and remove the flag once it's been archived, but I'm not sure if that needs to be even longer)
For reference, this is the code that pulls cached data sometimes, in case there is something I can add to it to force a fresh read (perhaps adding and removing data from a cell just to make it access the sheet?):
function saveHabs() {
var dateStamp = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd");
var ss = SpreadsheetApp.getActiveSpreadsheet();
var habSheet = ss.getSheetByName("Harvests");
SpreadsheetApp.flush;
var beVals = habSheet.getRange("be2:be").getDisplayValues();
var habs = beVals.filter(String).length;
var dataSheet = ss.getSheetByName("New Historical");
var aVals = dataSheet.getRange("a:a").getDisplayValues();
var firstRow = aVals.filter(String).length+1;
Logger.log(habs)
var archiveDetails;
if (habs>0){
archiveDetails = habSheet.getRange("be2:bi"+(habs+1)).getDisplayValues();
dataSheet.getRange(firstRow,1,habs,5).setValues(archiveDetails);
dataSheet.getRange(firstRow,6,habs,1).setValue(dateStamp);
dataSheet.getRange(firstRow,6,habs,1).setNumberFormat("#");
}
Logger.log(archiveDetails);
//debugging logs
var totalCount = habSheet.getRange("BM:BM").getDisplayValues().filter(String).length;
Logger.log(totalCount);
var logFlags = habSheet.getRange("BM2:BP"+totalCount).getDisplayValues();
console.log(habs+" habitats to archive, below are the flags and time remaining (ignore second column, that's only for unharvested habs)");
console.log(logFlags);
}
This is a simplified version of the sheet for a MRE: https://docs.google.com/spreadsheets/d/1od2G4i2YOJleQ6Ph0ICH-XC7L-SsINF8uBEivOsicTY/edit?usp=sharing
It is currently running every minute with logs that should show the same error - that is, the countdown in column BM will not always go down on each new script run, but rather the cached time remaining will be pulled by the script.
I'm not positive if this will fix the issue in all cases, but I came across this problem and solved it as you mentioned. I added a simple value (in my case dummyCell.setValue("Running")) to a blank cell on the source sheet. At the end of the script, the script deletes it. That has, so far, caused the script to always read the live sheet, nothing cached.
This seems to only happen when the source tab is only read from, never written to, so it would appear forcing the write action updates the sheet to the live version.
Hopefully this will solve this for you as well!
I cannot find any property receiving the currently for the user displayed calendar week with google apps-script here
What I want to do is:
Extract the start and end date of the days displayed for the user in google-calendar.
What I already archive is to get events of the current week's Monday (so from the perspective of today):
getEventsOfThisWeek(calendarId) {
calendar = CalendarApp.getCalendarById(calendarId);
let referenceDay = new Date(); //here I would like to get a day from displayed week
let monday = this.getMondayMidnightAM(referenceDay);
let friday = theĆs.getFridayMidnightPM(referenceDay)
let events = calendar.getEvents(monday, friday );
return events
}
getMondayMidnightAM(ref) {
let day = ref.getDay();
let diff = ref.getDate() - day + (day == 0 ? -6 : 1); // adjust when day is sunday
ref.setDate(diff);
let mondayMidnight = new Date(ref.toISOString().replace(/T\S{8}/, "T00:00:00"));
return mondayMidnight;
}
So far I understood, that isSelected() tells me not if the event is on screen in the current view, but rather if the display in all the calendar option is on.
In Short: It is not possible.
The problem with the calendar page you see as a user is that if multiple users have access to the same (e.g. group) calendar, every user might look at at different week.
The script can only access server-side data that is the same for every user (e.g. event name, guests etc.).
Only an Addon can access some specific information that only the user who installed the Addon sees.
This information is contained in object events that become available when a manifest or contextual trigger get fired - e.g. when a user creates or edits an event
However, as you can see in the documentation the Calendar Event Objects do not comprise any information about the currently open week, so there is no way to get this information programamtically
Maybe it is possible to get this data through webscraping, whereby I would not be able to tell you if and how this can be done
I have a google sheet that runs a report on data that is automatically updated. What I would like to do is compare the new inputs with the old inputs to determine if the changes were positive or negative. How would you go about automating a sheet to track these changes?
Changes happen monthly
there would be a score 1-100; 100 being the best
would like to store this data over time for a historical view
Any advice would surely be appreciated
The numbers in each criteria change every month producing a score at the end of the table called Current Score
This score is then pulled into the historical tab as the "Current Score"
What I would like to see happen is that the Current score be saved every month and processed with a percentage change month over month
So I would need a function that stores a copy of the results before they change, processes a new score, and then calculates the difference between the two. Example here is the Dec score (stored values) compared to the most recent score.
Here is a link to the working example
https://docs.google.com/spreadsheets/d/1ImbRhWqGjvIx2CFRKapZ2wmxC9qpSKxxCbHr5tPOBOs/edit#gid=0
Solution
You can automate this process by using Google Apps Script. Open the script editor by clicking on Tools > Script Editor. It is based on JavaScript and allows you to create, access and modify Google Sheets files with a service called Spreadsheet Service.
In addition, you can use Time-driven triggers to run the script automatically once a month. To set it up, click Triggers in the left bar, then Add Trigger and select Time-driven in Select event source. You can now specify the month timer and the exact day and hour you want the script to run. However, I recommend that you do some testing before setting up the trigger to check that you get the desired results. You can test the code by clicking Run in the Editor.
Explanation of the code
There are three functions in the code. The main function is called updateScores and it does what you described in the question. It takes the current score, stores it in a new column and calculates the difference from the last month. Try this function and if you like the result, you can put the trigger in the main function. This way, the trigger calls main which its only responsibility is to call the other two functions. The first is updateScores, which I have already explained, and the second is clearScores, which clears all the values of Reports so you don't have to do it manually and you can start writing the new values for the new month.
I have added some comments so you can understand what each line does.
var lr = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('report').getLastRow()
function updateScores() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Historical')
var currentValues = ss.getRange('B2:B'+lr).getDisplayValues() // get current score
ss.insertColumnsAfter(2,2) // insert two new columns (current score and percent difference)
ss.getRange('D2:D'+lr).setValues(currentValues) // paste stored score
ss.getRange('C2:C'+lr).setFormula('=if(D2=0,"N/A",B2/D2-1)') // apply formula for last stored scores
ss.getRange('E2:E'+lr).setFormula('=if(F2=0,"N/A",D2/F2-1)') // correct formula reference
ss.getRange('E2:E'+lr).copyFormatToRange(ss,3,3,2,lr) // copy format percent
ss.getRange('F2:F'+lr).copyFormatToRange(ss,4,4,2,lr) // copy format scores
var month = new Date().toString().split(' ')[1] // get current month
ss.getRange('D1').setValue(month + ' score') // write current month on last stored scores
var diff = ss.getRange('E1').getDisplayValue() // get diff symbol
ss.getRange('C1').setValue(diff) // write diff
}
function clearScores(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('report')
ss.getRange('B2:G'+lr).clear()
}
function main(){
updateScores()
clearScores()
}
I want to make a Google Form for online order which would display the number of remaining units for each product, updated every time an order is passed.
As a first step, I try to create a TextItem for each product in a spreadsheet and to set the title of this item as "(Name of the product) (remains : (number remaining))"
var wsStocks = SpreadsheetApp.openById(ssID).getSheetByName("Stocks");
var form = FormApp.openById(formID);
function myFunction(){
var Products = wsStocks.getRange(1,1,wsStocks.getLastRow(),1).getValues();
for(j=1;j<Products .length+1;j++){
form.addTextItem().setTitle(wsStocks.getRange(j,1).getValue().toString()+" (remains: "+wsStocks.getRange(j,2).getValue().toString()+")");
};
}
When I run the code, the correct number of items are created, but the title seems to be attributed randomly: sometimes it is the expected title, sometimes it remains blank ("Question"). If I run several times the code, the distribution of correct and blank titles changes (sometimes the first and fourth are correct, the other ones are blank, sometimes it's just the second...)
I can check with Logger.log that "Products" does contain the list of products' names, and that the expression given as argument to SetTitle is indeed what I expect. I have no clue what's going on : /
Explanation / Issue:
It is not a good practice to use getRange and getValue within a for loop. You can construct the 2D array Products to include both column A and B and then you can just index both columns directly. I also used template literals to simplify your string expression:
var Products = wsStocks.getRange(1,1,wsStocks.getLastRow(),2).getValues();
for(j=0;j<Products.length;j++){
form.addTextItem().setTitle( `${Products[j][0]} (remains: ${Products[j][1]})` );
};
To answer your question, the issue with your current solution is that you are iteratively adding questions and setting their titles. However, this process needs some time to be completed and the for loop is always faster. You just need to slow down the requests.
Unlike SpreadsheetApp, FormApp does not support the flush method. But there is a workaround which you can use to slow down the requests and that is the sleep method. Of course, this approach will overall slow down your algorithm and if you specify a big enough time interval, your script might not be able to finish in time.
You need to choose the time interval wisely. In the following example, I used 4 seconds, but feel free to try a smaller or bigger time interval depending on the number of requests you need to process. For example, if you still see that you are getting wrong titles, increase that number.
Solution / Workaround:
var wsStocks = SpreadsheetApp.openById(ssID).getSheetByName("Stocks");
var form = FormApp.openById(formID);
function myFunction(){
var Products = wsStocks.getRange(1,1,wsStocks.getLastRow(),2).getValues();
for(j=0;j<Products.length;j++){
form.addTextItem().setTitle( `${Products[j][0]} (remains: ${Products[j][1]})` );
Utilities.sleep(4*1000); // 4 second delay
};
}
I am importing JSON data to Google Sheets using this solution (https://blog.fastfedora.com/projects/import-json).
It is vital that this data is accurate all the time, however, I have noticed that the data provided through this function lags behind the actual API feeds.
The issue can be sorted if I delete and Ctrl+Z, but obviously I am not available 24/7 to constantly do that :).
A solution I have is that in the cells with the IMPORTJSON function, I have placed the following before it: IF(A1=1,"",IMPORTJSON....
So if 1 is entered in A1, everything is deleted, and once the 1 is deleted, the feeds refresh with the correct data.
Again the issue is that I have to manually enter this 1. I would like to create a method of this one is entered automatically. Like every minute or five minutes.
How do I go about creating this time triggering cell?
function updateCell() {
var range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Name of Sheet").getRange("A1");
range.clear({contentsOnly: true});
range.setValue(1);
}
And set that function to trigger on a timer every x minutes.
See the documentation for further information if you need finer revisions.