package Ernad::Membership; use strict; use warnings; use Carp qw(confess); use Data::Dumper; use Date::Format; use File::Compare; use File::Copy; use File::Path; use File::Slurper; use Storable; my $verbose; $|=3; sub form_membership_doc { my $e=$main::e or confess 'I see no erimp.'; my $membership_dir=$e->{'dir'}->{'membership'} // confess 'I no membership directory.'; if(not -d $membership_dir) { die "no such membership directory '$membership_dir'"; } my $doc=$e->{'x'}->get_ernad_doc(); my $m=&get_membership_dump(); if($verbose) { print Dumper $m; } my $ernad_ns=$e->{'const'}->{'ernad_ns'}; my $membership_element=$doc->createElementNS($ernad_ns,'membership'); my $total_element=$doc->createElementNS($ernad_ns,'total'); $total_element->setAttribute('subscribers',$m->{'total_subscribers'}); delete $m->{'total_subscribers'}; $total_element->setAttribute('subscriptions',$m->{'total_subscriptions'}); delete $m->{'total_subscriptions'}; $membership_element->appendChild($total_element); foreach my $repcode (keys %{$m}) { my $report_element=$doc->createElementNS($ernad_ns,'report'); $report_element->setAttribute('repcode',$repcode); $report_element->setAttribute('total_members',$m->{$repcode}); $membership_element->appendChild($report_element); } $doc->setDocumentElement($membership_element); return $doc; } sub get_membership_dump { my $e=$main::e // confess 'I see no erimp.'; my $tmp_dir=$e->{'dir'}->{'tmp'}; my $membership_dump=$tmp_dir.'/membership.dump'; ## output structure my $m; if(-f $membership_dump and -M $membership_dump < 0.5) { #print "retrieving $membership_dump\n"; $m=retrieve($membership_dump); } else { $m=get_the_membership_file($e); store($m,$membership_dump); } return $m; } sub get_the_membership_file { my $e=shift; my $m; foreach my $repcode ($e->list_repcodes()) { if($e->{'report'}->{$repcode}->{'end'}) { next; } if(defined($e->{'report'}->{$repcode}->{'testing'})) { next; } #print "recording members for $repcode\n"; $m=&record_members($repcode,$m) ; &zip_files($repcode) ; } delete $m->{'subscribers'}; return $m; } sub record_members { my $repcode=shift; my $m=shift; my $e=$main::e; # membership strut # # define the system command # my $today=`date +%Y-%m-%d`; chomp $today; my $tmp_dir=$e->{'dir'}->{'tmp'} or die; my $membership_dir=$e->{'dir'}->{'membership'}; if(not -d $membership_dir) { die "no membership_dir $membership_dir"; } my $tmp_file=$tmp_dir.'/'.$repcode.'_'.$today; my @subs; my $year=`date +%Y`; chomp $year; my $list_store_dir="$membership_dir/$repcode/$year"; if(not -d $list_store_dir) { mkpath($list_store_dir); } ## fixme. This should be configurable my $mm='/var/lib/mailman/bin/list_members'; foreach my $subscriber (`$mm $repcode`) { chomp $subscriber; #print "found $subscriber\n"; push(@subs,$subscriber); $m->{'total_subscriptions'}++; if(not $m->{'subscribers'}->{$subscriber}) { $m->{'total_subscribers'}++; $m->{'subscribers'}->{$subscriber}=1; } } if($verbose) { print Dumper $m; } $m->{$repcode}=$#subs; open(F,"> $tmp_file"); binmode(F,':utf8:'); foreach my $sub (sort @subs) { print F "$sub\n"; } my $short_today=`date +%m%d`; chomp $short_today; my $last_file=&find_last_file($repcode,$e); # print "last file is $last_file\n"; if($last_file=~m|^\s*$| or compare($tmp_file,$last_file) or ($short_today eq '0101')) { #print "copying $tmp_file into $list_store_dir\n"; copy($tmp_file,$list_store_dir); } return $m; } sub find_last_file { my $repcode=shift; my $e=shift; if(not $e) { $e=$main::e // confess "I can't see the erimp"; } my $year=`date -d "yesterday" +%Y`; chomp $year; my $store_dir=$e->{'dir'}->{'membership'} or die; my $ls=`ls $store_dir/$repcode/$year/$repcode* 2> /dev/null | tail -1`; chomp $ls; #if($verbose) { # print "last file $ls\n"; #} return $ls; } sub zip_files { my $repcode=shift; my $e=$main::e; # # define the system command # my $year=`date -d "yesterday" +%Y`; chomp $year; my $store_dir=$e->{'dir'}->{'membership'} or die; my $list_store_dir="$store_dir/$repcode/$year"; if(not -d $list_store_dir) { mkpath($list_store_dir); } my $s="ls $list_store_dir/$repcode* | grep -v gz\$"; if($verbose) { print "$s\n"; } my @files; foreach my $file (`$s`) { chomp $file; if($verbose) { print "file: $file\n"; } push(@files, $file); } my $today=`date +%m%d`; chomp $today; my $last=pop @files; if($verbose) { print "last $last\n"; } foreach my $file (@files) { if(-f "$file.gz") { next; } my $s="gzip $file\n"; if($verbose) { print "$s\n"; } system($s); } } ## fixme this should be used in record_members sub get_members { my $repcode=shift; my $e=$main::e; my $mm='/var/lib/mailman/bin/list_members'; my $m; foreach my $subscriber (`$mm $repcode`) { chomp $subscriber; $m->{$subscriber}=1; } return $m; } sub get_members_from_file { my $repcode=shift; my $file=&find_last_file($repcode); if(not -f $file) { confess "I can't see the file $file"; } my $m; my @lines=&File::Slurper::read_lines($file); foreach my $line (@lines) { chomp $line; $m->{$line}=1; } return $m; } ## and the members from several lists sub joint_members { my @lists=@_; my $m; foreach my $list (@lists) { $m->{$list}=&get_members_from_file($list); } ## take the first list as the base my $first=shift(@lists); foreach my $list (@lists) { foreach my $member (keys %{$m->{$first}}) { if(not $m->{$list}->{$member}) { #print $member; delete $m->{$first}->{$member}; } } } return $m->{$first}; } ## take members and substract members from others sub out_members { my $in=shift; my @lists=@_; my $m; foreach my $list (@lists) { $m->{$list}=&get_members_from_file($list); } foreach my $list (@lists) { foreach my $member (keys %{$m->{$list}}) { #print "delete $member\n"; delete $in->{$member}; } } #die Dumper $in; return $in; } 1;