Re: Funky date question.
by Abigail (Deacon) on Jun 09, 2001 at 15:55 UTC
|
I can imagine you have a problem determining that. I have too.
Look at your example. In the first two lines, the same ID
is opened twice. Then it's put on hold twice, and opened
again, to be put on hold for a third time.
Confusing, isn't? What you really need is some kind of state
diagram, and transistions how IDs can change from state to
state. Then decide which states are "open" (probably after
they are opened, and no after they are closed - but what if
they are on hold?). Also, from your explaination, it isn't at
all clear what "hold" means. How does an ID get out of a hold
state?
Once you can define your problem in clear, unambigious terms,
the solution you are asking is likely to be clear to you too.
But currently, people can only guess what you mean - present
solutions matching their guess, but you won't know. You have
to guess too, and that's likely to result in a lot of wasted
efford.
-- Abigail | [reply] |
|
|
Having read the thread another time, I think I kind of understand what
you want to do.
Here's what I think:
You have a log file, three fields per record, one record per line.
First field is a timestamp, second field an ID or case number, last
field a transistion of the case. Valid states are "open", "hold"
and "closed". You want to sum, for each case, the time the case is
"open", that is, neither "closed", nor on "hold".
I've some untested partial code to do this. It's important to note that
for this calculation, "hold" and "closed" are equivalent. Only relevant
states are "open" and non-"open". The code assumes a parse_me
subroutine that parses a line, and returns three values: a timestamp
in seconds since some epoch, a case ID, and a state.
my %info;
while (<>) {
my ($time, $id, $state) = parse_me ($_);
unless ($info {$id}) {
next unless $state eq 'open';
$info {$id} = [$state, $time, 0];
next;
}
if ($state eq 'open') {
next unless $info {$id} -> [0] eq 'open';
$info {$id} -> [0] = 'open';
$info {$id} -> [1] = $time;
next;
}
next unless $info {$id} -> [0] eq 'open';
# This means the previous state was "open", and the current
# state is "hold" or "closed". Increment the time.
$info {$id} -> [2] += $time - $info {$id} -> [1];
$info {$id} -> [0] = $state;
$info {$id} -> [1] = $time; # Not really needed.
}
# Finally, for all "open" cases, add the time since it went
# "open" till now.
my $time = time;
while (my ($id, $info) = each %info) {
next unless $info -> [0] eq 'open';
$info -> [2] += $time - $info -> [1];
}
Now, for each ID, the total open time is found in
$info {$ID} -> [2].
-- Abigail
| [reply] [d/l] [select] |
Re: Funky date question.
by Chady (Priest) on Jun 09, 2001 at 11:21 UTC
|
Not sure what you're asking for here.. but, again, here's what I thought it is:
my (@opn, @hold);
while (my $line = <DATA>) {
my ($_date, $id, $code) = split(/ \| /, $line);
push @opn, $id if ($code =~ /open$/i);
push @hold, $id if ($code =~ /hold$/i);
}
# do whatever you want with them now..
__DATA__
Date: Jan 1 2001 3:30PM | ID: 1 | Code: open
Date: Jan 1 2001 3:35PM | ID: 1 | Code: open
Date: Jan 1 2001 3:37PM | ID: 1 | Code: hold
Date: Jan 1 2001 3:46PM | ID: 1 | Code: hold
Date: Jan 1 2001 4:10PM | ID: 1 | Code: open
Date: Jan 1 2001 4:35PM | ID: 1 | Code: hold
Update: I searched at CPAN, and found you might use DateTime-Precise
He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.
Chady | http://chady.net/ | [reply] [d/l] |
|
|
Mabye I should restate the problem
1 Date: Jan 1 2001 3:30PM | ID: 1 | Code: open
2 Date: Jan 1 2001 3:35PM | ID: 1 | Code: open
3 Date: Jan 1 2001 3:37PM | ID: 1 | Code: hold
4 Date: Jan 1 2001 3:46PM | ID: 1 | Code: hold
5 Date: Jan 1 2001 4:10PM | ID: 1 | Code: open
6 Date: Jan 1 2001 4:35PM | ID: 1 | Code: hold
Im looking for time calculations, IE
entries 1 and 2 are both open so thats 5 minutes open
entries 3 and 4 are on hold, so we dont count that to our total
entry 5 is open again and is open until entry 6 where it goes into hold again so thats 25 mins open there, in total a total of 30 mins the ID has been in 'open' code.
Also, this is being pulled out of a sql database, so if theres any funky thing i can do with the database too would be informative. | [reply] [d/l] |
|
|
If I understand you correctly, it seems that you just need to define an extra variable that contains the basepoint for the current calculations. I'll try to demonstrate this is pseudocode here:
my @currenttime=(); ## or initialize to the start time
my @totaltime=(); ## will hold the sums for each ID
foreach (@databaseline) {
## realistically, this will be something like
## while (@values = $sth->fetchrow) when you convert to
## using SQL ( Look at the DBI module and the corresponding
## DBD module for your database )
my ($dbtime,$code,$id)= &parseline($_);
## This routine would split the line and convert
## the date, passing back the useful values.
## When you get it out of SQL, you should have an array
## of values anyway, so it'll only be necessary to
## convert the date.
$totaltime[$id]+=($dbtime-$currenttime[$id]) if $code eq 'open';
$currenttime[$id]=$dbtime;
## we move the baseline to the time of the current record
}
## now the @totaltime array has the values you want.
| [reply] [d/l] |
|
|
|
|
|
|
|
|
|
The problem does not really lie in the 'how do I subtract this from this to get this', it lies in the logic behind the matter. 'how do I make my program so it does the math here, stops for a hold, and starts the math again when it goes out of hold'.
| [reply] |
|
|
Okay, I'll give this a shot; it appears you're looking for the logic, and not the code (which is good, because I'm not that good at Perl yet :). Anyway, here's my attempt at pseudocoding your problem:
my ($starttime, $isopen, $totaltime) = (0, 0, 0);
while (<TIMELOG>) {
if (/open$/) {
next if $isopen;
$isopen = 1;
$starttime = getseconds($_);
}
elsif (/hold$/ || /closed$/) {
next unless $isopen;
$isopen = 0;
$totaltime += getseconds($_) - $starttime;
}
}
Well, that came out a bit closer to code than I had intended... oh well. I hope that helps.
Update: (1000 CDT 09 Jun) It occurred to me a bit more explanation might be in order. Basically, as I understand it, the key to your problem is to keep track of the first time you noticed the ID was in an open state, and only perform the addition to $totaltime when the ID moves out of the open state (and presumably, into one of the closed or hold states).
Also, having watched a bit of the CB discussion on this topic, I noticed that you want to run this in realtime. If that's the case, you may want to generate a fake log entry to be processed at the end with the current time and a non-open state to force the calculation of the open time. This would change the code to be more like this:
@lines = <TIMELOG>;
push @lines, makefake(time(), "closed");
for (@lines) {
# Same loop body
}
Update #2: Edited code and above comments on real time about current state.
I know it might not be the optimal method to do it, but it seems as if it should get the job done (if I understand the job to be done correctly). Try it out - if it doesn't work, let me know. If it does, let me know then, too - I never know when I might need this bit of code. :)
CheeseLord | [reply] [d/l] [select] |
Re: Funky date question.
by Vynce (Friar) on Jun 09, 2001 at 13:03 UTC
|
foreach $line (@lines) {
($time, $id, $open) = parse($line);
next until $code eq 'open';
$open = $time;
until ($code eq 'hold') {
$line = shift(@lines);
($time, $id, $code) = parse($line);
$total += $time-$open;
}
now, the real problem comes in if you have multiple IDs mixed together. then you have to get much more intericate, because you need to keep track of whether that ID is open or not. for that, you might do something like this:
while (@lines) {
$line = shift(@lines);
($time, $id, $code) = parse($line);
$hash{$id}{want} ||= 'open';
next unless ($hash{$id}{want} eq $code);
if ($code eq 'open') {
$hash{$id}{open} = $time;
$hash{$id}{want} = 'hold';
} else {
$hash{$id}{total} += $time - $hash{$id}{open};
$hash{$id}{want} = 'open';
delete $hash{$id}{open};
}
}
what i'm doing in this latter is keeping track, for each ID, of whether i am currently open (want = 'hold') or currently on hold (want = 'open'). if i am on hold, and find an open, i start keeping track. if i am currently open and go on hold, i subtract the new time minus when i opened, and i was open for that long, so i add it to my total.
does that make sense? it might not be the most efficient way to do it, but it seems to me it'd get the job done.
Note that this is just the idea; you'll ahev to make the variables match yours, and write your own parse(), etc. but i think you were just looking for the idea.
hope that helps
.
| [reply] [d/l] [select] |