Parsing log files in a folder in ColdFusion - html

The problem is there is a folder ./log/ containing the files like:
jan2010.xml, feb2010.xml, mar2010.xml, jan2009.xml, feb2009.xml, mar2009.xml ...
each xml file would like:
<root><record name="bob" spend="20"></record>...(more records)</root>
I want to write a piece of ColdFusion code (log.cfm) that simply parsing those xml files. For the front end I would let user to choose a year, then the click submit button. All the content in that year will be show up in separate table by month. Each table shows the total money spent for each person. like:
person cost
bob 200
mike 300
Total 500
Thanks.

The short answer is that, if your XML is correctly formatted, you can use the XMLParse() function to get the XML into a CF data object.
Sergii pointed out that XMLParse cna take a path, so you can simply read the file directly into it and assign the results to a variable.
The data should look something like an array of structures. Use CFDUMP on your CF data object to view it and help you figure it out.

I would strongly urge you to check out Microsoft's "Log Parser" if you're on Windows. It provides essentially a SQL-like query interface to all manner of log files. There's an app called "Log Parser Lizard" which provides a GUI for testing out "queries", and then you can cfexecute logparser with the queries you come up with.
Not sure if there's an equivalent for linux.
If you're on Windows and interested in hearing more, let me know. I had it hooked into an ANT task that downloaded log files every night, parsed them, generated reports, etc. It worked very well.

Concrete example:
<CFSET year = 2011 />
<CFDIRECTORY directory="#expandpath("log")#" action="list" sort="name" name="logfiles" filter="*#year#.xml" />
<CFOUTPUT query="logfiles">
<CFSET singlelogfile = xmlparse(directory & "/" & name) />
<CFSET records = XmlSearch(singlelogfile, "//record") />
<table>
<tr><td colspan="2">Month: #left(logfiles.name,3)#</td></tr>
<CFLOOP array="#records#" index="record">
<tr><td>#record.XmlAttributes.name#</td><td>#record.XmlAttributes.spend#</td></tr>
</CFLOOP>
</table>
</CFOUTPUT>
Of course you would need to change that the year comes from FORM-Scope, sum up multiple records for each person and maybe (if you can control it) change the filename from logs to 01-2011,02-2011,03-2011,..12-2011 so that they're getting directly sorted correctly.

Related

How to decouple variable names in external files and the code?

Imagine I have an external file dates.csv in the following format:
Name
Date
start_of_fin_year
01.03.2022
end_of_fin_year
28.02.2023
Obviously, the file may get updated in the future, and the date may change. I create a piece of code that checks the file periodically to extract needed dates and put them into the DB/variables. Roughly speaking, I have this pseudocode:
start_of_fin_year = SELECT Date FROM table WHERE Name = 'start_of_fin_year'
The problem I face: my code will break if I or someone else changes the name in the table. How do I prevent this?
FYI this is a personal project that I developed on my own, but I will have to give access to .csv files to others so they can update info. I'm afraid they may accidentally change the names, so that's why I'm worried.

Batch job to export data into CSV

I'm doing my first ABAP job and I don't have much experience so I need a little help.
What I want to do to create a batch job that runs every morning at a specific time, fetches data from different tables and exports it as a csv file. To create that batch job I can use transaction code SM36 or SM37.
But I need some help how to fetch the data?
Has anyone an example code that I can use or take a look at?
TheG is right, it sounds like you're trying to learn ABAP from scratch with no guidance. That's difficult but here are some basics:
There are three parts to this:
1. create a program
2. generate a file
3. schedule the job
For 1,
If you go to SE38, you can create a new report. You'll have to check with your colleagues about the namespace, but usually you just start the program with Z (which puts it in the 'customer' namespace).
In the entry box of SE38, you can type DEMO to pull up lots of sap-provided demo reports. The names usually give you a hint about what they demo and you can probably find one that mentions creating a file.
Once you create your own report through SE38 by typing in the name and hitting enter, you can use SELECT...INTO TABLE or SELECT ... ENDSELECT to query the database tables. Highlight select and click the blue i icon to pull up SAP's internal documentation.
At it's most basic, you can use the WRITE statement to print out the rows and columns of your data.
Once you have your report running, then scheduling it with SM36 will be more self explanatory.
This is very basic ABAP reporting program stuff. Making the report run as a background/batch job is the least of the concerns. Let us help you walk through this.
-> Have you done any reporting programming before ?
-> Do you have the list of tables from which you want the data and do you know how they are linked ?
-> Do you know how often this report would be run and what would be the selection criteria required ?
-> Did you check with the functional team whether you want 'delta pull' or 'full pull' every time you run the report ?
-> Do you have the file share where you want to output the file ? Is it on the presentation server or the application server ? If not presentation server can you reason out why not ?
-> Did you confirm on the file name and how it should look ?
-> Do you know how to generate a CSV file ? If this is a 'production requirement' ,are there reusable frameworks for handling file operations in your company ?
-> Do you have the final format of how the CSV file would look ?
-> Did you verify with the functional team whether they want the output data in external format for some fields ?
-> Did you check if there are date fields in your output and what format you want it to be for consistency ?
If you are familiar with ABAP a little bit, explore answers for above, write a report and getting it running in dialog mode. Then revert back to us and we will help you on how to run it as a batch job.

Display image with wildcard in src

I have a series of photos on a server with a strict naming convention: "uniqueId-readableName.jpg". I do this because my website database currently only logs the uniqueID (and some unrelated info), but people occasionally need to look at the file server directly to browse the photos (via FTP) so a readable name is useful. For example
001456-War Horse.jpg
003295-Sunshine Daiseys.jpg
129084-Laboring at the farm 2013-08-11.jpg
Now, I'm fairly sure the best option would be to set up the database with a record of the full file names, but I just wanted to see if anyone had any ideas I may have missed. This question on SO is similar, but here I have strict naming conventions - not sure if that opens up any possibilities or not.
I'm applying this to img, but the same idea could be appled to any file extension (eg, download "789-My Homework.zip" or "123-Family vacation.zip").
As an example of what I'm looking for, in Windows Explorer you can do a file search for
0*.jpg
and all of the following files can be returned
001456-War Horse.jpg
003295-Sunshine Daiseys.jpg
029084-Laboring at the farm 2013-08-11.jpg
In my case the beginning part is always unique, so I'd like to use something like 001456-*.jpg and have it return 001456-War Horse.jpg.
Is there any way do this on a website?
<img src="001456-*.jpg" />
Although no such method exists, you can still do a server side scripting to acheive the functionality you require.
For example in PHP you could use in-built commands to browse a folder, select all files matching the criteria name as '001456-*.jpg' and then depending upon the number of records returned select the first filename and insert it into an IMG tag.
Here is a sample implementation in PHP:
$files = array();
$id = "001456";
$files = glob("id-*.jpg");
echo "<img src='$files[0]' />"; //Assuming one file is found for every unique ID.

Display certain records in DataGrid in Flash Builder

I have an application with a DataGrid and I have it's Data/Services coming from a MySQL database relayed by a ColdFusion server. I have a table that has the following columns: name, tstamp, store, and dept. Here is my question:
On the click of a button, I want to be able to display the most recent tstamp(as a label) for a name that was selected prior to the button being clicked. This label will change depending on what name the user selects. Ive tried writing MySQL queries in the .cfc files and Ive also tried to do it using straight Flex code without any luck. Does anyone have any tips they can give me to point me in the right direction? Thanks in advance.
Here is the code in the .cfc file that is incomplete due to not knowing how to change the results based on the name the user picks.
<cffunction name="recent_timestamp" output="false" access="remote" returntype="any" >
<cfset var qAllItems="">
<cfquery name="qAllItems" datasource="TimeClock">
SELECT * FROM tbl_timestamps
WHERE tstamp = (select max(tstamp) from tbl_timestamps where store = "Dodge")
</cfquery>
<cfreturn qAllItems>
</cffunction>
I tried to use Flex code as well but I can't figure out how to call certain data fields from a Data Service.
After some discussion the resolution was to get the necc data via join at login time. This issue was bypassed.

Pre-process (classic) ASP page

I am running a classic vbscript ASP site with a SQL2008 database. There are a few pages that are processor-heavy, but don't actually change that often. Ideally, I would like the server to process these once a night, perhaps into HTML pages, that can then fly off the server, rather than having to be processed for each user.
Any ideas how I can make this happen?
The application itself works very well, so I am not keen to rewrite the whole thing in another scripting language, even if classic asp is a bit over the hill!!
Yes :
You didn't specify which parts of the pages are "processor heavy" but I will assume it's the query and processing of the SQL data. One idea is to retrieve the data and store it as a cached file, in the filesystem. XML is a good choice for the data format.
Whereas your original code is something like this:
(psuedocode)
get results from database
process results to generate html file
...your modified code can look like this:
check if cache file exists
if not exist
get results from database
store results in cache file
get results from cache file
process results to generate html file.
This is a general caching approach and can be applied to a situation where you've got
query parameters determining the output. Simply generate the name of the cache file based on all the constituent parameters. So if the results depend on query parameters named p1 and p2, then when p1 and p2 have the values 1234 and blue respectively, the cache file might be named cache-1234-blue.xml . If you have 5 distinct queries, you can cache them as query1-1234-blue.xml, query2-1234-blue.xml and so on.
You need not do this "nightly". You can include in your code a cache lifetime, and in place of the "if cache file exists" test, use "if cache file exists and is fresh". To do that just get the last modified timestamp on the cache file, and see if it is older than your cache lifetime.
Function FileOlderThan(fname, age)
'function returns True if the file is older than the age,
' specified in minutes.
Dim LastModified, FSO, DateDifference
Set FSO = CreateObject("Scripting.FileSystemObject")
LastModified = FSO.GetFile(fname).DateLastModified
DateDifference = DateDiff("n", LastModified, Now())
If DateDifference > age Then
FileAge = False
Else
FileAge = True
End If
End Function
fname = Server.MapPath(".") & cacheFileName
If FileOlderThan(fname, 10) Then
... retrieve fresh data ...
End If
This could be 10 minutes, 10 hours, 10 requests, whatever you like.
I said above that XML is a good choice for the data format in the cachefile. ADO has a SaveAsXML method, and you can also generate XML directly from SQL2008 using the FOR XML clause appended to the query.
If the "processor heavy" part is not the query and retrieval, but is the generation of the html page, then you could apply the same sort of approach, but simply cache the html file directly.