Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations wOOdy-Soft on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Can you help? string manipulation pollava!

Status
Not open for further replies.

cfdeveloper

Programmer
Nov 20, 2003
144
GB
Hello everybody,

I was wondering if someone could help. I'm hopeless with string manipulation and am in right mess. There was someone helping me out but I've pissed him off and he doesn't want to help anymore.

I'm working on a logging system for the help desk, the key to which is the log comments. The db is SQL Server 2000. The support desk currently use a desktop client (proprietory applicaton) to log calls. I'm doing the web-front end which will replace the desktop application. The log comments are stored in a table that has the column for comments.

I'll use the term "legacy log" for all logs that have been logged through the desktop application. Most if not all legacy comments are stored in this format. There might be few logs without the slug

Ex
This is the slug.

*** 12/06/2003 19:56:38 USER1 ***
closing if fixed - awaitng response from User2

*** 04/12/2003 18:55:18 USER2 ***
a test log comment

As you can see from the above example, the comment column stores the slug (most of the legacy calls have a slug, the slug is the top most line in the comment)

The legacy comment is stored in this format:

*** 12/06/2003 19:56:38 USER1 ***
closing if fixed - awaitng response from User2

The proposed new comment format for the webfront end looks like this:

USER1 | 12/06/2003 19:56:38
closing if fixed - awaitng response from User2

The 'user' and 'timestamp' text is wrapped around a span tag like this:

<span class="timestamp">USER1 | 12/06/2003 19:56:38</span><br>
closing if fixed - awaitng response from User2<hr>

The webfront end allows users to add rich text in the comments field. I'm also wrapping all comments added through the new system around a userdefined tag:<newlog>. All legacy logs will be wrapped around a userdefined tag: <oldlog>
All comments added from the new system would be stored in this fashion in the db:

<newlog userName="user1"><span class='timestamp'>user1 | 4/7/2004 10:41:00</span>
a test log</newlog>

I've pasted the entire code, you have to save it in a cfm file and run it on your localhost. It should work without any errors.

Each time the function (getComments) is called, the code does some processing ie, creates the header, finds the timestamp and user name and puts it in the header, it then builds the comments ie calls the FormatComment() function which adds the header wrapped around the span tag followed by the comment wrapped around the pre tag and returns and appends the value in the comment array.

It does the above every time a log is viewed even if the comment has the <newlog> or <oldlog> tag wrapped around.
I've been asked to not bother doing the above. If the comment is already wrapped around the "<newlog> or <oldlog>" tag, the code should grab everything between the newlog and oldlog tag including the tags and append them in the order they appear in the array.

<newlog userName="AClark01"><span class="timestamp">AClark01 | 5/7/2004 17:11:21</span><br>
a new log<hr></newlog>
<oldlog userName="AClark01"><span class='timestamp'>AClark01 21/06/2002 14:12:54</span>
printer was marked as use offline, changed and all ok</oldlog>

The function getComments should look for the opening and closing <newlog> and/or <oldlog> tag. It should append the comment in the array , look for the next comment, if the next comment is not wrapped around the newlog or oldlog tags, it should call create the header (build the user and timestamp) and if its in the legacy format (ie *** ***) it should wrap the oldlog tag around it. If the comment has the <span class="timestamp"> tag it should wrap the newlog tag around it, and if its in the legacy call format it should wrap the oldlog tag around it.

I've spent 4 days trying to do this. I would suggest you to look at my code and possibly change it so it only ever does any processing if the comment is not wrapped around the userdefined tags. I'll be more than willing to answer your questions.

Best regards,
cfcoder

Here is the code:

<cfsavecontent variable="str">
<newlog userName="aclark10"><span class='timestamp'>aclark10 | 4/7/2004 10:41:00</span>
a test log</newlog>
<span class="timestamp">User1 | 23/6/2004 18:34:59</span><br>added test comment - send email
*** USER1 test 05/12/2003 09:52:10 ***
closing if fixed <-> awaitng response from User2

*** 04/12/2003 18:05:01 USER4 ***
closed

User2 24/11/2003 11:30:38
engineer to call.

</cfsavecontent>
<cfscript>
qComments = QueryNew("comments");
QueryAddRow(qComments);
QuerySetCell(qComments,"comments",str);
</cfscript>
<!---
The code above is for me to test. Replace it by the following code

<cfquery name="qComments" datasource="qsm">
select comments from log_comments where callNo = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#url.callNo#">
</cfquery>
--->
<cfscript>
function GetHeaders(header){
// regexp for timestamp
var timestampRegexp = "\d{1,2}/\d{1,2}/\d{4}\s+\d{1,2}:\d{1,2}:\d{1,2}";
var stTmp = StructNew();
var stReturn = StructNew();
// remove *** if necessary
header = REReplace(header,"(^\s*[*]{3}\s*|\s*[*]{3}\s*$)","","all");
// find timestamp
stTmp = REFind(timestampRegexp,header,1,true);
if(stTmp.pos[1]){
// if there is a timestamp, the user is everything in the header but the timestamp
stReturn.user = Trim(Removechars(header,stTmp.pos[1],stTmp.len[1]));
// get the timestamp
stReturn.timestamp = REReplace(Mid(header,stTmp.pos[1],stTmp.len[1]),"\s+"," ");
}
else{
// if there is NO timestamp, the user is everything in the
stReturn.user = Trim(header);
// create a fake timestamp for normalization
stReturn.timestamp = "00/00/0000 00:00:00";
}
// remove trailing pipe from user if necessary
stReturn.user = REReplace(stReturn.user,"\s*\|$","");
// remove log tags
stReturn.user = REReplaceNoCase(stReturn.user,"</?(new|old)log\b.*?>","","all");
// determine log type
stReturn.log = "oldlog";
if(FindNoCase("<newlog",header)) stReturn.log = "newlog";
return stReturn;
}
function FormatComment(stComment){
var oldchars = "<,>,""";
var newchars = "&lt;,&gt;,&quot;";
return '<#stComment.log# userName="#stComment.user#"><span class="timestamp">#stComment.user#
| #stComment.timestamp#</span><br>
<pre style="font-family:Arial, Verdana;">#ReplaceList(stComment.text,oldchars,newchars)#</pre><hr></#stComment.log#>';
}
function GetComments(str){
var start = 1;
var cnt = 0;
var i = 0;
var stComments = StructNew();
var bComments = true;
var stTmp = StructNew();
// regular expression for header: line surrounded with *** OR line ending with
var commentRegexp = "(?m)^[ \t]*([*]{3}[^\n]*?[*]{3}|[^\n]*?\d{1,2}/\d{1,2}/\d{4}[ \t]+\d{1,2}:\d{1,2}:\d{1,2})[ \t]*$";
/*
Initialize structure
The structure has 3 keys:
1. "slug" containing the slug
2. "comments" is an array of comments. each element in the array is a structure containing 4 keys:
"user": user from the header
"timestamp": timestamp from the header
"text": text of the comment
"comment": full formatted comment
3. "formatted": the "comments" field with formatted headers
*/
stComments.comments = ArrayNew(1);
stComments.slug = "";
// normalize returns to newline character (chr(10))
str = REReplace(str,"\r(\n)","\1","all");
// first get rid of redundant tags
str = REReplaceNoCase(str,"</?(span|pre|hr)\b.*?>","","all");
// convert <br> to newline
str = REReplaceNoCase(str,"<br\b.*?>",Chr(10),"all");
// GET THE SLUG
// find first header
stTmp = REFind(commentRegexp,str,start,true);
if(stTmp.pos[1]){
// if there is a header, the slug is everything untill that header
if(stTmp.pos[1] GT 1) stComments.slug = Trim(Left(str,stTmp.pos[1]-1));
// insert the first header into the array
ArrayAppend(stComments.comments, GetHeaders(Mid(str,stTmp.pos[1],stTmp.len[1])));
// next start position
start = stTmp.pos[1]+stTmp.len[1];
}else{
// if there are no comments, everything is the slug
stComments.slug = Trim(str);
bComments = false;
}
// get comments if any
while(bComments){
// find next header
stTmp = REFind(commentRegexp,str,start,true);
// get index of last comment added to array
i = ArrayLen(stComments.comments);
if(stTmp.pos[1]){
// there is a next header
// get number of characters for last comment
cnt = stTmp.pos[1]-start;
// extract last comment text (and get rid of double linefeeds and log tags)
stComments.comments.text = REReplace(Trim(Mid(str,start,cnt)),"(\n)\n+","\1","all");
stComments.comments.text = REReplaceNoCase(stComments.comments.text,"</?(new|old)log\b.*?>","","all");
// construct entire comment (you can adapt this to whatever you need
stComments.comments.comment = FormatComment(stComments.comments);
// Get next header
ArrayAppend(stComments.comments, GetHeaders(Mid(str,stTmp.pos[1],stTmp.len[1])));
// next starting position
start = stTmp.pos[1]+stTmp.len[1];
}
else{
// there is no next header
// get number of characters for last comment (untill end of string)
cnt = Len(str)+1-start;
// extract last comment text (and get rid of double linefeeds)
stComments.comments.text = REReplace(Trim(Mid(str,start,cnt)),"(\n)\n+","\1","all");
stComments.comments.text = REReplaceNoCase(stComments.comments.text,"</?(new|old)log\b.*?>","","all");
// construct entire comment (you can adapt this to whatever you need
stComments.comments.comment = FormatComment(stComments.comments);
// no more comments are found, so stop the loop
break;
}
}
// rebuild the entire formatted comment field
// start with the slug
stComments.formatted = stComments.slug;
// loop over formatted comments
for(i=1;i LE ArrayLen(stComments.comments);i=i+1){
// add a double newline if there is already something in the comment field
if(Len(stComments.formatted)) stComments.formatted = stComments.formatted & chr(10) & chr(10);
// add the formatted comment
stComments.formatted = stComments.formatted & stComments.comments.comment;
}
return stComments;
}
</cfscript>

<cfif IsDefined("qComments") AND qComments.recordCount EQ 1>
<cfset mycomments = qComments.comments>
<cfset stComments = GetComments(mycomments)>
<cfset lastComment = "">
<cfset i = ArrayLen(stComments.comments)>
<cfif i>
<cfset lastComment = stComments.comments[1].comment>
</cfif>
<cfoutput>
<textarea name="comments" cols="50" rows="10">#variables.lastComment#</textarea>
<hr><h1>Normalized comment field</h1>
#stComments.formatted#
<cfdump var="#stComments#">
</cfoutput>
</cfif>
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top