Matching a date on a calendar to another field - date

Ok, I have a calendar on my page and each day is a formatted version of that date, eg.
Jan 2021
1 2 3 4
5 6 7 8
etc..
so the 1 cell will contain 1/1/2021 (but formatted to just show '1' [d])
I also have a cell (K5) on that page for a user to enter a date eg:[1/1/2021]
What i'd like is a script that changes the border colour of the day in the calendar if it matches the user entry cell.
The code:
function onEdit(e) {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var date = sheet.getRange("K5").getValue();
if (e.range.getValue()== date) {
e.range.setBorder(true, true, true, true, true, true, "red", SpreadsheetApp.BorderStyle.solid); }
};
This doesn't work and I can't think of a way of having the code work for every cell of the calendar (there are 2 years worth so over 1000 cells).
It doesn't need to be onEdit, i was just testing to see if the actual setBorder function worked (which it does)
Also, I cant use conditional formatting as I've already used that to change to bg and font colours for something else (and there's no option to change boarder colour anyway)
I've made a mini version with just Jan if you'd like to have a look and see what you can do:
https://docs.google.com/spreadsheets/d/1oV4lE8cQB-e2bVc_HgiGM31ivk3uHxPcqsSdLdCxsmQ/edit?usp=sharing

One alternative to looking for all the dates would be to use a formula to show the dates and their addresses in a different sheet. This formula will output a series of dates and their addresses for your holidays. It can then be read with an onEdit() script to put borders on the correct addresses:
Formula:
=ARRAYFORMULA(IFERROR(QUERY(SPLIT(FLATTEN(N(Calendar!E9:AY)&"|"&ROW(Calendar!E9:AY)&"|"&COLUMN(Calendar!E9:AY)&"|"&LEN(TO_TEXT(Calendar!E9:AY))),"|",0,0),"select Col1,Col2,Col3 where Col4<3 and ("&TEXTJOIN(" or",TRUE," Col1="&FILTER(Calendar!BE28:BE100,Calendar!BE28:BE100<>""))&")")))
Script:
function onEdit(e){
holidayBorders(e);
}
function holidayBorders(e){
var sheet = e.range.getSheet();
if(e.value && sheet.getName()=='Calendar' && e.range.getColumn()==57 && e.range.getRow() >= 28){
SpreadsheetApp.flush();
Utilities.sleep(10000);
var datasheet = e.source.getSheetByName('DATA');
var rcs = datasheet.getRange('AQ3:AR').getValues().filter(e=> e[0]);
for (let i=0;i<rcs.length;i++){
sheet.getRange(rcs[i][0],rcs[i][1]).setBorder(true, true, true, true, true, true, "red", SpreadsheetApp.BorderStyle.solid);
}
}
}

You could do the following:
Get all values in your range, using getValues().
Iterate through all values in the resulting 2D array.
For each value, check if (1) the value is a Date (for example, using instanceof) and whether the two dates are from the same day (for example, doing this).
If both conditions are met, set the border.
Code snippet:
function setBorders() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var date = sheet.getRange("K5").getValue();
var firstRow = 7;
var firstCol = 5;
var data = sheet.getRange(firstRow, firstCol, sheet.getLastRow() - firstRow + 1, sheet.getLastColumn() - firstCol + 1).getValues();
data.forEach((row, i) => {
row.forEach((value, j) => {
if (value instanceof Date && sameDay(date, value)) {
sheet.getRange(i + firstRow, j + firstCol).setBorder(true, true, true, true, true, true, "red", SpreadsheetApp.BorderStyle.solid);
}
});
})
}
function sameDay(date1, date2) {
return date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate();
}

Related

How to set default value as a populated 3D array and still define the objects within it?

I want to make a 3d array that will represent years, months and days:
[years[months[days]]]
And so I made a simple function that does this perfectly, for each day I am populating a default object:
function generateEmpty3dArray() {
// i did that on purpuse with [[]], im doing it just for 1 year atm,
// thats why i am using index 0
const array = [[]];
const months = 12;
for(let month = 0; month < months; month++) {
const monthDays = new Date(2021, month + 1, 0).getDate();
array[0].push([]);
for(let day = 0; day < monthDays; day++) {
array[0][month].push({shift: "X"})
}
}
console.log(array)
}
The array that is generated is exactly what I want, my issue is in defining the Person schema:
const personSchema = new mongoose.Schema({
schedule: {
type: Array,
default: generateEmpty3dArray() //????
}
})
I dont know how to populate the default field, what I want to happen is, whenever a new Person is created into the database, I want him to receive that empty 3d array, so that I can then work with it and set different shifts.
The problem is that, I have no idea how to both specify what the specific value of each day should look like [[[{shift: {type: String, enum: shifts}}]]] and at the same time, create the default array for the whole thing.
This is what worked:
schedule: {
type: [
[
[
{
shift: { type: String, required: true, enum: shifts },
},
],
],
],
default: generateEmpty3dArray(),
},

Free jqGrid restoring date with custom formatter after cancelling inline edit

After inline edit is cancelled, the date column comes back as undefined instead of restoring the original value. Column is defined as following (dates are coming in 1970-01-01 format):
{name:'Release<br>Date',index:'Street_Date', sorttype:"date", width:70,
formatter: function (cellvalue, options, rowObject) {
return cellvalue === ('1970-01-01') ? "" : $.fn.fmatter.call(this, "date", cellvalue, options, rowObject);
},
formatoptions: {newformat:'d M y'},
editable:true,
editoptions: {
size:9,
dataInit: function(el, options) {
$(el).datepicker({
dateFormat: "d M y",
defaultDate: '01 Jan 70',
onSelect: function(dateText, inst) {
}
});
}
},
searchoptions: {
sopt: ['eq','ne','ge','le'],
dataInit: function (elem) {
$(elem).datepicker({ showButtonPanel: true, dateFormat: 'yy-mm-dd' })
}
}
},
The inline edit is setup as following:
ondblClickRow: function (rowid) {
var savedRows = $grid.jqGrid("getGridParam", "savedRow");
if (savedRows.length > 0 && savedRows[0].id !== rowid) {
// cancel editing
$grid.jqGrid("restoreRow", savedRows[0].id);
}
if (savedRows.length === 0) {
$grid.jqGrid("editRow", rowid, editOptions);
}
}
When Grid is loaded, the date shown like 07 Aug 18, entering the inline editing by double click, the date is still 07 Aug 18. After cancelling the edit either by clicking away or clicking Cancel button, date becomes NaN undefined N. After refresh, it comes back correctly though.
How to preserve the correct date after cancelling editing?
Grid behaves correctly with formatter: date
free jqGrid v jqGrid 4.13.5
Maybe the author of free-jqGrid will help better, but I would recommend you to add additional parameter (action='edit') when the formatter is called. Code below:
formatter: function (cellvalue, options, rowObject) {
return cellvalue === ('1970-01-01') ? "" : $.fn.fmatter.call(this, "date", cellvalue, options, rowObject, "edit");
},
Note the last parameter in $.fn.fmatter.call
UPDATE
This is working in my tests.
Since you use a custom date fomatter it is needed the value in savedRows to be unformated in order to be saved correct. In case of default formatter = date this is done automatically.
Below is the code that can be used, suppose you know the index of the field in colModel:
ondblClickRow: function (rowid) {
var savedRows = $grid.jqGrid("getGridParam", "savedRow");
if (savedRows.length > 0 && savedRows[0].id !== rowid) {
// cancel editing
savedRows[0].Release_Date = $.unformat.date.call($grid[0], savedRows[0].Release_Date, $grid[0].p.colModel[1]);
$grid.jqGrid("restoreRow", savedRows[0].id);
}
if (savedRows.length === 0) {
$grid.jqGrid("editRow", rowid, editOptions);
}
}

Ag-grid: Count the number of rows for each filter choice

In my ag-grid I want to display counts of rows next to each filter choice in a set filter, and maybe sort choices by that count (descending).
This is what it looks like by default:
I want the choices to be displayed as
Select All (88)
Katie Taylor (2)
Darren Sutherland (1)
John Joe Nevin (1)
Barack Obama (0)
...
What is the most efficient way to get those counts (and maybe sort the choices accordingly) from the row data, taking into account filters already set in the other fields (if any)?
Assuming your columns field is "name", you could try building up a map and refer to this in the filters cellRenderer:
var nameValueToCount = {};
function updateNameValueCounts() {
nameValueToCount = {};
gridOptions.api.forEachNodeAfterFilter((node) => {
if(!nameValueToCount.hasOwnProperty(node.data.name)) {
nameValueToCount[node.data.name] = 1;
} else {
nameValueToCount[node.data.name] = nameValueToCount[node.data.name] + 1;
}
});
}
And your column def would look like this:
{
headerName: "Name",
field: "name",
width: 120,
filter: 'set',
filterParams: {
cellRenderer: NameFilterCellRenderer
}
},
And finally, the NameFilterCellRenderer would look like this:
function NameFilterCellRenderer() {
}
NameFilterCellRenderer.prototype.init = function (params) {
this.value = params.value;
this.eGui = document.createElement('span');
this.eGui.appendChild(document.createTextNode(this.value + " (" + nameValueToCount[params.value] + ")"));
};
NameFilterCellRenderer.prototype.getGui = function () {
return this.eGui;
};
You would need to ensure that you called updateCountryCounts to update it when data changed (either with new/updated data, or when a filter was updated etc), but this should work for your usecase I think

Date Filter in sapui5

I have used two date controls to filter a row repeater as,
oF_cell5 = new sap.ui.commons.layout.MatrixLayoutCell({id:"F05",colSpan : 2});
var oCreateFrom = new sap.ui.commons.DatePicker("EV_AE_DATE1",
{width:"150px",placeholder:"Created From",
change:function(oEvent){
oController.onChangeFilterValue(oEvent);}
})
oF_cell51 = new sap.ui.commons.layout.MatrixLayoutCell({id:"F051",colSpan : 2});
var oCreateTill = new sap.ui.commons.DatePicker("EV_AE_DATE2",
{width:"150px",placeholder:"Created Till",
change:function(oEvent){
oController.onChangeFilterValue(oEvent);}
});
Now i have a rowrepeater in which one of the column is CreatedOn date like..,,
new sap.m.HBox({
items:[new sap.ui.commons.TextView({text:"Created on:"}),
new sap.ui.commons.TextView("TV11")
.bindProperty("text",
{
path: "CM_EventList>CREATEDON",
type: new sap.ui.model.type.Date({pattern:"MMM dd, yyyy",
source : {pattern : "dd.MM.yyyy"}})
})]
}),
And in the controller i have written this code as....,,
onInit: function() {
var model = new sap.ui.model.json.JSONModel("eventlist.json");
model.setData();
sap.ui.getCore().setModel(model,"CM_EventList");
},
onChangeCmFilterValue : function(oEvent){
var CM_FDATEVAL = sap.ui.getCore().byId("EV_AE_DATE1").getValue();
var CM_TDATEVAL = sap.ui.getCore().byId("EV_AE_DATE2").getValue();
var CM_Date = new sap.ui.model.Filter('CM_EventList>CREATEDON',
sap.ui.model.FilterOperator.BT,CM_FDATEVAL,CM_TDATEVAL);
var oCM_VBOX1 = sap.ui.getCore().byId("EV_CM_VBOX");
var oCM_RR1 = sap.ui.getCore().byId("EV_AE_ROWREPEATER");
oCM_RR1.bindRows('CM_EventList>/eventlist',oCM_VBOX1,null,[CM_Date]);
},
And the eventlist is my seperate json file which has date values as
{
"eventlist": [
{
"CREATEDON": "10.07.2014",
},
{
"CREATEDON": "10.08.2014",
},
.......
and so on..........
Now if select a date range from my date controls then the row repeater should show the records which are between the range of dates as in my json.
But the filter is not working.
Please Help me on this.
Thanks
Sathish
First of all, use the DatePicker Control for date fields in your view if you aren't using it already.
You can obtain the value of your date picker as a Date object using the method GetDateValue(). You can then use these date objects to create a filter for a datetime field of your data model.
var dateFrom = this.getView().byId("filterDateFrom").getDateValue();
var dateTo = this.getView().byId("filterDateTo").getDateValue();
if (dateFrom != null && dateTo != null) {
filter = new sap.ui.model.Filter(
"CM_EventList>CREATEDON",
sap.ui.model.FilterOperator.BT,
dateFrom,
dateTo
);
}
By the way: Note that both date objects will actually represent the moment at the beginning of the day (0:00:00) while the timestamps in your database will often be some point in time throughout the day. So when you want to search between two dates inclusively, you need to add one day to dateTo:
dateTo.setDate(dateTo.getDate() + 1);
Another problem you might or might not have to deal with are timezones... and of course all the other falsehoods programmers believe about time.
I think you should check the value of the following. The format should be different than your json value "CREATEDON": "10.08.2014".
var CM_FDATEVAL = sap.ui.getCore().byId("EV_AE_DATE1").getValue();
var CM_TDATEVAL = sap.ui.getCore().byId("EV_AE_DATE2").getValue();
Please try create a DatePicker with:
type: new sap.ui.model.type.Date({pattern: ""yyyy-MM-dd""})
Edit: to use Date as filter
var CM_FDATEVAL_DATE = new Date(sap.ui.getCore().byId("EV_AE_DATE1").getValue());
var CM_TDATEVAL_DATE = new Date(sap.ui.getCore().byId("EV_AE_DATE2").getValue());
Regards,
Allen

Filter by date range

In Ember it is easy to filter an array where you are looking for matching values ( Only return name == "John) What I can't figure out is how to filter with a greater than or less than ( Return all objects whose startDate is before today
In my app I have a collection of deliverables. I want to divide these deliverables into three categories: Due within ten days, Past due, and then the rest.
I found the following example in another SO post, but can't figure out how to use it to accomplish my goal
filterComputed: function() {
return this.get('content').filter(function(item, index, enumerable){
return item.firstName == 'Luke';
});
}.property('content.#each')
You can just do:
this.get('content').filter(function(item){
return item.get('someProperty') > someVar;
});
This should return an array of objects within your defined date range. Should work in Ember ^2.x.
filterComputed: computed('content.#each', 'startDate', 'endDate', function() {
return this.get('content').filter(function(item) {
var contentDate = item.get('date'); // expecting item to have a date property
return contentDate > this.get('startDate') && bookingDate < this.get('endDate');
});
})
With ES6 you could even do something like this:
filterComputed: computed('content.#each', 'startDate', 'endDate', function() {
return this.get('content').filter(item => item.get('date') > this.get('startDate') && item.get('date') < this.get('endDate'));
})
If you have a simpler requirement, computed.filterBy() might be right for you. https://emberjs.com/api/classes/Ember.computed.html#method_filterBy
Also helpful: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter