Getting the cell given the cell value in google sheets using app script - date

I'm trying to write a script that tracks payment dates in google sheets (shows a different colour (either FontColor or Background) three days before payment, another colour on the day of payment and a totally different colour after the payment date.I'd appreciate if there's anyone with know how on how to use those values to get the cell name and use it to change the FontColor or alternatively if there's a better solution
Here is my google sheet
[![enter image description here][1]][1]
This is the code I've written to get the dates into a list
function myFunction() {
let spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
let lastRow = spreadsheet.getLastRow();
let lastCol = spreadsheet.getLastColumn();
var dataRange = spreadsheet.getActiveSheet().getRange(2, 11, lastRow, lastCol)
dataRange.setFontColor("green")
var data = dataRange.getDisplayValues();
let dates=[];
for (let i=0; i < dates.length; i++ ) {
// console.log(dates[i])
if (dates[i] === new Date().toLocaleDateString()) {
dataRange.setBackground('pink')
} else if (dates[i]) {
// do sth
} else {
// maintain the current state
}
}
}

Does it need to be with scripts?? With conditional formatting that would be MUCH faster, easier and uploads constantly.
You can apply it to the entire sheet or to a specific range. Use this custom formula (change A1 with the top left formula of your range)
=if(A1="",FALSE,(A1 - Today()) < 0)
Get sure to set these conditions in the correct order (in these case it would be preferrable to be the past dates, the actual date and the close future dates). Like this:
Here you have a link to play with:
https://docs.google.com/spreadsheets/d/1zhEFRQwOyAYQwXfv5lYTjI7B-6fIfz1rgdCt3MGvzmI/edit?usp=sharing

Payment Tracker
function paymentTracker() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
const rg = sh.getRange(2, 11, sh.getLastRow() - 1, sh.getLastColumn() - 10);
rg.setFontColor("black")
const vs = rg.getDisplayValues();
const d = new Date();
//Logger.log('y: %s,m: %s,d: %s', d.getFullYear(), d.getMonth(), d.getDate());
const dt = new Date(d.getFullYear(), d.getMonth(), d.getDate());
const dtv = dt.valueOf();
const dt3 = new Date(d.getFullYear(), d.getMonth(), d.getDate() + 3);
const dt3v = dt3.valueOf();
vs.forEach((r, i) => {
let ds = r.map(ds => {
let t = ds.split('/');
//Logger.log(JSON.stringify(t))
let v = new Date(Number(t[2]), Number(t[1]) - 1, Number(t[0])).valueOf();
let diff3 = dt3v - v;
if (dt3v == v) {
return "purple";
} else if (dtv == v) {
return "green";
} else {
return "pink";
}
});
sh.getRange(i + 2, 11, 1, ds.length).setBackgrounds([ds]);
})
}

Related

Make TODAY formula stop when checkbox is clicked

I'm setting up a Google Sheet with a few columns to be filled for a certain request. So, I included a checkbox to be clicked at the end as a confirmation the request is done. My idea is to have an automated column called 'Request Date' automatically filled with the current date as soon as the Confirmation checkbox is clicked. However, can't use TODAY() formula once it's going to change the date every day. Any solution for this?
unfortunately no. you can't stop TODAY on demand by any means without a script. but you could have a script which would print out the date if certain checkboxes were checked.
function onEdit(e) {
var aCell = e.source.getActiveCell(), col = aCell.getColumn();
if(col == 5) { //number of column where you have a checkbox
var adjacentCell = aCell.offset(0,1);
var newDate = Utilities.formatDate(new Date(),
"GMT+1", "dd/MM/yyyy");
adjacentCell.setValue(newDate);
}}
this will print date in column F if a checkbox in column E is checked
or perhaps like this:
function onEdit(e) {
var activeSheet = e.source.getActiveSheet();
if (activeSheet.getName() == "Sheet1") { // SHEET NAME
var aCell = e.source.getActiveCell(), col = aCell.getColumn();
if (col == 12) { // COLUMN WITH CHECKBOXES
var dateCell = aCell.offset(0,1); // OFFSET ROWS, COLUMNS
if (aCell.getValue() === true) { // VALUE OF CHECKED CHECKBOX
var newDate = new Date();
dateCell.setValue(newDate);
} else {
dateCell.setValue("");
}}}}

How do I leave the clicked point highlighted in dygraphs?

I am using the selected shapes to draw a larger diamond shape on my graph. When a user clicks a point. I display the data in another div, but I want to leave the clicked point highlighted. In other words, I want to 'toggle' data behind the points on and off and the clicked points need to show if they are included in the dataset. I believe I have seen this somewhere but I cannot find it. Is there a 'standard' way of leaving a clicked point in the 'highlight' state when you mouse away after clicking?
Here is my code. The pointClickCallback is getting the data through ajax and displaying it in another div. That part works. I just want to leave the point highlighted so I know which points I have clicked on.
I also need the point to revert back to normal when I click a second time. This is a toggle that allows me to select and unselect points.
EDIT: I found the interaction model example but when I add it to my code I lose my pointClickCallback functionality. I saw the call to captureCanvas and the interaction model structure.
var g = new Dygraph(document.getElementById('rothmangraph'), lines, {
//showRangeSelector: true,
title: "Personal Wellness Index (PWI)",
labels: ['Date', 'Index'],
color: ['#006699'],
valueRange: [0, 101],
axisLabelFontSize: 12,
drawPoints: true,
gridLineColor: "#aaaaaa",
includeZero: true,
strokeWidth: 2,
rightGap: 20,
pointSize: 4,
highlightCircleSize: 8,
series : {
Index: {
drawHighlightPointCallback : Dygraph.Circles.DIAMOND
},
},
axes: {
y: {
pixelsPerLabel: 20,
},
x: {
valueFormatter: function(ms) {
return ' ' + strftime('%m/%d/%Y %r',new Date(ms)) + ' ';
},
axisLabelWidth: 60,
axisLabelFormatter: function(d, gran) {
return strftime('%m/%d %I:%M %p',new Date(d.getTime())) ;
}
}
},
underlayCallback: function (canvas, area, g) {
var warning = g.toDomCoords(0,41);
var critical = g.toDomCoords(0,66);
// set background color
canvas.fillStyle = graphCol;
canvas.fillRect(area.x, area.y, area.w, area.h);
// critical threshold line
canvas.fillStyle = "#cc0000";
canvas.fillRect(area.x,warning[1],area.w,2);
// warning threshold line
canvas.fillStyle = "#cccc00";
canvas.fillRect(area.x,critical[1],area.w,2);
},
pointClickCallback: function(e,point) {
var idx = point.idx;
var line = lines[idx];
var sqltime = strftime('%Y-%m-%d %H:%M:%S',new Date(line[0]));
var dispdate = strftime('%m/%d %r',new Date(line[0]));
_secureAjax({
url: '/ajax/getDataPoint',
data: {'patient_id': pid, "rdate": sqltime},
success: function (result) {
// parse and add row to table if not exists.
var data = JSON.parse(result);
var aid = data['id'];
var indexCol = "#a9cced"
if (line[1] <= 65) indexCol = "#ede1b7";
if (line[1] <= 40) indexCol = "#e5bfcc";
var headerinfo = '<th class="'+aid+'"><span class="showindex" style="background-color:'+indexCol+'">'+line[1]+'</span></th>';
var fixdate = dispdate.replace(' ','<br>');
var headerdate = '<th class="'+aid+'">'+fixdate+'</th>';
// skip if already exists
var found = false;
var whichone = false;
$('#headerdate tr th').each(function(idx, item) {
if (fixdate == $(this).html()) {
found = true;
whichone = idx;
}
});
if (!found) {
$.each(data, function (idx, item) {
$('#' + idx).append('<td class="'+aid+'" style="width:70px">' + item + '</td>');
});
$('#headerdate tr').append(headerdate);
$('#headerinfo tr').append(headerinfo);
} else {
$('tr').each(function() {
$('.'+aid).remove();
});
}
}
});
}
});
}

syncfusion ej chart redraw [n] undefined

I am using ASP MVC and I have a ej chart built in script, and I need to refresh its content:
my chart is built like this:
var modelSummaryList = getMySummaryModel();
var summaryChartDataManager = ej.DataManager(($scope.FirstLoad && IsCurrentUserDpiUser()) ? null : modelSummaryList);
jQuery("#MySummaryChart").ejChart({
range: { min: summaryChartDataManager == null ? -1 : summaryChartDataManager.ChartMin, max: summaryChartDataManager == null ? 1 : summaryChartDataManager.ChartMax },
series: [{
name: "MTM",
tooltip: {
visible: true,
format: "#point.x#: #point.y# #series.name#"
},
dataSource: summaryChartDataManager,
xName: "CtpyShort",
yName: "MTM"
}, {
name: "Threshold",
tooltip: {
visible: true,
format: "#point.x#: #point.y# #series.name#"
},
dataSource: summaryChartDataManager,
xName: "CtpyShort",
yName: "Threshold"
}, {
name: "My Held/(Posted)",
tooltip: {
visible: true,
format: "#point.x#: #point.y# #series.name#"
},
dataSource: summaryChartDataManager,
xName: "CtpyShort",
yName: "Held"
}],
type: 'column',
});
which is working good, but upon filter and change of data, I need to redraw the chart so I did this:
$scope.RefreshChart = function (data) {
var chart = $("#MySummaryChart").ejChart("instance");
chart.model.range.min = data.MyTradeSummaryVM.ChartMin;
chart.model.range.max = data.MyTradeSummaryVM.ChartMax;
for (var s = 0; s <= chart.model.series.length - 1; s++) {
var pts = IsCurrentUserDpiUser() ? $.grep(data.MyTradeSummaryVM, function (item) {
return item.ClientID == localStorage.getItem("MyPosting_client_sticky");
}) : data.MyTradeSummaryVM;
chart.model.series[s].points = [];
for (var p = 0; p <= pts.length - 1; p++) {
var newPt = new Object();
newPt.x = pts[p].CtpyShort;
if (chart.model.series[s].name == "MTM")
newPt.y = pts[p].MTM;
else if (chart.model.series[s].name == "Threshold")
newPt.y = pts[p].Threshold;
else if (chart.model.series[s].name == "My Held/(Posted)")
newPt.y = pts[p].Held;
newPt.visible = true;
chart.model.series[s].points.push(newPt);
}
}
chart.redraw();
};
which throws the error TypeError: n[0] is undefined on the chart.redraw() line, and I am stumped. Please note that the RefreshChart() works if I build the chart via cshtml and not javascript like this:
#(Html.EJ().Chart("MySummaryChart")
.PrimaryXAxis(pr => pr.Title(tl => tl.Text("")))
.PrimaryYAxis(pr => pr.Range(ra => ra.Max(Model.ChartMax).Min(Model.ChartMin)).Title(tl => tl.Text("")))
.CommonSeriesOptions(cr => cr.Type(SeriesType.Column).EnableAnimation(true).Marker(mr => mr.DataLabel(dt => dt.Visible(true).EnableContrastColor(true)))
.Tooltip(tt => tt.Visible(true).Format("#point.x# : #point.y# #series.name# ")))
.Series(sr =>
{
.
.
.
})
.Load("loadTheme")
.IsResponsive(true)
.Size(sz => sz.Height("400"))
.Legend(lg => { lg.Visible(true).Position(LegendPosition.Bottom); }))
but I am opting to use JS coz I need to handle null and firstload(meaning no data should appear on the chart upon first load, and unless user picks a client(filter), no data should be loaded. Which I cant seem to make it work if via html, I couldn't erase the chart xaxisregions label along with other data, the only things erased are the main Y axis points but not labels.
We have analyzed the reported scenario. We would like to you know that, when the data for chart is bind using dataSource, then while refreshing the chart with new data, you need to bind the data to series.dataSource property not to series.points, so that only chart will refresh properly. We have prepared a sample with respect the above scenario. Find the code snippet to achieve this requirement.
<input type="button" id="refreshChart" onclick="refresh()" value="Refresh Chart" />
function refresh(sender) {
var chart = $("#MySummaryChart").ejChart("instance");
for (var s = 0; s <= chart.model.series.length - 1; s++) {
//Obtained random data,
var pts = GetData().data;
var newPt = [];
for (var p = 0; p <= pts.length - 1; p++) {
if (chart.model.series[s].name == "MTM")
newPt.push({ "CtpyShort": pts[p].CtpyShort, "MTM": pts[p].MTM });
else if (chart.model.series[s].name == "Threshold")
newPt.push({ "CtpyShort": pts[p].CtpyShort, "Threshold": pts[p].Threshold });
else if (chart.model.series[s].name == "My Held/(Posted)")
newPt.push({ "CtpyShort": pts[p].CtpyShort, "Held": pts[p].Held });
}
//Assign the updated data to dataSource property
chart.model.series[s].dataSource = newPt;
}
chart.redraw();
}
In the above code in a button click event, we have refreshed the chart data. Here you need to assign the x value to CtpyShort and y values of three series to MTM, Threshold and Held, since we have mapped these properties while rendering the chart initially.
Screenshot before updating data:
Screenshot after updating data:
Sample for reference can be find from below link.
Sample Link
If you face still any concern, kindly revert us by modifying the above sample with respect to your scenario or provide your sample with replication steps which will helpful in further analysis and provide you the solution sooner.
Thanks,
Dharani.

Chartjs: display different average line while grouping

Currently I'm working on chartjs and I found that is extremely fast to learn(at least for normal task).Currently I'm facing a problem: I was asked to display a grouped bar chart. like in figure.
grouped bar char
as you can see for the date 30-08-2016 there will be 3 distinct values for B,C,D and for 31-09-2016 same group of data but with different values.
I was asked also to add an average line for each different group in the chart
to look like this:
grouped bar chart with averages
I need to bind the start of one average line with the associated bar group.
I serached on internet but i couldn't find any example.Can you tell me if there is an example or give some suggestion? thanks in advance
this was my solution: i created a plugin and than tried to draw a line for each component of the chart. the code is quite messy (i'll try to clean)
// Define a plugin to provide average for different groups of data
Chart.plugins.register({
afterDatasetsDraw: function(chartInstance, easing) {
// To only draw at the end of animation, check for easing === 1
{
var ctx = chartInstance.chart.ctx;
var mapAverageLinePoints = {};
chartInstance.data.datasets.forEach(function (dataset, i) {
var meta = chartInstance.getDatasetMeta(i);
if (!meta.hidden) {
meta.data.forEach(function(element, index) {
var dataString = dataset.label;
var groupAverageLine = mapAverageLinePoints[dataString];
if(groupAverageLine==null)
{
groupAverageLine = [];
}
//store the point coordinate and the value
var linePoint =
{
x : position.x,
y : position.y,
value: dataset.data[index],
avg : 0
}
//adding the point to the array going to be stored in the map that group the point by the label
groupAverageLine.push(linePoint);
mapAverageLinePoints[dataString]=groupAverageLine;
}
);
}
});
for (var type in mapAverageLinePoints) {
var avgLinePoints = mapAverageLinePoints[type];
//NON E' in valore bensì rispecchia la sommatoria dei posY utilizzati nella rappresentazione
var totalYAxis=0;
var totale=0;
var labelNumero=0;
for(var k=0;k<avgLinePoints.length;k++)
{
var point = avgLinePoints[k];
totalYAxis+=point.y;
totale+=point.value;
//jump the first one
if(k>=1)
{
var prevPoint = avgLinePoints[k-1];
//k start from 0!!!!
var avgYAxis = (totalYAxis/(k+1));
var avg = (totale/(k+1));
// here i draw the line starting from the previous average
ctx.beginPath();
ctx.moveTo(point.x, avgYAxis);
ctx.strokeStyle = '#979797';
ctx.lineTo((prevPoint.x), prevPoint.avg);
ctx.stroke();
point.avg=avgYAxis;
//this one is for drawing a "o" where two segments collide
var fontSize = 12;
var fontStyle = 'normal';
var fontFamily = 'Helvetica Neue';
ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
ctx.fillText("avg: "+(avg/range).toLocaleString() + rangeSuffix + ' €', point.x, point.y+(point.value>0?-30:+10));
}
else{
//for the first one only record the y as the avg
point.avg=point.y;
}
var fontSize = 10;
var fontStyle = 'normal';
var fontFamily = 'Helvetica Neue';
ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
ctx.fillText("o", point.x, point.avg);
}
labelNumero=labelNumero+1;
}
}
}
});
the result is this one:
chart result

iTextSharp - Adding content to a cell later in the code

I am using iTextSharp to generate an invoice PDF. For that I wish to display order number and total amount at top of PDF. However the actual amount is calculated during product displaying part.
To make it simple the report follows this structure
===========order number=========
===========total amount=========
//here I display the products and so total amount is calculated here.
==========end of report=========
To achieve this I am creating a cell for total amount like this
PdfPCell cell = new PdfPCell();
PdfPCell headingAmountCell = cell;
//add some components to cell, reinitialize them again with new cells etc.
//but still have first cell reference in headingAmountCell
//calculate total amount here.
.
.
.
.
headerAmountCell.AddElement(new Phrase("Total Amount:" + totalAmount.ToString());
.
.
.
However this doesn't work.
I can see order number at top but not total amount.
If you could please help, that'll be great.
Thanks in anticipation.
Regards
Anshul
UPDATE:
Here is a small portion of code to help understand the problem
//header
var headerTable = new PdfPTable(logoExists ? 2 : 1);
headerTable.WidthPercentage = 100f;
if (logoExists)
headerTable.SetWidths(new[] { 50, 50 });
float padding = 5f;
//logo
if (logoExists)
{
var logoFilePath = _pictureService.GetPictureUrl(logoPicture, 0, false);
var cellLogo = new PdfPCell(Image.GetInstance(logoFilePath));
cellLogo.Border = Rectangle.BOTTOM_BORDER;
cellLogo.BorderColorBottom = BaseColor.GRAY;
cellLogo.PaddingBottom = padding;
cellLogo.VerticalAlignment = Rectangle.ALIGN_TOP;
headerTable.AddCell(cellLogo);
}
//store info
var cell = new PdfPCell();
cell.Border = Rectangle.BOTTOM_BORDER;
cell.BorderColorBottom = BaseColor.GRAY;
cell.PaddingBottom = padding;
cell.HorizontalAlignment = Element.ALIGN_RIGHT;
cell.VerticalAlignment = Element.ALIGN_TOP;
//payment method
var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(order.PaymentMethodSystemName);
string paymentMethodStr = paymentMethod != null ? paymentMethod.GetLocalizedFriendlyName(_localizationService, lang.Id) : order.PaymentMethodSystemName;
PdfPCell headingAmountCell = cell;
if (!paymentMethodStr.Contains("COD") && order.PaymentStatus == global::Nop.Core.Domain.Payments.PaymentStatus.Paid)
{
cell.AddElement(new Paragraph("PREPAID", bigFont));
}
else if (paymentMethodStr.Contains("COD") && order.PaymentStatus == global::Nop.Core.Domain.Payments.PaymentStatus.Paid)
{
cell.AddElement(new Phrase("PAYMENT COLLECTED ON DELIVERY", bigFont));
}
else
{
cell.AddElement(new Paragraph("COLLECT ON DELIVERY", bigFont));
}
string order_number = order.GetOrderNumber();
cell.AddElement(new Paragraph(String.Format(_localizationService.GetResource("PDFInvoice.Order#", lang.Id), order_number), bigFont));
var store = _storeService.GetStoreById(order.StoreId) ?? _storeContext.CurrentStore;
cell.AddElement(new Paragraph(String.Format("Order Date: {0}", _dateTimeHelper.ConvertToUserTime(order.CreatedOnUtc, DateTimeKind.Utc).ToString("D", new CultureInfo(lang.LanguageCulture))), font));
headerTable.AddCell(cell);
doc.Add(headerTable);
//THEN I PERFORM SOME PRODUCT DISPLAY PART WHERE I CALCULATE THE ORDERTOTAL THEN AT THE BOTTOM
//.
//.
..
headingAmountCell.AddElement(new Phrase(Math.Round(orderTotal) + "<-AMOUNT TO COLLECT")));