Creating OBX segments from multiple DB rows - mirth

I have a DB source which I am transforming into HL7. In the transform, I have a step to connect to the database and retrieve rows for OBX segments, there can be no rows or multiple rows.
I'm successfully getting all the values, but I'm having trouble getting them written into OBX segments. They're all the same, and all the last row retrieved.
Database values:
OBX3 OBX5
Test123 This is a new referral
Test456 Person
Test789 Anxiety
The result I'm getting in the message is:
OBX|0||Test789||Anxiety
OBX|1||Test789||Anxiety
OBX|2||Test789||Anxiety
Code:
var erefID = msg['erefid'].toString();
var dbConn = DatabaseConnectionFactory.createDatabaseConnection(driver,address,username,password);
var sql = "SELECT OBX3,OBX5 from table where column =" + erefID;
var results = dbConn.executeCachedQuery(sql);
var resultSize = results.size();
logger.info('query results ' + results);
logger.info('result size ' +resultSize);
var obx3 = "";
var obx5 = "";
while(results.next()){
var i=0
obx3 = results.getString(1);
logger.info('obx3 ' + obx3);
obx5 = results.getString(2);
logger.info('obx5 '+obx5);
while(i<resultSize)
{
createSegment('OBX', tmp,i);
tmp['OBX'][i]['OBX.1']['OBX.1.1'] = i;
tmp['OBX'][i]['OBX.3']['OBX.3.1'] = obx3;
tmp['OBX'][i]['OBX.5']['OBX.5.1'] = obx5;
i++;
}
}
dbConn.close();

Switched around the while statements, works now
while(i<resultSize){
var i=0
obx3 = results.getString(1);
logger.info('obx3 ' + obx3);
obx5 = results.getString(2);
logger.info('obx5 '+obx5);
while(results.next()){
createSegment('OBX', tmp,i);
tmp['OBX'][i]['OBX.1']['OBX.1.1'] = i;
tmp['OBX'][i]['OBX.3']['OBX.3.1'] = obx3;
tmp['OBX'][i]['OBX.5']['OBX.5.1'] = obx5;
i++;
}
}

Related

BIRT - creating a temporary table

I am building a BIRT report based on a dynamic table. I have a function that needs to return a table with variable number of columns of different types, so we have decided that this function will create a temporary table and just return its name.
So in the BIRT beforeFactory I am running the query that calls this function and then I am trying to read from the table of returned name and create a report table dynamically based on the number of columns and types. Two questions:
1. Is there a better way to return a table with variable number of columns? (this cannot be all the possible columns that I later filter as that would exceed maximum number of allowed columns)?
2. How to make Birt to see my temporary table? Here is the code, that I run in beforeFactory. Apparenty the table does not exists when calling second query.
importPackage(Packages.java.lang);
importPackage(Packages.org.eclipse.birt.report.data.adapter);
importPackage(Packages.org.eclipse.birt.report.data.adapter.api);
importPackage(Packages.org.eclipse.birt.report.data.adapter.impl);
importPackage(Packages.org.eclipse.birt.report.model.api);
importPackage(Packages.org.eclipse.birt.data.engine.api.querydefn);
//Get Data Source
var dataSource =
reportContext.getDesignHandle().getDesign().findDataSource("mydb");
//Create Data Set for data table name
var elementFactory =
reportContext.getReportRunnable().designHandle.getElementFactory();
var dataSet = elementFactory.newOdaDataSet("tableName",
"org.eclipse.birt.report.data.oda.jdbc.JdbcSelectDataSet");
dataSet.setDataSource(dataSource.getName());
dataSet.setQueryText("select table_name from my_export_function('str1',
'str2');");
reportContext.getDesignHandle().getDataSets( ).add(dataSet);
//Create Data Session
var myconfig =
reportContext.getReportRunnable().getReportEngine().getConfig();
var des = DataRequestSession.newSession(myconfig, new DataSessionContext(3));
var dset = reportContext.getDesignHandle().findDataSet("tableName");
des.defineDataSource(des.getModelAdaptor()
.adaptDataSource(reportContext.getDesig nHandle()
.findDataSource("mydb")));
des.defineDataSet(des.getModelAdaptor()
.adaptDataSet(reportContext.getDesignHandle()
.findDataSet("tableName")));
//Query Definition
queryDefinition = new QueryDefinition();
queryDefinition.setDataSetName(dataSet.getName());
queryDefinition.setAutoBinding(true);
var pq = des.prepare(queryDefinition);
var qr = pq.execute(null);
var ri = qr.getResultIterator();
var tableName = "";
while (ri.next()) {
tableName = ri.getString("table_name");
}
var dataSet2 = elementFactory.newOdaDataSet("reportData",
"org.eclipse.birt.report.data.oda.jdbc.JdbcSelectDataSet");
dataSet2.setDataSource(dataSource.getName());
dataSet2.setQueryText("select * from " + tableName + ";");
System.out.println("TTTT: " + dataSet2.getQueryText());
reportContext.getDesignHandle().getDataSets( ).add(dataSet2);
//Query Definition
queryDefinition2 = new QueryDefinition();
queryDefinition2.setDataSetName(dataSet2.getName());
queryDefinition2.setAutoBinding(true);
var pq2 = des.prepare(queryDefinition2);
var qr2 = pq2.execute(null);
var ri2 = qr2.getResultIterator( );
var cc = ri2.getResultMetaData().getColumnCount();
System.out.println("col_01_name: " +
ri2.getResultMetaData().getColumnLabel(1));
System.out.println("Count: " + cc);
while (ri2.next()) {
System.out.println("Table: "+ ri2);
System.out.println("col_01: "+ ri2.getValue("col_01"));
}
ri.close();
qr.close();
ri2.close();
qr2.close();
des.close();
I managed to make the code work. Following lines solved the problem with second query:
var des2 = DataRequestSession.newSession(myconfig, new DataSessionContext(3));
des2.defineDataSource(des2.getModelAdaptor()
.adaptDataSource(reportContext.getDesig nHandle()
.findDataSource("mydb")));
des2.defineDataSet(des2.getModelAdaptor()
.adaptDataSet(reportContext.getDesignHandle()
.findDataSet("tableName")));
and then:
var pq2 = des2.prepare(queryDefinition2);
Also we have changed the function to return the query instead of creating a temporary table.

Corrupt records from OpenXML Spreadsheet creation

I'm trying to generate a simple XLSX file using OpenXML but I'm getting an error when I open my file and the only info in the repairedRecord part of the log file is this:
Repaired Records: Cell information from /xl/worksheets/sheet1.xml part
The strange thing is that all the cells I'm trying to write do have the value I expect them to have. I'm just trying to write a single header row right now, where the headers is just an IEnumerable<string>:
using (var doc = SpreadsheetDocument.Create(filename, SpreadsheetDocumentType.Workbook)) {
var workbookPart = doc.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
var worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet();
var sheets = workbookPart.Workbook.AppendChild(new Sheets());
var sheet = new Sheet {
Id = workbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Sheet 1"
};
sheets.Append(sheet);
workbookPart.Workbook.Save();
var sheetData = worksheetPart.Worksheet.AppendChild(new SheetData());
var row = new Row { RowIndex = 1 };
var column = 1;
foreach (var header in headers)
row.AppendChild(new Cell {
CellReference = GetColumnLetter(column++) + "1",
DataType = CellValues.SharedString,
CellValue = new CellValue(header)
});
sheetData.Append(row);
workbookPart.Workbook.Save();
}
If you're inserting a string value, you should be using CellValues.InlineString
foreach (var header in headers)
row.AppendChild(new Cell (new InlineString(new Text(header))) {
CellReference = GetColumnLetter(column++) + "1",
DataType = CellValues.InlineString
});

Nesting Model Reports + Search Results

Seem like I'm not the first to investigate this. But not despairing yet :-)
The target state I'd like to reach is:
A recursion through Packages, where some of the nested Sub-Packages are glorified Linked Documents, and some are Grids rendering Search Results.
I like that Model Documents provide Search Tag values -- but I can't seem to nest the grids where I want them to show up.
Approach 1: Nested Model Documents
If I could nest Model Document elements, with their Search/Search Term tags, I would be sorted. I could have
Context:Package
Introduction:Package <- just renders a Linked Document attached to it
Systems Affected:Package <- just renders a Linked Document attached to it
Systems Affected:Model Document <- renders the results of a canned Search.
But EA appears to be designed to ignore the Model Document element.
Approach 2: SQL backed Template Fragment
I like the Model Document as it has Search/Search Term + all the Inclusion/Exclusion configuration options. But if I had to give that part up I am thinking of emulating as much as I can via SQL or script.
First attempt - using SQL and a nested set of Packages such as:
Context:Package
Introduction:Package <- just renders a Linked Document attached to it
Systems Affected:Package <- just renders a Linked Document attached to it
Systems Affected:Package<> <- renders the results of a SQL Search.
If the template uses a TemplateSelector it can spot the Package with a wellknown stereotype, and invoke a Template that is backed by SQL.
The SQL uses the Package's Keywords as the source of the Element type to search for.
SELECT
o.ea_guid AS [CLASSGUID],
o.Object_Type AS [CLASSTYPE],
o.PDATA5 AS [KEYWORDS],
o.Object_Type AS [Type],
o.Stereotype AS [Stereotype],
*
FROM t_object as O
WHERE O.Object_Type IN
(SELECT PDATA5 FROM t_object AS S where S.Object_ID = #OBJECTID#)
It works...barely.It's a bit hacky.
Searching off of Element Type is not going to be sufficient for production documents.
Approach 3: Script backed Template Fragment
If I can get a script running I suspect that I could leverage functionality that is already in the system.
Context:Package
Introduction:Package <- just renders a Linked Document attached to it
Systems Affected:Package <- just renders a Linked Document attached to it
Systems Affected:Package<> <- renders the results of a canned Search.
If the template uses a TemplateSelector it can spot the Package with a wellknown stereotype, and invoke a Template that is backed by a script.
The script I'm trying is:
var x = Repository.GetElementsByQuery("POC.Req", "Device");
return x;
But the report remains blank of those elements I need for the table.
Q: Does the returned Collection need transformation before it can be used?
Approach 4
I've heard that there is an approach to Render Document sections as PDF, and link to them as Linked Documents... Sounds Convoluted. Sounds like a wrong/magic approach.
Approach 5
Any other suggestions?
Thanks for the help.
Addendum
Just got approach 3 to work by converting the script results to xml before handing it back
The template's script now looks like
-- first pass ...I'll work on passing the ObjectId in a bit in order to get fancier.
ExecuteSearch("Simple", "Device")
and it invokes a script saved somewhere else as
!INC Local Scripts.EAConstants-JScript
/*
* Script Name:
* Author:
* Purpose:
* Date:
*/
function main()
{
return ExecuteSearch("Simple", "Device");
}
function ExecuteSearch(searchName, searchParam){
var x = Repository.GetElementsByQuery(searchName, searchParam);
//return x;
var xmlDOC = CreateReport(x);
var s = xmlDOC.xml;
Session.Output(s);
return s;
}
function CreateReport(entityCollection){
var xmlDOC = CreateXmlDOC();
var xmlRoot = AppendXmlChild(xmlDOC,"EADATA");
var xmlDataSet = AppendXmlChild(xmlDOC,"Dataset_0",xmlRoot);
var xmlData = AppendXmlChild(xmlDOC,"Data",xmlDataSet);
for(var i = 0;i<entityCollection.Count();i++){
var entity = entityCollection.GetAt(i);
var xmlRow = AppendXmlChild(xmlDOC,"Row",xmlData);
//AppendXmlChild(xmlDOC,"CLASSTYPE",xmlRow).Text = entity.Type;
AppendXmlChild(xmlDOC,"Guid",xmlRow).text = entity.ElementGUID;
AppendXmlChild(xmlDOC,"CLASSTYPE",xmlRow).text = entity.Type;
AppendXmlChild(xmlDOC,"CLASSGUID",xmlRow).text = entity.ElementGUID;
AppendXmlChild(xmlDOC,"Type",xmlRow).text = entity.Type;
AppendXmlChild(xmlDOC,"Stereotype",xmlRow).text = entity.Stereotype;
AppendXmlChild(xmlDOC,"Name",xmlRow).text = entity.Name;
AppendXmlChild(xmlDOC,"Object",xmlRow).text = entity.Name;
AppendXmlChild(xmlDOC,"Id",xmlRow).text = entity.ElementID;
AppendXmlChild(xmlDOC,"Scope",xmlRow).text = entity.Scope;
AppendXmlChild(xmlDOC,"Phase",xmlRow).text = entity.Phase;
AppendXmlChild(xmlDOC,"Status",xmlRow).text = entity.Status;
var noteElement = AppendXmlChild(xmlDOC,"Notes",xmlRow);//.text = entity.Notes;
noteElement.appendChild(xmlDOC.createCDATASection(entity.Notes));
AppendXmlChild(xmlDOC,"Keywords",xmlRow).text = entity.PDATA5;
}
return xmlDOC;
}
function CreateXmlDOC()
{
var xmlDOM;
try
{
xmlDOM = new ActiveXObject( "MSXML2.DOMDocument.4.0" );
}
catch(e)
{
xmlDOM = new ActiveXObject( "MSXML2.DOMDocument.6.0" );
}
xmlDOM.createProcessingInstruction("xml","version=\"1.0\"");
xmlDOM.validateOnParse = false;
xmlDOM.async = false;
return xmlDOM;
}
function AppendXmlChild(xmlDOM, xmlElementName, xmlParent, isCDATA){
if (!xmlParent){xmlParent = xmlDOM;}
var child = xmlDOM.createElement(xmlElementName);
xmlParent.appendChild(child);
return child;
}
main();
The script I used in the end is as follows. It now correctly investigates the element's tags for clues as how to proceed.
Hope it helps others.
!INC Local Scripts.EAConstants-JScript
/*
* Script Name:
* Author:
* Purpose:
* Date:
*/
//Only used for development
function devTest()
{
//With Child elements: {2255D8C8-F1BB-4069-BDAF-8B303D108C62}
//With SearchName: {919252E0-BDEB-4f26-A39F-C0E74382952A}
//With PackageGUID: {8543ED3B-EC39-4bf0-92C2-FD49A00C376B}
Session.Output ("DEVTEST");
var package = Repository.GetPackageByGuid("{8543ED3B-EC39-4bf0-92C2-FD49A00C376B}");
Session.Output("Package Name:" + package.Name);
Session.Output("Package Guid:" + package.PackageGUID);
Session.Output("Package Id:" + package.PackageID);
Session.Output("Package ElementId:" + package.Element.ElementID);
//Session.Output("Package Element Id:" + package.Element.ElementID);
//Use the Element associate to the Package, not the Package ID itself.
var packageElementId = package.Element.ElementID; //NOT: package.PackageID;
var xmlDoc=ExecuteSearch(packageElementId);
try {
Session.Output(xmlDoc.xml);
}catch (e){
Session.Output(e.message);
}
}
//Extracts from a given Package it's SearchName/SearchValue tags
//in order to do a search that mimicks a ModelDocument's way of
//generating a report.
function ExecuteSearch(elementId){
//Validation
if (!elementId){
Session.Output("Exiting: No elementId received.");
return;
}
var packageElement = Repository.GetElementByID(elementId);
if (!packageElement){
Session.Output("Exiting: No package with given elementId: " + elementId);
return;
}
try {
var xmlDOC = ExecuteSearch2(packageElement);
var xml = xmlDOC.xml;
return xml;
}catch (e){
Session.Output("ERROR: " + e.message);
}
return null;
}
function ExecuteSearch2(packageElement){
//Session.Output(packageElement.ElementGUID + ": '" + packageElement.Name + "' invoking ExecuteSearch(" + packageElement.ElementID + ")");
//Session.Output("Attribute 'Test': " + GetElementTaggedValue(packageElement,'Test'));
//Precendence is to search
//* Direct children,
//* by Package, recursively,
//* Package, single
//* Package
//First dibs: does this package have any direct elements?
//Get back to the package that is related to the Element before you count Elements:
var package = Repository.GetPackageByGuid(packageElement.ElementGUID);
var elementCollection = package.Elements;
if (elementCollection.Count()){
Session.Output("Package [" + packageElement.ElementGUID + "] has child Elements:"+ elementCollection.Count());
return CreateReportDoc(elementCollection);
}
//If package had no children, look at Attributes for reference to other package.
//At present, can't find an easy way to determine package Id from the EA GUI, so
//using the Guid.
var searchPackageGuid = GetElementTaggedValue(packageElement,'SearchPackageGUID');
if (!searchPackageGuid){
searchPackageGuid = GetElementTaggedValue(packageElement,'SearchPackageGuid');
}
if (searchPackageGuid){
//Session.Output("Package [" + packageElement.ElementGUID + "] has SearchPackageGuid:"+ searchPackageGuid);
return ExecuteSearchByPackageGuid(searchPackageGuid);
}
// //If I ever find a way to get a packageId:
var searchPackageId = GetElementTaggedValue(packageElement,'SearchPackageId');
if (searchPackageId){
//Session.Output("Package [" + packageElement.ElementGUID + "] has SearchPackageId:"+ searchPackageId);
return ExecuteSearchByPackageId(searchPackageId);
}
// //If searching by SQL:
var searchSQL = GetElementTaggedValue(packageElement,'SearchSQL');
if (searchSQL){
Session.Output("Package [" + packageElement.ElementGUID + "] has SearchSQL:"+ searchSQL);
return ExecuteSearchBySQL(searchSQL);
}
//Not pointing to a package, so maybe pointing to a canned search:
var searchName = GetElementTaggedValue(packageElement,'SearchName');
if (!searchName){
//Session.Output("No SearchName");
return;
}
var searchValue = GetElementTaggedValue(packageElement,'SearchValue');
//Session.Output("Package [" + packageElement.ElementGUID + "] has SearchName/Value:"+ searchName + "/" + searchValue);
return ExecuteSearchBySearchName(searchName, searchValue);
}
//Mimicks functionality of a ModelDocument that searches by canned SearchName/SearchValue.
function ExecuteSearchBySearchName(searchName, searchValue){
var elementCollection = Repository.GetElementsByQuery(searchName, searchValue);
//return x;
return CreateReportDoc(elementCollection);
}
function ExecuteSearchByPackageGuid(packageGuid){
var package = Repository.GetPackageByGuid(packageGuid);
return ExecuteSearch2(package.Element);
}
function ExecuteSearchBySQL(searchSQL){
var elementCollection = Repository.GetElementSet(searchSQL, 2);
}
function HOLD_ExecuteSearchBySet(idList){
var elementCollection = Repository.GetElementsSet(idList);
//return x;
return CreateReportDoc(elementCollection);
}
//Iterate through the elements and convert to an Xml Document
//suitable for use by a Script backed Template:
function CreateReportDoc(elementCollection){
var xmlDOC = CreateXmlDOC();
var xmlData = CreateXmlReport(xmlDOC);
for(var i = 0;i<elementCollection.Count();i++){
//For each Element, create a new row:
var xmlRow = AppendXmlChild(xmlData,"Row");
//And embed the specific element:
var element = elementCollection.GetAt(i);
CreateReportRow(xmlRow, element);
}
return xmlDOC;
}
function CreateReportRow(xmlRow, element){
//And attach child property elements.
//For hairy ones, add them as a CDATA.
//AppendXmlChild(xmlDOC,"CLASSTYPE",xmlRow).Text = element.Type;
AppendXmlChild(xmlRow,"Guid").text = element.ElementGUID;
AppendXmlChild(xmlRow,"CLASSTYPE").text = element.Type;
AppendXmlChild(xmlRow,"CLASSGUID").text = element.ElementGUID;
AppendXmlChild(xmlRow,"Type").text = element.Type;
AppendXmlChild(xmlRow,"Stereotype").text = element.Stereotype;
AppendXmlChild(xmlRow,"Name").text = element.Name;
AppendXmlChild(xmlRow,"Object").text = element.Name;
AppendXmlChild(xmlRow,"Id").text = element.ElementID;
AppendXmlChild(xmlRow,"Scope").text = element.Scope;
AppendXmlChild(xmlRow,"Phase").text = element.Phase;
AppendXmlChild(xmlRow,"Status").text = element.Status;
AppendXmlChild(xmlRow,"Keywords").text = element.PDATA5;
//Notes need wrapping as CDATA
var noteElement = AppendXmlChild(xmlRow,"Notes");//.text = entity.Notes;
noteElement.appendChild(xmlRow.ownerDocument.createCDATASection(element.Notes));
//Now get tags:
AppendXmlChild(xmlRow,"Tags.ID").text = GetElementTaggedValue(element,"ID");
AppendXmlChild(xmlRow,"Tags.Type").text = GetElementTaggedValue(element,"Type");
AppendXmlChild(xmlRow,"Tags.Category").text = GetElementTaggedValue(element,"Category");
AppendXmlChild(xmlRow,"Tags.Traceability").text = GetElementTaggedValue(element,"Traceability");
return xmlRow;
}
//helper function to create an empty xml document
function CreateXmlDOC()
{
var xmlDOM;
try
{
xmlDOM = new ActiveXObject( "MSXML2.DOMDocument.4.0" );
}
catch(e)
{
xmlDOM = new ActiveXObject( "MSXML2.DOMDocument.6.0" );
}
xmlDOM.createProcessingInstruction("xml","version=\"1.0\"");
xmlDOM.validateOnParse = false;
xmlDOM.async = false;
return xmlDOM;
}
//helper function to create the beginning of an xml document
//suitable to render the results of a search:
function CreateXmlReport(xmlDOC){
var xmlRoot = AppendXmlChild(xmlDOC,"EADATA");
var xmlDataSet = AppendXmlChild(xmlRoot,"Dataset_0");
var xmlData = AppendXmlChild(xmlDataSet,"Data");
return xmlData;
}
//helper function to attach a new child xml element to a parent xml element
function AppendXmlChild(xmlParent, xmlElementName, isCDATA){
var xmlDocument = xmlParent.ownerDocument;
if (!xmlDocument){xmlDocument = xmlParent}
var child = xmlDocument.createElement(xmlElementName);
xmlParent.appendChild(child);
return child;
}
//Gets an Element's tag. Eats exception if Tag does not exist.
function GetElementTaggedValue(element, tagName){
var tag;
try {
tag = element.TaggedValues.GetByName(tagName);
}
catch (e) {
}
if (!tag){return;}
var result = tag.Value;
return result;
}
function ConvertPackageIdToBranchId(packageId){
var package = Repository.GetPackageByID(objectId);
if (!package){return;}
var packages = [package];
packages.concat(ConvertPackageToBranch(package));
var result=[];
for(var i=0;i<packages.length;i++){
result.push(packages[i].PackageID);
}
return result;
}
function ConvertPackageToBranch(package){
var result = [];
for (var i=0;i<package.Packages.Count();i++){
var childPackage = package.Packages.GetAt(i);
result.push(childPackage);
result.concat(ConvertPackageToBranch(childPackage));
}
return result;
}
//devTest();

Google Apps Script: How to pull values from column A based on values in column E and send all values in one email?

I'm trying to create a script for a student attendance spreadsheet that will look in Column E for the string "X". For each instance of "X", the string from column A (the student name) will be added to the body of an email. I'm pretty new to JavaScript, although I have been studying the basics. I've done a lot of research and found some scripts I was able to modify to send an individual email for each instance of X in E. However, I have not been able to figure out how to combine that information into a single email.
Here's what I have so far:
function Email_ReminderNS() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("July_August"),
EMAIL_SENT = "EMAIL_SENT",
statusArray = sheet.getDataRange().getValues();
var class = statusArray[0][8],
status = "X",
email = "XXXX"
for (i=7;i < statusArray.length;i++){
var emailSent = statusArray[i][84];
if (status == statusArray[i][4] & emailSent != EMAIL_SENT) {
var student = statusArray[i][0];
var body = "This is a No-Show Report for " +student+ " from " + class;
var subject = "No-Show Report for " + student+ " from " + class;
MailApp.sendEmail(email,subject,body,{NoReply : true});
sheet.getRange(i+1, 85).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
}
}
I realize I'll probably need to move the sendEmail function to be outside the IF statement. I tried to create an array with the names and join those into a string and add it to the body of the email, but I've had no luck. It just ended up sending the last name instead of all of them.
If anyone has any suggestions for me I would be deeply grateful.
First set up variables to keep track of which student did not show up:
var students = [];
var student_rows = [];
Then, add student to these arrays when X is found:
if (status == statusArray[i][4] & emailSent != EMAIL_SENT) {
var student = statusArray[i][0];
students.push(student);
student_rows.push(i+1);
}
Then send the email with all student names combined (outside of the for loop like you said)
var body = "This is a No-Show Report for " + students.join(', ') + " from " + class;
var subject = "No-Show Report for " + students.join(', ') + " from " + class;
MailApp.sendEmail(email,subject,body,{NoReply : true});
Finally update the spreadsheet indicating which names were in that email:
for (var i=0; i<student_rows.length; i++) {
sheet.getRange(student_rows[i], 85).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
Here's the complete script:
function Email_ReminderNS() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("July_August"),
EMAIL_SENT = "EMAIL_SENT",
statusArray = sheet.getDataRange().getValues();
var class = statusArray[0][8],
status = "X",
email = "francis#bposolutions.com";
var students = [];
var student_rows = [];
for (i=7;i < statusArray.length;i++){
var emailSent = statusArray[i][84];
if (status == statusArray[i][4] & emailSent != EMAIL_SENT) {
var student = statusArray[i][0];
students.push(student);
student_rows.push(i+1);
}
}
var body = "This is a No-Show Report for " + students.join(', ') + " from " + class;
var subject = "No-Show Report for " + students.join(', ') + " from " + class;
MailApp.sendEmail(email,subject,body,{NoReply : true});
for (var i=0; i<student_rows.length; i++) {
sheet.getRange(student_rows[i], 85).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
}
There are probably many ways to implement a new version of your code, the other answer probably works but I think it can be improved (a bit).
First of all, you can get rid of the flush method that does nothing else than slowing down the function (it was originally used in the Google example to check the sent status row by row, it is useless when we send only one mail with all the data in it)
Secondly, it might be a good idea to use html format to get a better looking result.
And lastly, it is good practice to write back to the sheet using one setValues instead of multiple setValue() in a loop.
Here is a possible replacement code, you'll have to "tune" it to your needs to eventually improve the message format but the main structure is there and working.
function Email_ReminderNS() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("July_August"),
statusArray = sheet.getDataRange().getValues();
var email = Session.getActiveUser().getEmail(); //replace with the email you want, this value will send mails to you I used it for test.
var class = statusArray[0][8],
status = "X",
students = [];
for (var i=7;i < statusArray.length; i++){
var emailSent = statusArray[i][84];
if (status == statusArray[i][4] & emailSent != "EMAIL_SENT") {
students.push(statusArray[i][0]);
statusArray[i][84]="EMAIL_SENT";
}
}
var subject = "No-Show Report for " + students.length + " from " + class;
var textBody = "This is a No-Show Report for " +students.length+ " from " + class+"\n";
var HTMLBody = "<b>This is a No-Show Report for " +students.length+ " from " + class+"</b><br><br>"
+'<table style="background-color:lightblue;border-collapse:collapse;" border = 1 cellpadding = 5><th>Sent Mails</th><tr>';
for(var n in students){
HTMLBody += '<tr><td>'+n+'</td><td>'+statusArray[n][0]+'</td></tr>';
textBody += '\n'+n+' - '+statusArray[n][0];
}
HTMLBody+='</table><BR> kind regards.' ;
textBody+='\n\nKind regards';
Logger.log(HTMLBody);
Logger.log(textBody);
MailApp.sendEmail(email,subject,textBody,{'NoReply' : true, 'htmlBody' : HTMLBody});
sheet.getRange(1,1,statusArray.length,statusArray[0].length).setValues(statusArray);
}

How to search multi keywork in linq query

i have this code in homepage
CheckBox[] ch= new CheckBox[12];
ch[0] = ChkContextA;
ch[1]= ChkContextB;
ch[2]= ChkContextC;
ch[3]= ChkContextD;
ch[4]= ChkContextE;
ch[5]= ChkContextF;
ch[6]= ChkContextG;
ch[7]= ChkContextH;
ch[8]= ChkContextI;
ch[9]= ChkContextJ;
ch[10]= ChkContextK;
ch[11]= ChiContextL;
for (int i = 0; i < 11; i++)
if (ch[i].Checked) search += ch[i].Text + " ";
Response.Redirect("SearchEstate.aspx?content="+search);
and this code in SearchEstate
var content = Request.QueryString["content"];
RealEstateEntities db = new RealEstateEntities();
var query = from O in db.Owners
join E in db.Estates on O.OwnerID equals E.OwnerID
join P in db.Properties on E.PropertyID equals P.PropertyID
where P.Facilities.Contains(content)
select new
{
regdate = E.RegisterDate,
region = E.Region,
Estype = E.EstateType,
Fac = P.Facilities,
deal = P.DealType,
price = P.TotalCost,
img = E.Picture,
addrss = O.Address,
area = P.Area,
tel = P.TellNum,
bed = P.RoomNum,
park = P.ParikingNum
};
Repeater2.DataSource = query.OrderByDescending(x => x.regdate);
Repeater2.DataBind();
when user checked some checkbox "content" for example have this value:
SearchEstate.aspx?content=ContextB ContextE ContextJ
I Want search this values in Facility field in db
How can I do this? (Sorry for my bad English)
I have the feeling your are looking for something along the lines of this query:
var content = Request.QueryString["content"];
string[] contentArray = content.Split(' ');
//...
var query = //...
where P.Facilities.Any(f => contentArray.Contains(f.FacilityName))
//...
(or instead of FacilityName some other property of Facility)
But I am not sure.