#!/usr/bin/perl
#
# Author:  Petter Reinholdtsen
# Date:    2005-11-29
# License: GNU Public License v2
#
# Make summary of membership counts.

use strict;
use warnings;

BEGIN {
    # Add script location to include path
    my @p = split(m%/%, $0); pop @p; push @INC, join("/", @p);
}

use Getopt::Std;
use medlemsliste;
use Date::Parse;
use Text::Wrap;
use POSIX qw(strftime);

sub help {
    print <<EOF;
Usage: $0 [-dFk] [-f <filename>]
  -f <filename>  read member database from <filename>
  -a             show local area (fylke) member distribution
  -b             show payment information
  -p             count only paying members, current year if no -y option
  -y <year>      with -p, year in question, 4 digits
  -k             show county (kommune) member distribution
  -F             show firm member distribution
  -m             list invididual members in area/county/firm
  -L             with individual members, include more details
  -d             enable debug output
  -M             show munin style output of member count
  -s             show summary useful for the board meetings
  -c             produce one line of member numbers, CSV style
  -T             show munin style output of average membership duration
  -h             display this help text
EOF
    exit 1;
}

my %memberclasses = ();
my %firmcount = ();
my $noemail = 0;
my %pcode_to_area;
my $sumperiod = 0;
my %pcode_to_county = ();
my %firmdistr = ();
my %countydistr = ();
my %areadistr = ();
my %members;
my $sagecount = 0;
my $unixaccess = 0;
my $unixaccesstotal = 0;

my %betalttil = ();

# How many persons are covered by the default firm membership fee
my $firmlimit = firmmembers_included();

my %fylker =
    ("01" => "Østfold",
     "02" => "Akershus",
     "03" => "Oslo",
     "04" => "Hedmark",
     "05" => "Oppland",
     "06" => "Buskerud",
     "07" => "Vestfold",
     "08" => "Telemark",
     "09" => "Aust-Agder",
     "10" => "Vest-Agder",
     "11" => "Rogaland",
     "12" => "Hordaland",
     "14" => "Sogn og Fjordane",
     "15" => "Møre og Romsdal",
     "16" => "Sør-Trøndelag",
     "17" => "Nord-Trøndelag",
     "18" => "Nordland",
     "19" => "Troms",
     "20" => "Finnmark",
     "21" => "Svalbard",
     "22" => "Jan Mayen",
     "23" => "Kontinentalsokkelen",
     );

my %opts;
getopts("abpy:df:FhkmLMscT", \%opts) || help();
my $debug = $opts{d};

help() if ($opts{h});

my $onlypaid = $opts{p}; # Count only paying members
my $checkpdate = $opts{y}; # Current payment check date
if ($onlypaid) {
  if (!$checkpdate || length($checkpdate) != 4 || $checkpdate lt 2000 || $checkpdate gt 2100) {
    # invalid year or no year
    $checkpdate = 1900 + (localtime)[5];
  }
  $checkpdate .= "-12-31";
}
    
load_pcode_map();

my $personcount = 0;
load_memberslist($opts{f} || "medlemsliste.csv", \&process_entry);

if ($opts{M}) {
    # munin style output
    if (defined $ARGV[0] && $ARGV[0] eq "autoconf") {
        print "yes\n";
    } elsif (defined $ARGV[0] && $ARGV[0] eq "config") {
        print "graph_category NUUG\n";

        print "graph_title Member count\n";

        # Arguments to "rrdtool graph". In this case, tell it that the
        # lower limit of the graph is '0', and that 1k=1000 (not 1024)
        print "graph_args --base 1000 -l 0\n";

        # The Y-axis label
        print "graph_vlabel members\n";

        # We want Cur/Min/Avg/Max unscaled (i.e. 0.42 members instead of
        # 420 millimembers)
        print "graph_scale no\n";

        # The fields. "label" is used in the legend. "label" is the only
        # required subfield.
        for my $class (sort keys %memberclasses) {
            my $type = lc($class);
            $type =~ tr/ /_/;
            $type =~ s/Æ/ae/;
            print "$type.label $class\n";
        }
        # print "sage.label LISA SIG-medlem\n";    # fjernet LISA jonp 2021-01-09
    } else {
        for my $class (sort keys %memberclasses) {
            my $type = lc($class);
            $type =~ tr/ /_/;
            $type =~ s/Æ/ae/;
            printf "$type.value %d\n",  $memberclasses{$class};
        }
        # print "sage.value $sagecount\n";         # fjernet LISA jonp 2021-01-09
    }
    exit 0;
} elsif ($opts{T}) {
    # munin style output with average membership duration
    if (defined $ARGV[0] && $ARGV[0] eq "autoconf") {
        print "yes\n";
    } elsif (defined $ARGV[0] && $ARGV[0] eq "config") {
        print "graph_category NUUG\n";
        print "graph_title Avg. membership duration\n";
        print "graph_args --base 1000 -l 0\n";
        print "graph_vlabel years\n";
        print "graph_scale no\n";

        print "duration.label Avg. membership duration\n";
    } else {
        my $value = $sumperiod / ($personcount * 60*60*24*365.15);
        print "duration.value $value\n";
    }
    exit 0;
}

if  ($opts{s}) {
    my $date = strftime("%Y-%m-%d %H:%M%z", localtime());
    my $firmcount = $memberclasses{"FIRMAMEDLEM"};

    $firmcount = 0 if (!$firmcount);
    my $str = "I følge medlemsregisteret $date har vi $firmcount firma ".
        "og $personcount personer som medlem.";
    my $avgtime = $sumperiod / ($personcount * 60*60*24*365.15);
    $str .= sprintf " Gjennomsnittlig medlemstid er %.1f år.", $avgtime;

    $str .= " Vi mangler epostadresse for $noemail personmedlemmer.";
    my %classmap =
        ('Medlem under bedrift' => 'medlemmer under bedrift',
         'Personlig medlem' => 'personlige medlemmer',
         'Personlig medlem uten USENIX' => 'personlige medlemmer uten USENIX',
         'Studenter' => 'studenter',
         'Studentmedlem uten USENIX' => 'studenter uten USENIX',
         'Æresmedlem' => 'æresmedlemmer',
         );
    $str .= " Fordelingen i kategorier er";
    for my $class (sort keys %classmap) {
        $memberclasses{$class} = 0 if (!$memberclasses{$class});
        $str .= " " . $memberclasses{$class} . " " . $classmap{$class} .",";
    }
    # Endre "a, b, c" til "a, b og c."
    $str =~ s/(.*), (.*),$/$1 og $2./;
    # $str .= " $sagecount medlemmer er også LISA SIG-medlem.";  # fjernet jonp 2018-03-04
    if ($onlypaid) {
      $str .= " Oversikten omfatter kun medlemmer som har betalt kontingent til og med $checkpdate.";
    } else {
      $str .= " Oversikten omfatter også medlemmer med ubetalt kontingent.";
    }

    $Text::Wrap::columns = 72;
    print wrap('','', $str), "\n";
    exit 0;
}

if  ($opts{c}) {
    # Produce one linje of CSV output
    my $str = "";
    my $count = 0;
    my $file = $opts{f} || "medlemsliste.csv";

    print "file,year,bedrift,totalperson,medlubedrift,personlig,persuusenix,student,studuusenix,æresmedl\n";

    $str .= "$file,";

    if ($onlypaid) {
      $str .= substr($checkpdate,0,4) . ",";
    } else {
      $str .= "No,";
    }

    $count = $memberclasses{"FIRMAMEDLEM"};
    $count = 0 if (!$count);
    $str .= "$count,";
    $str .= "$personcount,";
    $count = $memberclasses{"Medlem under bedrift"};
    $count = 0 if (!$count);
    $str .= "$count,";
    $count = $memberclasses{"Personlig medlem"};
    $count = 0 if (!$count);
    $str .= "$count,";
    $count = $memberclasses{"Personlig medlem uten USENIX"};
    $count = 0 if (!$count);
    $str .= "$count,";
    $count = $memberclasses{"Studenter"};
    $count = 0 if (!$count);
    $str .= "$count,";
    $count = $memberclasses{"Studentmedlem uten USENIX"};
    $count = 0 if (!$count);
    $str .= "$count,";
    $count = $memberclasses{"Æresmedlem"};
    $count = 0 if (!$count);
    $str .= "$count\n";
    print $str;

    exit 0;
}

if ($onlypaid) {
    print "Oversikt over medlemmer med betalt kontingent til og med $checkpdate\n";
} else {
    print "Oversikt over medlemmer, også uten betalt kontingent\n";
}
print "Fordeling i medlemskategorier\n";
print "\n";
for my $class (sort keys %memberclasses) {
    if ("FIRMAMEDLEM" eq $class) {
        printf " %4d       %s\n", $memberclasses{$class},$class;
    } else {
        printf(" %4d %4.1f%% %s\n", $memberclasses{$class},
               (100*$memberclasses{$class} / $personcount),$class);
    }
}

my $firmextra = 0;
my $firmextracount = 0;
my $firmshort = 0;
my $firmshortcount = 0;
my $firmliststr;
for my $firmid ( sort sortfirm keys %firmcount) {
    my $count = $firmcount{$firmid};
#    print "C: ", $firmcount{$firmid},"\n";
    if ($firmcount{$firmid} < $firmlimit) {
        $firmshort += $firmcount{$firmid};
        $firmshortcount ++;
    }
    if ($firmcount{$firmid} > $firmlimit) {
        $firmextra += $firmcount{$firmid} - $firmlimit;
        $firmextracount ++;
    }
    my $firmname = $members{$firmid}->{Name} || "[undefined]";
    $firmliststr .= "  $count $firmname ($firmid)\n";
    if ($opts{m}) {
        my @infirm = @{$firmdistr{$firmid}};
        $firmliststr .= listsubmembers(@infirm);
    }
}
print "\n";
printf(" %4d Medlem under bedrift (1-%d), %d bedrifter\n",
       $firmshort, $firmlimit - 1, $firmshortcount);
printf(" %4d Medlem under bedrift (%d), %d bedrifter\n",
       $memberclasses{'Medlem under bedrift'} - $firmextra - $firmshort - ($firmextracount * $firmlimit),
       $firmlimit,
       $memberclasses{"FIRMAMEDLEM"} - $firmshortcount - $firmextracount);
printf(" %4d Medlem under bedrift (%d->), %d bedrifter, %d ekstra medlemmer\n",
       $firmextra + ($firmextracount * $firmlimit), $firmlimit + 1, $firmextracount, $firmextra);
print "\n";
# print "Totalt $personcount personer er medlem, $sagecount er også LISA SIG-medlem.\n";   # LISA ikke lenger aktuelt  jonp 2018-03-04
print "Totalt $personcount personer er medlem.\n";
print "Mangler emailadresse for $noemail personmedlemmer.\n";
# print "$unixaccesstotal personer ($unixaccess medlemmer) har unix-brukernavn hos NUUG.\n"; # Gir feil tall jonp 2024-09-17
printf("Gjennomsnittlig medlemstid: %.1f år\n",$sumperiod / ($personcount * 60*60*24*365.15));

my $totalcount = (scalar keys %members) - $memberclasses{'FIRMAMEDLEM'};

sub listsubmembers {
    my @memberlist = @_;
    my $msg = "";
    my $lisa = "";
    my $printedid = "";

    for my $customerid (sort({ $members{$a}->{Name} cmp $members{$b}->{Name}}
                             @memberlist)) {
        if ($opts{L}) {
           if ($members{$customerid}->{'SAGE'}) {
              $lisa = " (LISA SIG medlem)";
           } else {
              $lisa = "";
           }
           $printedid = "($customerid) ";
        }
        $msg .= sprintf("      %s%s <%s>%s\n",
                         $printedid,
                         $members{$customerid}->{Name},
                         $members{$customerid}->{EmailAddress},
                         $lisa);
    }
    return $msg;
}

sub sort_by_number_alpha {
    my ($hashref, $a, $b) = @_;
    my $ret = @{$hashref->{$a}} <=> @{$hashref->{$b}};
    $ret = $b cmp $a if (0 == $ret);
    return $ret;
}

if ($opts{a}) {
    print "\n";
    print "Personer fordelt på fylke (totalt $totalcount):\n\n";
    for my $areaname (sort { sort_by_number_alpha(\%areadistr, $b, $a); }
                                 keys %areadistr) {
        my @inarea = @{$areadistr{$areaname}};
        my $count = @inarea;
        my $fraction = 100 * $count/$totalcount;
        printf " %3d %s (%2.2F %%)\n", $count,$areaname,$fraction;
        print listsubmembers(@inarea) if ($opts{m});
    }
}
if ($opts{k}) {
    print "\n";
    print "Personer fordelt på kommune (totalt $totalcount):\n\n";
    for my $countyname (sort { sort_by_number_alpha(\%countydistr, $b, $a); }
                        keys %countydistr) {
        my @incounty = @{$countydistr{$countyname}};
        my $count = @incounty;
        my $fraction = 100 * $count/$totalcount;
        printf " %3d %s (%2.2F %%)\n", $count,$countyname,$fraction;
        print listsubmembers(@incounty) if ($opts{m});
    }
}
if ($opts{F}) {
    print "\n";
    print "Personer fordelt på firma\n\n";
    print $firmliststr;
}

if ($opts{b}) {
    print "\n";
    print "Fram til hvilken måned er kontingenten betalt (ant. personer):\n";
    for my $til (sort keys %betalttil) {
        print "  $til ", $betalttil{$til}, "\n";
    }
}


sub sortfirm {
    my $compare = $firmcount{$b} <=> $firmcount{$a};
    if (0 == $compare) {
        return lc($members{$a}->{Name}) cmp lc($members{$b}->{Name});
    } else {
        return $compare;
    }
}

sub process_entry {
    my $memberinforef = shift;
    $unixaccesstotal++ if ($memberinforef->{'unixlogin'});
    if ($memberinforef->{'ZUsrMedlemstatus'} &&
        !($onlypaid && $checkpdate gt ($memberinforef->{ZBetaltTil}? $memberinforef->{ZBetaltTil}:"0000-00-00"))) {  # payment check
        my $customerid = $memberinforef->{CustomerNo};
        $members{$customerid} = $memberinforef;
        print STDERR $memberinforef->{Name} if $debug;
        print STDERR " <",$memberinforef->{'EmailAddress'},">" if $debug;
        print STDERR ";",$memberinforef->{'ZUsrMedlemstatus'} if $debug;
        print STDERR ";",$memberinforef->{'ZUsrArbeidsgiver'},"\n" if $debug;
        $memberclasses{$memberinforef->{'ZUsrMedlemstatus'}}++;
        $noemail++ if (!$memberinforef->{'EmailAddress'} and
                       $memberinforef->{'ZUsrMedlemstatus'} ne 'FIRMAMEDLEM');

        # Only distribute people on county, area, sage and company
        unless ("FIRMAMEDLEM" eq $memberinforef->{'ZUsrMedlemstatus'}) {
            $personcount++;

            $betalttil{$memberinforef->{ZBetaltTil} ? $memberinforef->{ZBetaltTil} : "ikke bet  "}++;

            $sagecount++ if ($memberinforef->{'SAGE'});
            $unixaccess++ if ($memberinforef->{'unixlogin'});

            if ($memberinforef->{'ZUsrArbeidsgiverNo'}) {
                $firmcount{$memberinforef->{'ZUsrArbeidsgiverNo'}}++;
                push(@{$firmdistr{$memberinforef->{'ZUsrArbeidsgiverNo'}}},
                     $customerid);
            }

            my $countyname = $pcode_to_county{$memberinforef->{PostCode}};
            $countyname = "Ukjent/Utlandet" unless $countyname;
            printf(" county '%s' for pcode '%s'\n", $countyname,
                   $memberinforef->{PostCode}) if $debug;
            push(@{$countydistr{$countyname}}, $customerid);

            my $area = "Ukjent/Utlandet";
            if ($memberinforef->{PostCode} and
                exists $pcode_to_area{$memberinforef->{PostCode}} ) {
                $area = $fylker{$pcode_to_area{$memberinforef->{PostCode}}}
            } else {
                print "Unknown pcode for member $customerid: ",$memberinforef->{PostCode},"\n"
                    if $debug;
            }
            push(@{$areadistr{$area}}, $customerid);
            my $inntime = str2time($memberinforef->{ZUsrInnmeldtDato});
            $sumperiod += (time() - $inntime) if defined $inntime;
        }
    }
}

sub load_pcode_map {
    open(PCODES, "< pcode-oversikt.txt") || die <<EOF;
Unable to load pcode-oversikt.txt.  Fetch it from
<URL:http://epab.posten.no/Norsk/Nedlasting/_Files/tilbud5.txt>.
EOF
    while (<PCODES>) {
        next if /^#/;
        chomp;
        my ($pcode, $pcodename, $countynr, $countyname, $code) = split /\t/;
        my $name = "$countynr $countyname";
        $pcode_to_county{$pcode} = $name;
        $pcode_to_area{$pcode} = substr($countynr, 0,2);
    }
}
