I've created a simple script that reads through an xml file and posts the results to an SQL database. This works perfectly.
I've put a little if statement in the script to identify orders that have already been posted to SQL. Basically if the transactionID in the input array is higher than the highest transactionID on the SQL server it adds the row values to the output array.
It seems that I am missing a trick here because I am getting "TypeError: Cannot call method "getAttribute" of undefined. (line 18, file "Code")" when trying to compare the current xml row to the last transaction ID.
I've done some searching and whilst I can see people with similar problems the explanations don't make a whole lot of sense to me.
Anyway, here is the relevant part of the code. Note that this all works perfectly without the if() bit.
function getXML() {
var id = lastTransactionID();
var xmlSite = UrlFetchApp.fetch("https://api.eveonline.com/corp/WalletTransactions.xml.aspx?KeyID=1111&vCode=1111&accountKey=1001").getContentText();
var xmlDoc = XmlService.parse(xmlSite);
var root = xmlDoc.getRootElement();
var row = new Array();
row = root.getChild("result").getChild("rowset").getChildren("row");
var output = new Array();
var i = 0;
for (j=0;i<row.length;j++){
if(row[j].getAttribute("transactionID").getValue()>id){ //Produces: TypeError: Cannot call method "getAttribute" of undefined. (line 18, file "Code")
output[i] = new Array();
output[i][0] = row[j].getAttribute("transactionDateTime").getValue();
output[i][1] = row[j].getAttribute("transactionID").getValue();
output[i][2] = row[j].getAttribute("quantity").getValue();
output[i][3] = row[j].getAttribute("typeName").getValue();
output[i][4] = row[j].getAttribute("typeID").getValue();
output[i][5] = row[j].getAttribute("price").getValue();
output[i][6] = row[j].getAttribute("clientID").getValue();
output[i][7] = row[j].getAttribute("clientName").getValue();
output[i][8] = row[j].getAttribute("stationID").getValue();
output[i][9] = row[j].getAttribute("stationName").getValue();
output[i][10] = row[j].getAttribute("transactionType").getValue();
output[i][11] = row[j].getAttribute("transactionFor").getValue();
output[i][12] = row[j].getAttribute("journalTransactionID").getValue();
output[i][13] = row[j].getAttribute("clientTypeID").getValue();
i++;
}
}
insert(output,output.length);
}
I have seen my mistake and corrected.
Mistake was in the for loop.
for (j=0;i
Related
Need your help and expert guidance as I need my google sheet to send emails every time a condition becomes true in "K" column which is named "Subject" as a header in the tab name "Analysis". Whenever I run the below code. Similarly last 3 lines I get while running the code as errors. Please explain in a simple possible way and not too much technical
function sendEmail(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Analysis");
var range = ss.getRange("J2:J35");
range.clear();
var n = ss.getLastRow();
for (var i = 2;i<n+1; i++){
var emailRequired = ss.getRange(i,9).getValue();
var subject = ss.getRange(i,11).getvalue();
var message = ss.getRange(i,12).getvalue();
if (emailRequired=="YES"){
MailApp.sendEmail("ksm272364#gmail.com",subject,message);
ss.getRange(i,10).setvalue("YES");
}
}
}
1:21:51 PM Error
TypeError: ss.getRange(...).getvalue is not a function
sendEmail # Code.gs:8
While running this code, there is error while executing this line:
var invoiceSheet = newSSFile.getSheets()[0];'
"TypeError: Cannot find function getSheets in object Copy of Invoice Przykładowy. (line 69, file "Code")"
With this code I want:
Create a new spreadsheet and move it to proper folder [works]
Get a value from another spreadsheet and paste it in this new one [error]
Looked for answer for an hour without any result. Any idea what might cause this error?
function invoice() {
//Create copy SS + name
var ssTemp = SpreadsheetApp.openById("1Cr2W_4lNrHYRdXK-KDQ7UFJJ3Iagh-tGct8Ee5Y");
var newSS = ssTemp.copy("Copy of " + ssTemp.getName());
// Move to folder
var DestinyFolder = DriveApp.getFolderById("0B-y1OC8ChG2XRjRRbEJ");
var newSSFile = DriveApp.getFileById(newSS.getId());
DestinyFolder.addFile(newSSFile);
DriveApp.getRootFolder().removeFile(newSSFile);
// Modify details
// Invoice No
var klienciSS = SpreadsheetApp.openById("18B151VlJaVtDdQ9CcLrL3iwRAtWw2ZzZydproj");
var klienciSheet = klienciSS.getSheets()[0];
var klienciRange= klienciSheet.getRange('AB6');
var klienciValue = klienciRange.getValue();
var invoiceSheet = newSSFile.getSheets()[0];
var inboiceRange = invoiceSheet.getRange('F4');
newCellInvoice.setValue(klienciValue);
The problem is caused by the following line
var newSSFile = DriveApp.getFileById(newSS.getId());
It makes that newSSFile holds an instance of Class File instead of an instance of Class Spreadsheet
But the problematic line looks unnecessary. Replace the line that throws the error by
var invoiceSheet = newSS.getSheets()[0];
As per in this FileHelpers 3.1 example, you can automatically detect a CSV file format using the FileHelpers.Detection.SmartFormatDetector class.
But the example goes no further. How do you use this information to dynamically parse a CSV file? It must have something to do with the DelimitedFileEngine but I cannot see how.
Update:
I figured out a possible way but had to resort to using reflection (which does not feel right). Is there another/better way? Maybe using System.Dynamic? Anyway, here is the code I have so far, it ain't pretty but it works:
// follows on from smart detector example
FileHelpers.Detection.RecordFormatInfo lDetectedFormat = formats[0];
Type lDetectedClass = lDetectedFormat.ClassBuilderAsDelimited.CreateRecordClass();
List<FieldInfo> lFieldInfoList = new List<FieldInfo>(lDetectedFormat.ClassBuilderAsDelimited.FieldCount);
foreach (FileHelpers.Dynamic.DelimitedFieldBuilder lField in lDetectedFormat.ClassBuilderAsDelimited.Fields)
lFieldInfoList.Add(lDetectedClass.GetField(lField.FieldName));
FileHelperAsyncEngine lFileEngine = new FileHelperAsyncEngine(lDetectedClass);
int lRecNo = 0;
lFileEngine.BeginReadFile(cReadingsFile);
try
{
while (true)
{
object lRec = lFileEngine.ReadNext();
if (lRec == null)
break;
Trace.WriteLine("Record " + lRecNo);
lFieldInfoList.ForEach(f => Trace.WriteLine(" " + f.Name + " = " + f.GetValue(lRec)));
lRecNo++;
}
}
finally
{
lFileEngine.Close();
}
As I use the SmartFormatDetector to determine the exact format of the incoming Delimited files you can use following appoach:
private DelimitedClassBuilder GetFormat(string file)
{
var detector = new FileHelpers.Detection.SmartFormatDetector();
var format = detector.DetectFileFormat(file);
return format.First().ClassBuilderAsDelimited;
}
private List<T> ConvertFile2Objects<T>(string file, out DelimitedFileEngine engine)
{
var format = GetSeperator(file); // Get Here your FormatInfo
engine = new DelimitedFileEngine(typeof(T)); //define your DelimitdFileEngine
//set some Properties of the engine with what you need
engine.ErrorMode = ErrorMode.SaveAndContinue; //optional
engine.Options.Delimiter = format.Delimiter;
engine.Options.IgnoreFirstLines = format.IgnoreFirstLines;
engine.Options.IgnoreLastLines = format.IgnoreLastLines;
//process
var ret = engine.ReadFileAsList(file);
this.errorCount = engine.ErrorManager.ErrorCount;
var err = engine.ErrorManager.Errors;
engine.ErrorManager.SaveErrors("errors.out");
//return records do here what you need
return ret.Cast<T>().ToList();
}
This is an approach I use in a project, where I only know that I have to process Delimited files of multiple types.
Attention:
I noticed that with the files I recieved the SmartFormatDetector has a problem with tab delimiter. Maybe this should be considered.
Disclaimer: This code is not perfected but in a usable state. Modification and/or refactoring is adviced.
I use this code to replace text of bookmark in word :
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open("doc3.docx", true))
{
var bookmarkStarts = wordDoc.MainDocumentPart.Document.Body.Descendants<BookmarkStart>();
foreach (var start in bookmarkStarts)
{
OpenXmlElement elem = item.NextSibling();
while (elem != null && !(elem is BookmarkEnd))
{
OpenXmlElement nextElem = elem.NextSibling();
elem.Remove();
elem = nextElem;
}
item.Parent.InsertBefore<Run>(new Run(new Text("Hello")), item);
}
wordDoc.Close();
}
But this not work where the bookmark is in the table.
Have you checked that you don't delete any bookmarks with your approach?
I've run your test code after editing a little (you don't have a var name items in your example code), and I've succesfully inserted Hello in 2 bookmarks out of a table, and 2 in a table, without any issues.
Which leads me to believe your problem lies elsewhere.
Have you looked at the open-xml in your document after you've run your program?
Is there any errors?
I've experienced bookmarks being placed the oddest places in a word-document when you leave the placing to word, and not you.
You can also end up with bookmarks overlapping each other like this
<bookmark1 start><xml xml><bookmark2 start><bookmark1 end><xml xml><bookmark2 end>
If you run into that case, your code will delete the bookmarkstart 2 before it reaches bookmarkend 1, and that will cause your bookmark to not be replaced.
You'll easily run into that problem with larger complex documents.
The way I solved it was to "sort" the bookmarks before doing any editing.
So the example above would become
<bookmark1 start><xml xml><bookmark1 end><bookmark2 start><xml xml><bookmark2 end>
after the sort
The code I use to do this look like this:
var bookmarks = mainPart.Document.Body.Descendants<BookmarkStart>();
for (int i = 0; i < bookmarks.Count(); i++)
{
var bks = bookmarks.ElementAt(i);
var next = bks.NextSibling();
if (next is BookmarkEnd)
{
var bme = (BookmarkEnd)next;
if (int.Parse(bks.Id) - int.Parse(bme.Id) == 1)
{
var copy = (BookmarkEnd)next.Clone();
bks.Parent.RemoveChild<BookmarkEnd>(bme);
bks.Parent.InsertBefore<BookmarkEnd>(copy, bks);
}
}
}
Which i'll admit isn't totally fool-proof but have worked well for me.
Another check you can add, to avoid deleting bookmarks is in your replace method
This will make sure you don't delete bookmarkstarts as you remove elements when inserting text
while (elem != null && !(elem is BookmarkEnd)) //fjern elementer
{
OpenXmlElement nextElem = elem.NextSibling();
if (elem.LocalName != "bookmarkStart")
elem.Remove();
elem = nextElem;
}
Good luck :)
I'm trying to setup an output parameter using PetaPoco. I found someone using this sample online:
var ctx = new CustomDBDatabase();
var total = new SqlParameter("Total", System.Data.SqlDbType.Int);
total.Direction = System.Data.ParameterDirection.Output;
var results = ctx.Query<DBEntity>("exec GetDBEntities #StartIndex, #MaxIndex, #TotalCount = #Total out",
id, start, max, total);
int totalCount = (int)total.Value;
However, total.value returns null, even though when I run this statement directly against SQL Server, it returns me 3. Is this setup correctly with PetaPoco? Are output parameters supported?
Thanks.
This is supported. But your current syntax is wrong anyways.
var ctx = new CustomDBDatabase();
var total = new SqlParameter("TotalCount", System.Data.SqlDbType.Int);
total.Direction = System.Data.ParameterDirection.Output;
var results = ctx.Query<DBEntity>("exec GetDBEntities #StartIndex, #MaxIndex, #TotalCount OUTPUT", new { StartIndex = start, MaxIndex = max, TotalCount = total});
int totalCount = (int)total.Value;
Something like this should work though. Not quite sure of the sql syntax but this should get you on your way.