人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

CGIの質問です。メールアドレス保有数を日毎、月毎に表示するCGIを作りたいと考えています。list.datにはメールアドレスが以下のように並んでいます。
aaa@bbb.ccc,,,,,,
bb@dd.ee,,,,,,
eeee@ccc.fff,,,,,,
list.datの行数が現在のメールアドレス保有数です。
毎日CRONで23:59にCGIを起動し、その日のlist.datの行数を数えて記録します。
ブラウザで確認できるよう記録したデータは次のように表示します。

◆月毎
日付 増減数 アドレス保有数
2007/4 100 3280
2007/3 -20 3180
2007/2 300 3200

◆日毎
日付 増減数 アドレス保有数
2007/2/22 -5 3000
2007/2/21 20 3005
2007/2/20 30 2985

CGIの知識が設置できるくらいしか持ち合わせていないため、完全な回答をしていただいた方にはお礼として1000ポイントを差し上げます。どうぞよろしくお願いいたします。

●質問者: icta
●カテゴリ:コンピュータ ウェブ制作
✍キーワード:23 AAA BB CCC CGI
○ 状態 :終了
└ 回答数 : 2/2件

▽最新の回答へ

1 ● tkyk3
●1000ポイント

ちょっと適当になってしまいましたが、作ってみました。

#!/usr/bin/perl

use strict;
use warnings;
use Template;
use CGI;
my $q = new CGI;#CGIとして実行した場合
#use Data::Dumper;

print $q->header( '-Content-Type'=> 'text/html',
'-charset' => 'UTF-8',
);

my $tt = Template->new();

my $address_filename = 'list.dat'; #読み込むメールアドレスリスト
my $log_filename = 'log.dat'; #ログを記録するファイル名
my $template_filename = 'template.tt'; #テンプレートファイル名
my $html_filename = 'logview.html'; #ログHTMLファイル名

#アドレスファイルを読み込む
open(IN,"$address_filename");
my @data = <IN>;
close(IN);

#今日の日付を取得
$ENV{'TZ'} = "JST-9";
my ($mday, $mon, $year) = ( localtime(time) )[3..5];
my $today = sprintf( "%04d/%02d/%02d" , $year + 1900, $mon + 1, $mday); #今日の日付

my $list_num = @data; #リスト数
my $log_data = join( ',' , $today, $list_num ); #書き込むデータを生成

#ログファイルを読み込む
open(IN,"$log_filename");
my @log_datas = <IN>;
close(IN);

push(@log_datas, $log_data); #今日の状況を追加する
my @daily_logs;
my @monthly_logs;

foreach (0 .. $#log_datas) {
($daily_logs[$_]->{date}, $daily_logs[$_]->{num}) = split(/,/, $log_datas[$_]);
}


my ($month_diff, $month);
foreach (0 .. $#log_datas) {
$month = $daily_logs[$_]->{date};
$month =~ s/\/\d{2}$//;
if ( $month ne $month_diff && $_ > 0 ) { #月が変わったら
push(@monthly_logs, { date => $month_diff, num => $daily_logs[$_ - 1]->{num} } );
 }
 elsif ($_ == $#log_datas) { #現時点で最後の日付
push(@monthly_logs, { date => $month, num => $daily_logs[$_]->{num} } );
 }
$month_diff = $month;
}

#ログファイルに追記保存
open OUT,">>$log_filename";
print OUT "$log_data\n";
close OUT;

balance(\@daily_logs);
balance(\@monthly_logs);
@daily_logs = reverse( @daily_logs ); #日付の新しい順にする
@monthly_logs = reverse( @monthly_logs ); #日付の新しい順にする

#テンプレートをもとにHTML保存
$tt->process("$template_filename", { 
daily => \@daily_logs ,
monthly => \@monthly_logs ,
 },
 $html_filename
 ) or die $tt->error();

#print "<pre>".Dumper(\@monthly_logs)."</pre>";
#print "<pre>".Dumper(\@daily_logs)."</pre>";

#--------------------------------------------------------------
#増減を追記
sub balance {
my $log_ref = shift;
my @output = @{ $log_ref };

foreach (0 .. $#output) {
if ( $_ > 0 ) { #2日目以降は増減を計算
my $yesterday_num = $output[ $_ - 1 ]->{num};
my $today_num = $output[ $_ ]->{num};
$output[ $_ ]->{balance} = $today_num - $yesterday_num;
}
else { #最初は純増
$output[ $_ ]->{balance} = $output[ $_ ]->{num};
}
}
}

上をlog.cgiとでもします。

my $address_filename = 'list.dat'; #読み込むメールアドレスリスト
my $log_filename = 'log.dat'; #ログを記録するファイル名
my $template_filename = 'template.tt'; #テンプレートファイル名
my $html_filename = 'logview.html'; #ログHTMLファイル名

ここを書き換えると出力先などを変更できます。

例ですと、log.datに日ごとの数値が記録されます。

logview.htmlにHTML形式でログを出力します。

あと、テンプレート(ひな形)ファイルとして、

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;">
<title>ログ集計</title>
</head>
<body>

<h2>◆月毎</h2>
<table>
<tr>
<th>日付</th>
<th>増減数</th>
<th>保有数</th>
</tr>

[% FOREACH m IN monthly %]
<tr>
<td>[% m.date %]</td>
<td>[% m.balance %]</td>
<td>[% m.num %]</td>
</tr>
[% END %]
</table>

<h2>◆日毎</h2>
<table>
<tr>
<th>日付</th>
<th>増減数</th>
<th>保有数</th>
</tr>
[% FOREACH d IN daily %]
<tr>
<td>[% d.date %]</td>
<td>[% d.balance %]</td>
<td>[% d.num %]</td>
</tr>
[% END %]
</table>

</body>
</html>

を、template.ttなどとして保存して同じディレクトリ内において下さい。ここの書式をもとに出力します。

◎質問者からの返答

早々の回答ありがとうございます。

教えていただいた内容で試してみましたが、どうもうまく行きません。

log.cgiを実行するとInternal Server Errorが表示されます。

Template-Toolkitはsakuraの場合、インストールする必要があるようです。

現在以下のkiryuuさんよりいただいた回答で動作を確認中です。

明日、増減数を希望通りに取得できましたら質問を終了したいと思います。

明日改めてご報告いたします。


2 ● kiryuu
●1000ポイント ベストアンサー

多次元連想配列もモジュールも使わずに作ってみました。

CRON呼びだし用(setlog.pl)と、表示用CGI(showlog.cgi)に分かれます。

ログファイルは、ログファイルのあるディレクトリのパーミッションを707にするか、予め空ログファイルを作成しておいてパーミッションを606にしておいてください。

CRON呼びだし用(setlog.pl)

#!/usr/bin/perl

use strict;

#ログファイルをサーバ内の絶対パスで記入
#もちろんそれぞれの環境にあわせる
my $logfile = '/home/kiryuu/www/test/2008.dat';

#リストファイルをサーバ内の絶対パスで記入
#もちろんそれぞれの環境にあわせる
# データの構成は、日付,総数,前日との差
my $datfile = '/home/kiryuu/www/test/list.dat';

#リストファイルを読み込む
open(IN,$datfile);
my @list = <IN>;
close(IN);
# @listにはアドレスファイルの中身が1行ごとに入っている

#ログファイルを読み込む
my @log;
if(-e $logfile){
open(LOG,$logfile);
@log = <LOG>;
close(LOG);
}

# @logを分解
my(%dat,%all,%diff) =();
foreach(@log){
my($t1,$t2,$t3) = split("\,",$_);
$dat{$t1} = $t1;
$all{$t1} = $t2;
$diff{$t1} = $t3;
}
# %datには日付、%allには総数、%diffには前日との差が入っている
# %datはなくてもよいが、後々何かに使えるかもしれない
# ハッシュのキーは日付

# 日付をソートする
my @sdays = sort({$a <=> $b} keys(%all));
# @sdaysには日付が昇順に入っている

# 今日の日付を取得する
$ENV{'TZ'} = 'JST-9';
my ($mday, $mon, $year) = ( localtime(time) )[3..5];
my $today = sprintf( "%04d/%02d/%02d" , $year + 1900, $mon + 1, $mday);
# 今日の日付がYYYY/MM/DDの書式で$todayに入っている

# リストの総数を取得する
my $all_day = scalar(@list);
# $all{今日の日付}に@listの数が入る。
# = @list や = $#list+1 でも可

# 前日との差を取得する
my $diff_day = $all_day - $all{$sdays[$#sdays]};
# $sdays[$#sdays]は@sdaysの最後の日付。
# %allには総数が格納されているので、
# $all{最後の日付}には最終日の総数が入る。
# これを、今日の総数から引くと差が出る。
#
# なぜ差も記録するか=>
#何らかの事情でログファイルを編集して古いデータを削除しても
#先頭の日付で前日との差を表示させるため

# 書き込むデータを作成
my $wdat = join(',',$today,$all_day,$diff_day) . "\n";

# ログファイルを追記モードで開く。ついでに書く
open(LOG,">>$logfile");
print LOG $wdat;
close(LOG);
chmod 0666,$logfile;

# 終了する
exit();

表示用CGI(showlog.cgi)

#!/usr/bin/perl

#ログファイル
#こっちは相対パスでも大丈夫
my $logfile = './2008.dat';

# ハッシュ変数を定義
my(%day,%all,%diff) = ();

# ログファイルを読み込んでハッシュに格納
open(LOG,$logfile);
my $tmp;#使い捨ての変数
while($tmp = <LOG>){
#改行文字を取り除く
chomp $tmp;
#使い捨て変数にログの内容を分割して格納
my($t1,$t2,$t3)=split("\,",$tmp);
# $t1(日付)をキーにハッシュ変数に代入
$day{$t1} = $t1;
$all{$t1} = $t2;
$diff{$t1} = $t3;
}
close(LOG);
# %day,%all,%diffにそれぞれ値が入った




#日ごとの部分のHTMLソース作成
#ハッシュを日付順にソートして、前日との差分を計算する
my @sdays = sort({$a cmp $b} keys(%all));
#日付で回す。$_には日付が入る
my @src_day = ();#HTMLソースの種リスト
foreach(0 .. $#sdays){
#前日との差を取得する
#はじめのデータの場合は%diffを用い、それ以外の場合は前日のデータとの差を用いる
my $diff_t;
if($_ == 0){
$diff_t = $diff{$sdays[$_]};
}else{
$diff_t = $all{$sdays[$_]} - $all{$sdays[$_ - 1]};
}
push(@src_day,"<tr><td align=\"right\">$sdays[$_]</td><td align=\"right\">$diff_t</td><td align=\"right\">$all{$sdays[$_]}</td>\n");
}

#日付部分のHTMLソースを完成させる
my $html_day = <<'_HTML_';
<table border>
<tr>
<th>日付</th>
<th>増減数</th>
<th>保有数</th>
</tr>
_HTML_
#リストを逆順(新しい方が上)にしてからソースに追加
foreach(reverse(@src_day)){
$html_day .= $_;
}
#テーブルタグを閉じます
$html_day .= '</table>';


#月のデータのソース作成
# @sdaysは使い回す
# %day,%all,%diffも使い回す
# 考え方:前月の最終日の総数?今月の最終日の総数=差

#月のハッシュを作成
my %m_hash = ();
foreach(@sdays){
$_ =~ /^(\d{4}\/\d{2})\//;
$m_hash{$1} = $1;#重複分は上書きされる
}

my %m_all = ();

#それぞれの月の最終日を取り出す
#まず、月に合致する日付を取り出す
foreach(keys %m_hash){
my $month = $_;
my @tmp;
foreach(@sdays){
if($_ =~ /^$month/){
push(@tmp,$_);
}
}
# @sdaysは以前にソートされているので、
# @tempの最後の値がその月の最終日
$m_all{$month} = $all{$tmp[$#tmp]};
}
# これで、%m_allには、それぞれの月の最終日の総数が入った

# 月をソートする
my @smonth = sort({$a cmp $b} keys(%m_all));
# @smonthには、月が昇順で入っています。

# 前月との差を求めつつソースも作る。
# 但し、最初の場合は「--」とする。
my @src_month = ();
foreach(0 .. $#smonth){
my $m_diff_t;
if($_ == 0){
$m_diff_t = '--';
}else{
$m_diff_t = $m_all{$smonth[$_]} - $m_all{$smonth[$_ - 1]};
}
push(@src_month,"<tr><td align=\"right\">$smonth[$_]</td><td align=\"right\">$m_diff_t</td><td align=\"right\">$m_all{$smonth[$_]}</td>\n");
}

#月部分のHTMLソースを完成させる
my $html_month = <<'_HTML_';
<table border>
<tr>
<th>日付</th>
<th>増減数</th>
<th>保有数</th>
</tr>
_HTML_
#リストを逆順(新しい方が上)にしてからソースに追加
foreach(reverse(@src_month)){
$html_month .= $_;
}
#テーブルタグを閉じます
$html_month .= '</table>';



## HTMLにはき出します
$html_src = <<"_HTML_";
Content-type: text/html; charset=Shift_JIS

<html>
<body>
<h2>なんかタイトルとか</h2>
<hr>
◆月毎
$html_month
<hr>
◆日毎
$html_day
<hr>
</body>
</html>
_HTML_

print $html_src;

exit();

サブルーチンも使わず、上から下までダ?っと処理しているんで、長くなってすいません。

Templateモジュールが使えるなら、tkyk3さんの回答の方がスッキリします。

◎質問者からの返答

早々の回答ありがとうございます。

早速、試してみたところ昨日23時59分にアドレス保有数を取得することができました。

本日同時刻に増減数を取得でき、希望通りの動作を確認しましたら改めてご報告いたします。

関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ