"Fossies" - the Fresh Open Source Software Archive

Member "absence-v2.1/utils/manage-holidays.pl" (20 Oct 2013, 12858 Bytes) of package /linux/www/web-absence-2.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Perl source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "manage-holidays.pl" see the Fossies "Dox" file reference documentation.

    1 #!/usr/bin/perl
    2 
    3 #======================================================================
    4 #    This file is part of Absence.
    5 #
    6 #    Absence is free software: you can redistribute it and/or modify
    7 #    it under the terms of the GNU General Public License as published by
    8 #    the Free Software Foundation, either version 3 of the License, or
    9 #    (at your option) any later version.
   10 #
   11 #    Absence is distributed in the hope that it will be useful,
   12 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
   13 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14 #    GNU General Public License for more details.
   15 #
   16 #    You should have received a copy of the GNU General Public License
   17 #    along with Absence.  If not, see <http://www.gnu.org/licenses/>.
   18 #======================================================================
   19 
   20 # copyright Robert Urban
   21 
   22 use Getopt::Long;
   23 use Data::Dumper;
   24 use File::Basename;
   25 use Encode;
   26 
   27 #Getopt::Long::Configure('debug');
   28 my $DEBUG = 1;
   29 my ($import_file, $region, $country);
   30 
   31 $Data::Dumper::Indent = 1;
   32 
   33 my ($db_user, $db_pass, $db_host, $db_name);
   34 
   35 $db_host = 'localhost';
   36 
   37 #my $PATH_TO_CGI_BIN_DIR = '';
   38 
   39 my $mode;
   40 my $delete_list;
   41 my $list_thing;
   42 my ($new_region, $name, $desc);
   43 my $import_format;
   44 my $convert_file;
   45 my $date;
   46 
   47 my $res = GetOptions(
   48     'convert|conv=s'    => sub { $mode = 'convert'; $convert_file = $_[1]; },
   49     'import|i=s'        => sub { $mode = 'import'; $import_format = $_[1]; },
   50     'list|l=s'          => sub { $mode = 'list'; $list_thing = $_[1]; },
   51     'add-region|ar'     => sub { $mode = 'add-region'; },
   52     'add-holiday|ah'    => sub { $mode = 'add-holiday'; },
   53     'delete-holiday|dh=s'   => sub { $mode = 'delete-h'; $delete_list = $_[1]; },
   54     'delete-region|dr=s'    => sub { $mode = 'delete-r'; $delete_list = $_[1]; },
   55     'file|f=s'          => \$import_file,
   56     'country|c=s'       => \$country,
   57     'region|r=s'        => \$region,
   58     'user|u=s'          => \$db_user,
   59     'pass|p=s'          => \$db_pass,
   60     'host=s'            => \$db_host,
   61     'db-name|dbn=s'     => \$db_name,
   62     'help|h'            => \&usage,
   63     'name|n=s'          => \$name,
   64     'desc|d=s'          => \$desc,
   65     'date|dt=s'         => \$date,
   66 );
   67 
   68 if (!defined($mode)) {
   69     die "a mode must be specified, one of -convert/-import/-list/-delete*/-add-region\n";
   70 }
   71 
   72 #defined($import_file) || die "usage: $0 -file <input-file>\n";
   73 ($mode ne 'convert')
   74     && (!defined($db_user) || !defined($db_pass) || !defined($db_name))
   75     && die "DB username/password/name missing\n";
   76 
   77 #----------------------------------------------------------------
   78 # set up INC and require a few modules
   79 #----------------------------------------------------------------
   80 my $dn = dirname($0);
   81 push(@INC, $dn);
   82 foreach my $mod (qw(AbsenceUtils AbsenceMigration AbsenceImport)) {
   83     eval {
   84         require "$mod.pm";
   85     };
   86     if ($@) {
   87         die "failed to load module [$mod.pm]: $@\n";
   88     }
   89     
   90 }
   91 
   92 #----------------------------------------------------------------
   93 # get AbsenceConfig (and AbsenceDB)
   94 #----------------------------------------------------------------
   95 #AbsenceUtils::loadAbsenceDB($PATH_TO_CGI_BIN_DIR);
   96 
   97 ($mode ne 'convert')
   98     && AbsenceImport::init($db_user, $db_pass, $db_name, $db_host);
   99 
  100 if ($mode eq 'list') {
  101     list($list_thing);
  102 }
  103 elsif ($mode eq 'delete-h') {
  104     delete_hol($delete_list);
  105 }
  106 elsif ($mode eq 'delete-r') {
  107     delete_reg($delete_list);
  108 }
  109 elsif ($mode eq 'add-region') {
  110     add_region($name, $desc);
  111 }
  112 elsif ($mode eq 'add-holiday') {
  113     add_holiday($date, $desc, $country, $region);
  114 }
  115 elsif ($mode eq 'convert') {
  116     convert_ical($convert_file);
  117 }
  118 else {
  119     import_file($import_format, $import_file, $country, $region);
  120 }
  121 exit;
  122 
  123 #=======================================================================
  124 # subroutines
  125 #=======================================================================
  126 
  127 sub list
  128 {
  129     my $thing = shift;
  130 
  131     if ($thing =~ /^h.*/i) {
  132         list_holidays();
  133     }
  134     elsif ($thing =~ /^c.*/i) {
  135         list_countries();
  136     }
  137     elsif ($thing =~ /^r.*/i) {
  138         list_regions();
  139     }
  140     else {
  141         die "don't know how to list [$thing]\n";
  142     }
  143 }
  144 
  145 sub list_holidays
  146 {
  147     binmode(*STDOUT, ":utf8");
  148     my @hols = AbsenceImport::getDbHolidays();
  149     print "\n";
  150     printf("%3s %-25s %-12s %-18s %-17s\n",
  151         'ID', 'NAME', 'DAY', 'COUNTRY', 'REGION');
  152 
  153     my (@fixed, @variable);
  154 
  155     foreach my $hol (@hols) {
  156         $hol->{description} = decode_utf8($hol->{description});
  157         if ($hol->{day} =~ /^0001-/) {
  158             push(@fixed, $hol);
  159         }
  160         else {
  161             push(@variable, $hol);
  162         }
  163     }
  164 
  165     print "# fixed holidays\n";
  166     foreach my $hol (@fixed) {
  167         my $country = defined($hol->{country_id})
  168             ? AbsenceImport::getCountry($hol->{country_id})->{name}
  169             : '';
  170         my $region = defined($hol->{region_id})
  171             ? AbsenceImport::getRegion($hol->{region_id})->{name}
  172             : '';
  173         printf("%3d %-25s %-12s %-18s %-17s\n",
  174             $hol->{id}, $hol->{description}, $hol->{day}, $country, $region);
  175     }
  176 
  177     print "\n# variable holidays\n";
  178     my $prev_year;
  179     foreach my $hol (sort { $a->{day} <=> $b->{day} } @variable) {
  180         my ($y,$m,$d) = ($hol->{day} =~ /^(\d{4})-(\d{2})-(\d{2})$/);
  181         if ($prev_year != $y) {
  182             print "# $y\n";
  183         }
  184         my $country = defined($hol->{country_id})
  185             ? AbsenceImport::getCountry($hol->{country_id})->{name}
  186             : '';
  187         my $region = defined($hol->{region_id})
  188             ? AbsenceImport::getRegion($hol->{region_id})->{name}
  189             : '';
  190         printf("%3d %-25s %-12s %-18s %-17s\n",
  191             $hol->{id}, $hol->{description}, $hol->{day}, $country, $region);
  192         $prev_year = $y;
  193     }
  194 }
  195 
  196 sub list_countries
  197 {
  198     my @list = AbsenceImport::getCountries();
  199     print "\n";
  200     printf("%3s %3s %s\n", 'ID', 'CC', 'NAME');
  201     foreach my $c (@list) {
  202         printf("%3d %3s %s\n", $c->{id}, $c->{code}, $c->{name});
  203     }
  204     print "done.\n";
  205 }
  206 
  207 sub list_regions
  208 {
  209     my @list = AbsenceImport::getRegions();
  210     print "\n";
  211     if (!@list) {
  212         print "no regions defined\n";
  213         return;
  214     }
  215     printf("%3s %-30s %-40.40s\n", 'ID', 'NAME', 'DESCRIPTION');
  216     foreach my $r (@list) {
  217         printf("%3s %-30s %-40.40s\n", $r->{id}, $r->{name}, $r->{description});
  218     }
  219     print "done.\n";
  220 }
  221 
  222 sub add_region
  223 {
  224     my ($name, $desc) = @_;
  225 
  226     if (!defined($name) || !length($name)) {
  227         die qq{you must specify the region-name using "-rn"\n};
  228     }
  229 
  230     my $name_utf8 = AbsenceUtils::convertString($name);
  231     my $desc_utf8 = AbsenceUtils::convertString($desc);
  232 
  233     print "creating region [$name_utf8] with description [$desc_utf8]\n";
  234 
  235     my $id = AbsenceImport::createRegion($name_utf8, $desc_utf8);
  236     print "region created with id=$id\n";
  237 }
  238 
  239 sub add_holiday
  240 {
  241     my ($date, $desc, $country, $region) = @_;
  242     if ($date !~ /^(\d{4}|\*)-(\d{1,2})-(\d{1,2})$/) {
  243         die "add-holiday: date not specified or not in format YYYY-MM-DD\n";
  244     }
  245     my ($year, $mon, $day) = ($1, $2, $3);
  246     if (!defined($desc) || length($desc) == 0) {
  247         die "add-holiday: no description provided (use '-desc <desc>')\n";
  248     }
  249     my $desc_utf8 = AbsenceUtils::convertString($desc);
  250 
  251     my $additional = handle_country_region($country, $region);
  252 
  253     my $y   = ($year eq '*') ? '0001' : $year;
  254     my $new_date = "$y-$mon-$day";
  255     my $ref = {
  256         day         => $new_date,
  257         description => $desc_utf8,
  258         @{ $additional },
  259     };
  260     my $id = AbsenceImport::loadHoliday($ref);
  261     print "added holiday [$desc_utf8] with id=$id\n";
  262 }
  263 
  264 sub delete_hol
  265 {
  266     my $delete_list = shift;
  267 
  268     my @list = split(',', $delete_list);
  269 
  270     if ($list[0] eq 'all') {
  271         print "deleting all holidays\n";
  272         AbsenceImport::deleteAllHolidays();
  273         print "done.\n";
  274         return;
  275     }
  276 
  277     foreach my $n (@list) {
  278         if ($n !~ /^\d+$/) {
  279             die "ID in delete-list is not number: [$n]\n";
  280         }
  281     }
  282     foreach my $n (@list) {
  283         print "deleting holiday with id = $n\n";
  284         AbsenceImport::deleteHoliday($n);
  285     }
  286     print "done.\n";
  287 }
  288 
  289 sub delete_reg
  290 {
  291     my $delete_list = shift;
  292 
  293     my @list = split(',', $delete_list);
  294     foreach my $n (@list) {
  295         if ($n !~ /^\d+$/) {
  296             die "ID in delete-list is not number: [$n]\n";
  297         }
  298     }
  299     foreach my $n (@list) {
  300         print "deleting region with id = $n\n";
  301         AbsenceImport::deleteRegion($n);
  302     }
  303     print "done.\n";
  304 }
  305 
  306 sub import_file
  307 {
  308     my ($format, $file, $country, $region) = @_;
  309 
  310     if (defined($country) && defined($region)) {
  311         die "cannot specify both -country and -region.\n";
  312     }
  313 
  314     my $additional = handle_country_region($country, $region);
  315 
  316     if ($format eq 'ical') {
  317         import_ical($file, $additional);
  318     } elsif ($format eq 'legacy') {
  319         import_legacy($file, $additional);
  320     } else {
  321         die "format [$format] unknown. must be 'ical' or 'legacy'\n";
  322     }
  323 }
  324 
  325 sub handle_country_region
  326 {
  327     my ($country, $region) = @_;
  328 
  329     my $additional;
  330 
  331     if (defined($country)) {
  332         if ($country =~ /^\d+$/) {
  333             $additional = [country_id => $country];
  334         } elsif ($country =~ /^[a-z]{2}$/i) {
  335             my $id = AbsenceImport::lookupCountry($country);
  336             defined($id) || die "country-code [$country] unknown\n";
  337             $additional = [country_id => $id];
  338         } else {
  339             die "country must be an id (numeric) or a 2 letter country-code\n";
  340         }
  341     } elsif (defined($region)) {
  342         if ($region =~ /^\d+$/) {
  343             $additional = [region_id => $region];
  344         } else {
  345             my $id = AbsenceImport::lookupRegion($region);
  346             defined($id) || die "region [$region] unknown\n";
  347             $additional = [region_id => $id];
  348         }
  349     }
  350 
  351     return $additional;
  352 }
  353 
  354 sub import_legacy
  355 {
  356     my ($file, $additional_ref) = @_;
  357 
  358     my $hdata = AbsenceMigration::readHolidays($file);
  359 
  360     import($hdata, $additional_ref);
  361 }
  362 
  363 sub import_ical
  364 {
  365     my ($file, $additional_ref) = @_;
  366 
  367     my $hdata = AbsenceImport::parseIcal($file);
  368 
  369     import($hdata, $additional_ref);
  370 }
  371 
  372 sub import
  373 {
  374     my ($hdata, $additional_ref) = @_;
  375 
  376     my $debug = 0;
  377 
  378     foreach my $year (keys(%{ $hdata })) {
  379         $debug && print "year=[$year]\n";
  380         foreach my $month (keys(%{ $hdata->{$year} })) {
  381             $debug && print "  month=[$month]\n";
  382             foreach my $day (keys(%{ $hdata->{$year}->{$month} })) {
  383                 $debug && print "    day=[$day]\n";
  384                 my $y       = ($year eq '*') ? '0001' : $year;
  385                 my $d       = "$y-$month-$day";
  386                 my $id = AbsenceImport::lookupHoliday($day, $month, $y, $additional_ref);
  387                 if (defined($id)) {
  388                     print "holiday for [$d] already exists with id=$id. skip.\n";
  389                     next;
  390                 }
  391                 my $desc    = $hdata->{$year}->{$month}->{$day};
  392                 my $ref = {
  393                     day         => $d,
  394                     description => encode_utf8($desc),
  395                     @{ $additional_ref },
  396                 };
  397                 $id = AbsenceImport::loadHoliday($ref);
  398                 print "added holiday [$desc] with id=$id\n";
  399             }
  400         }
  401     }
  402 }
  403 
  404 sub convert_ical
  405 {
  406     my $file = shift;
  407 
  408     defined($file) || die "usage: $0 <file>\n";
  409 
  410     binmode(*STDOUT, ":utf8");
  411 
  412     my $data = AbsenceImport::parseIcal($file);
  413 
  414     if (exists($data->{'*'})) {
  415         my $fref = $data->{'*'};
  416         print "# fixed holidays\n";
  417         foreach my $month (sort { $a <=> $b } keys(%{ $fref })) {
  418             foreach my $day (sort { $a <=> $b } keys(%{ $fref->{$month} })) {
  419                 printf("%d.%d.%s\t%s\n",
  420                     $day, $month, '*', $fref->{$month}->{$day});
  421                     #$day, $month, '*', encode_utf8($fref->{$month}->{$day}));
  422             }
  423         }
  424     }
  425 
  426     print "\n# varying holidays\n";
  427     foreach my $year (sort {$a <=> $b} keys(%{ $data })) {
  428         next if ($year eq '*');
  429         print "# $year\n";
  430         foreach my $month (sort {$a <=> $b} keys(%{ $data->{$year} })) {
  431             foreach my $day (sort {$a <=> $b} keys(%{ $data->{$year}->{$month} })) {
  432                 printf("%d.%d.%s\t%s\n",
  433                     $day, $month, $year, $data->{$year}->{$month}->{$day});
  434                     #$day, $month, $year, encode_utf8($data->{$year}->{$month}->{$day}));
  435             }
  436         }
  437     }
  438 }
  439 
  440 sub usage
  441 {
  442     print <<_EOF_;
  443 usage: $0 MODE DB-OPTIONS [ IMPORT-OPTIONS | ADD-OPTIONS ]
  444 
  445 MODE is one of:             (abbreviation)
  446   -list    <holidays|countries|regions> (-l)
  447   -import       <type>      (-i) <type> can be "ical" or "legacy"
  448   -convert      <file>      (-conv)
  449   -add-region               (-ar)
  450   -add-holiday              (-ah)
  451   -delete-holiday   <list|all>  (-dh)
  452   -delete-region    <list>      (-dr)
  453 
  454     <list> consists of a list of IDs as returned by -list,
  455     comma-separated, with no white-space. exampe: 2,66,81,93
  456     for holidays list can be 'all', which deletes all holidays and
  457     resets the ID sequence to 1.
  458 
  459   modes:
  460   -import       imports a list of holidays from a file
  461             into database
  462   -convert      converts a list of holidays from an ics file to
  463             legacy format on standard output
  464   -add-region       adds a region to database
  465   -add-holiday      adds a holiday to database
  466   -list         lists holidays in database
  467   -delete-holiday   deletes a list of holidays from database
  468   -delete-region    deletes a list of regions from database
  469   
  470 
  471 DB-OPTIONS:
  472     -db-name DBNAME         (-dbn)
  473     -user    DBUSER         (-u)
  474     -pass    DBPASS         (-p)
  475     -host    DBHOST             (default: "localhost")
  476 
  477 ADD-REGION-OPTIONS:
  478     -name <name>            (-n) (mandatory)
  479     -desc <description>     (-d) (optional)
  480 
  481 ADD-HOLIDAY-OPTIONS:
  482     -date <day>         (-dt) (mandatory, format: YYYY-MM-DD)
  483     -desc <description>     (-d)  (mandatory)
  484     -country <country-id>       (-c)  (optional)
  485     -region <region-id>     (-r)  (optional)
  486 
  487 IMPORT-OPTIONS:
  488     -file <filename>        (-f) file to import from
  489     -country <country>      (-c) assign country to imported holidays
  490     -region <region>        (-r) assign region to imported holidays
  491 
  492 NOTE:
  493 -country and -region are mutally exclusive.
  494 To add a fixed holiday, use '*' instead of the four-digit year. example:
  495     -add-holiday -date '*-1-1' -desc "New Years Day"
  496 _EOF_
  497 exit;
  498 }