source: trunk/project-meta/project-meta @ 199

Last change on this file since 199 was 199, checked in by g7moreau, 6 years ago
  • Change version
  • Property svn:executable set to *
File size: 14.7 KB
RevLine 
[150]1#!/usr/bin/env perl
2#
[188]3# 2018/01/17 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>
[155]4#
[179]5# apt-get install yamllint libyaml-syck-perl libtemplate-perl libarchive-zip-perl
[150]6
7use strict;
8use warnings;
[199]9use version; our $VERSION = version->declare('0.0.5');
[150]10
[196]11use File::Copy qw(copy);   
[150]12use YAML::Syck;
13use Getopt::Long();
14use Cwd();
[154]15use Template;
[196]16use Archive::Zip qw(:ERROR_CODES :CONSTANTS);
[150]17
[154]18
[150]19my ($verbose);
20Getopt::Long::GetOptions(
21   'verbose' => \$verbose,
22   );
23
24
25my %CMD_DB = (
26   'help'            => \&cmd_help,
27   'version'         => \&cmd_version,
28   'check'           => \&cmd_check,
29   'make-link'       => \&cmd_make_link,
[193]30   'remove-link'     => \&cmd_remove_link,
[178]31   'make-zip'        => \&cmd_make_zip,
[153]32   'make-author'     => \&cmd_make_author,
33   'make-licence'    => \&cmd_make_licence,
[158]34   'make-copyright'  => \&cmd_make_copyright,
[161]35   'list-licence'    => \&cmd_list_licence,
[150]36   );
37
38################################################################
39# main program
40################################################################
41
42my $cmd = shift @ARGV || 'help';
43if (defined $CMD_DB{$cmd}) {
44   $CMD_DB{$cmd}->(@ARGV);
45   }
46else {
47   print {*STDERR} "project-meta: command $cmd not found\n\n";
48   $CMD_DB{'help'}->();
49   exit 1;
50   }
51
52exit;
53
54################################################################
55# subroutine
56################################################################
57
[188]58sub print_ok {
59   my ($key, $test) = @_;
60   
61   printf "%-35s : %s\n", $key, $test ? 'yes' : 'no';
62   }
63
[150]64################################################################
[188]65
66sub addfolder2list {
67   my ($folderdb, $folder) = @_;
68   
[198]69   return if $folder !~ m{/};
70   
[188]71   $folder =~ s{/[^/]+$}{};
72
73   $folderdb->{$folder}++;
74   return addfolder2list($folderdb, $folder);
75   }
76
77################################################################
[150]78# command
79################################################################
80
81sub cmd_help {
[164]82   print <<'END';
83project-meta - opendata project metafile manager
[150]84
[177]85 project-meta help
86 project-meta version
[168]87 project-meta check
[164]88 project-meta make-link
[192]89 project-meta remove-link
[186]90 project-meta make-zip
[164]91 project-meta make-author
92 project-meta make-licence
93 project-meta make-copyright
94 project-meta make-licence
95 project-meta list-licence
[150]96END
97   }
98
99################################################################
100
101sub cmd_version {
[196]102   print "$VERSION\n";
[150]103   }
104
105################################################################
106
107sub cmd_check {
108   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
109
110   my $acronym     = $meta->{'project'}{'acronym'};
[154]111   my $current_dir = Cwd::getcwd();
[150]112   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
113
[192]114   print_ok 'project/acronym',                  $acronym =~ m{\d\d\w[\w\d_/]+};
[150]115   print_ok 'public-dap/dap-folder',            $dap_folder ne '' and $dap_folder =~ m{^/};
116   print_ok 'dap-folder not match current_dir', $dap_folder !~ m{$current_dir};
117
118   #print YAML::Syck::Dump($meta);
119   }
120
121################################################################
122
123sub cmd_make_link {
124   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
[154]125   my $current_dir = Cwd::getcwd();
[175]126   my $acronym     = $meta->{'project'}{'acronym'};
127   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
128   my $data_set    = $meta->{'public-dap'}{'data-set'};
[150]129
[175]130   push @{$data_set}, 'AUTHORS.txt', 'COPYRIGHT.txt', 'LICENCE.txt';
[174]131   {
132      # Remove doublon
133      my %seen = ();
[175]134      @{$data_set} = grep { ! $seen{$_}++ } @{$data_set};
[174]135      }
136
[150]137   # Create a list of the folder
138   my %folders;
[175]139   for my $dataset (@{$data_set}) {
[150]140      addfolder2list(\%folders, $dataset);
141      }
142
[191]143   print "chmod o+rX,o-w '$current_dir'\n";
144   print "mkdir -p '$dap_folder/$acronym'\n" if not -d "$dap_folder/$acronym";
[150]145   for my $folder (sort keys %folders) {
[191]146      print "chmod o+rX,o-w '$current_dir/$folder'\n";
[198]147      print "mkdir '$dap_folder/$acronym/$folder'\n" if -d "$current_dir/$folder";
[150]148      }
149
[175]150   for my $dataset (@{$data_set}) {
[169]151      if ($dataset =~ m{/}) {
[198]152         # sub-folder case
[169]153         my $folder = $dataset =~ s{/[^/]+$}{}r;
[198]154         print "chmod -R o+rX,o-w '$current_dir/$dataset'\n";
[191]155         print "ln --symbolic --target-directory '$dap_folder/$acronym/$folder/' '$current_dir/$dataset'\n";
[169]156         }
157      else {
[198]158         # Root case
[191]159         print "ln --symbolic --target-directory '$dap_folder/$acronym/' '$current_dir/$dataset'\n";
[169]160         }
[150]161
[171]162      }
[191]163   print "chmod -R o+rX,o-w '$dap_folder/$acronym/'\n";
[150]164   }
165
166################################################################
167
[192]168sub cmd_remove_link {
169   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
170   my $current_dir = Cwd::getcwd();
171   my $acronym     = $meta->{'project'}{'acronym'};
172   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
173
174   die "Error: DAP folder match current folder" if $dap_folder =~ m{$current_dir} or $current_dir =~ m{$dap_folder};
175
[194]176   print "find '$dap_folder/$acronym/' -type l -o -type d -exec ls -l {} \+\n";
177   print "find '$dap_folder/$acronym/' -type l -delete\n";
178   print "find '$dap_folder/$acronym/' -type d -delete\n";
[192]179   }
180
181################################################################
182
[178]183sub cmd_make_zip {
184   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
185   my $current_dir = Cwd::getcwd();
186   my $data_set    = $meta->{'public-dap'}{'data-set'};
187   my $acronym     = $meta->{'project'}{'acronym'};
188
189   push @{$data_set}, 'AUTHORS.txt', 'COPYRIGHT.txt', 'LICENCE.txt';
190   {
191      # Remove doublon
192      my %seen = ();
193      @{$data_set} = grep { ! $seen{$_}++ } @{$data_set};
194      }
195
196   # Create a Zip file
197   my $zip = Archive::Zip->new();
198
199   for my $dataset (@{$data_set}) {
200      if (-d $dataset) {
201         # Folder case
[182]202         $zip->addTree($dataset, "$acronym/$dataset");
[178]203         }
204      elsif (-f $dataset) {
205         # File case
[183]206         $zip->addFile($dataset, "$acronym/$dataset");
[178]207         }
208      else {
[179]209         # Strange case
[178]210         print "Error: entry $dataset doesn't exists\n";
211         }
212      }
213
[181]214   my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime time;
[180]215   $year += 1900;
216   $mon++;
217   my $date = sprintf '%04i%02i%02i-%02i%02i', $year, $mon, $mday, $hour, $min;
218
[178]219   # Save the Zip file
[180]220   unless ($zip->writeToFileNamed("$current_dir/$acronym--$date.zip") == AZ_OK) {
[178]221      die 'Error: zip write error';
222      }
223   }
224
225################################################################
226
[150]227sub cmd_make_author {
228   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
[154]229
230   my $current_dir = Cwd::getcwd();
[155]231
[154]232   my $acronym    = $meta->{'project'}{'acronym'};
233   my $authors_list = $meta->{'project'}{'authors'};
[155]234
235   if (-f "$current_dir/AUTHORS.txt") {
[156]236      # Test for manual or automatically generated file
237      # Automatically generated file by project-meta
238      my $automatic;
[157]239      open my $fh, '<', "$current_dir/AUTHORS.txt" or die $!;
240      for my $line (<$fh>) {
[156]241         $line =~ m/Automatically generated .* project-meta/i and $automatic++;
242         }
243      close $fh;
244
245      if (not $automatic) {
246         print "Warning: AUTHORS.txt already exists\n";
247         return;
248         }
249
250      print "Warning: update AUTHORS.txt\n";
[155]251      }
252
[154]253   my $tt = Template->new(INCLUDE_PATH => '/usr/share/project-meta/template.d');
254   my $msg_format = '';
255   $tt->process('AUTHORS.tt',
256      {
257         acronym    => $acronym,
258         authorlist => $authors_list,
259      }, \$msg_format) || die $tt->error;
260
[155]261   open my $fh,  '>', "$current_dir/AUTHORS.txt" or die $!;
262   print $fh "$msg_format\n\n";
263   close $fh;
[150]264   }
265
266################################################################
267
268sub cmd_make_licence {
269   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
270
271   my $current_dir = Cwd::getcwd();
272
273   if (-f "$current_dir/LICENCE.txt") {
274      print "Warning: LICENCE.txt already exists\n";
275      return;
276      }
277
278   my $licence = $meta->{'public-dap'}{'data-licence'};
279
280   if (not -f "/usr/share/project-meta/licence.d/$licence.txt") {
281      print "Error: licence $licence doesn't exists in project-meta database\n";
282      exit 1;
283      }
284
285   copy("/usr/share/project-meta/licence.d/$licence.txt", "$current_dir/LICENCE.txt")
286      or die "Error: licence copy failed - $!";
[154]287
288   print "Info: LICENCE.txt file create\n";
[150]289   return;
290   }
291
292################################################################
[158]293
294sub cmd_make_copyright {
295   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
296
297   my $current_dir = Cwd::getcwd();
298
299   if (-f "$current_dir/COPYRIGHT.txt") {
300      # Test for manual or automatically generated file
301      # Automatically generated file by project-meta
302      my $automatic;
303      open my $fh, '<', "$current_dir/COPYRIGHT.txt" or die $!;
304      for my $line (<$fh>) {
305         $line =~ m/Automatically generated .* project-meta/i and $automatic++;
306         }
307      close $fh;
308
309      if (not $automatic) {
310         print "Warning: COPYRIGHT.txt already exists\n";
311         return;
312         }
313
314      print "Warning: update COPYRIGHT.txt\n";
315      }
[171]316   
[176]317   my $tt = Template->new(
318      INCLUDE_PATH   => '/usr/share/project-meta/template.d',
319      POST_CHOMP     => 1, # Remove space and carriage return after %]
320      );
[158]321   my $msg_format = '';
322   $tt->process('COPYRIGHT.tt',
323      {
324         title       => $meta->{'project'}{'title'},
325         acronym     => $meta->{'project'}{'acronym'},
326         authorlist  => $meta->{'project'}{'authors'},
327         description => $meta->{'project'}{'short-description'},
328         licence     => $meta->{'public-dap'}{'data-licence'},
329         doi         => $meta->{'publication'}{'doi'},
330      }, \$msg_format) || die $tt->error;
331
332   open my $fh,  '>', "$current_dir/COPYRIGHT.txt" or die $!;
333   print $fh "$msg_format\n\n";
334   close $fh;
335   }
336
337################################################################
[161]338
339sub cmd_list_licence {
340   opendir my $dh, '/usr/share/project-meta/licence.d/' or die $!;
341   for my $licence (readdir $dh) {
[163]342      # Keep only file
343      next if not -f "/usr/share/project-meta/licence.d/$licence";
[162]344     
345      # Keep only .txt file
346      next if not $licence =~ m/\.txt$/;
347
[163]348      $licence =~ s/\.txt$//;
[161]349      print "$licence\n";
350      }
351   closedir $dh;
352   }
[164]353
354################################################################
355# documentation
356################################################################
357
358__END__
359
360=head1 NAME
361
362project-meta - opendata project metafile manager
363
364
365=head1 USAGE
366
[177]367 project-meta help
368 project-meta version
[168]369 project-meta check
[164]370 project-meta make-link
[192]371 project-meta remove-link
[186]372 project-meta make-zip
[164]373 project-meta make-author
374 project-meta make-licence
375 project-meta make-copyright
376 project-meta make-licence
377 project-meta list-licence
378
379=head1 DESCRIPTION
380
[188]381Project-Meta is a small tool to maintain a set of open data files.
382In order to help you in this task, C<project-meta> command has a set of action
383to generated and maintain many files in your dataset.
[166]384
[186]385Everything is declare in the metafile F<PROJECT-META.yml>.
386This YAML file must exist in your root projet folder.
[188]387See L</METAFILE SPECIFICATION>.
[186]388
[164]389=head1 COMMANDS
390
391Some command are defined in the source code but are not documented here.
392Theses could be not well defined, not finished, not well tested...
393You can read the source code and use them at your own risk
[188]394(like for all the Project-Meta code).
[164]395
[168]396=head2 check
397
398 project-meta check
399
400Check your F<PROJECT-META.yml> has the good key.
401If your metafile is not a valid YAML file,
402you can use C<yamllint> command to check just it's format.
403
[189]404=head2 make-link
405
406 project-meta make-link
407
408Create UNIX soft links on the OpeNDAP folder to the real data.
409Files F<AUTHORS.txt>, F<LICENCE.txt> and F<COPYRIGHT.txt> are mandatory but could be generated (see below).
410The main keys use in the F<PROJECT-META.yml> are:
411
412=over
413
[190]414=item * C<project/acronym>: the project short acronym, add to the OpeNDAP root folder
415
416=item * C<public-dap/dap-folder>: the OpeNDAP root folder
417
[189]418=item * C<public-dap/data-set>: a list of files or folder to push
419
420=back
421
[192]422Because this command could be dangerous, it does nothing!
423It print on terminal shell command to be done.
424You have to verify ouput before eval it.
425
426 project-meta make-link
427 project-meta make-link | bash
428
429=head2 remove-link
430
431 project-meta remove-link
432
433Remove link in OpeNDAP folder for that projet.
434Because command C<rm> is always dangerous,
435we use here the command C<find> limited to folder and link.
436
437Please verify the returned values before excuted it with the C<-delete> option.
438
[190]439=head2 make-zip
440
441 project-meta make-zip
442
443Create a ZIP archive with the open data set.
444Files F<AUTHORS.txt>, F<LICENCE.txt> and F<COPYRIGHT.txt> are mandatory but could be generated (see below).
445The main keys use in the F<PROJECT-META.yml> are:
446
447=over
448
449=item * C<project/acronym>: the project short acronym, use as root folder
450
451=item * C<public-dap/data-set>: a list of files or folder to push
452
453=back
454
[164]455=head2 make-licence
456
457 project-meta make-licence
458
[189]459Copy the licence file from the project-meta licence database at the current folder
460with the file name: F<LICENCE.txt>.
[164]461
[189]462The licence is defined in the F<PROJECT-META.yml> specification under the key C<public-dap/data-licence>.
[188]463The list of possible licence is given with the command L</list-licence>.
[164]464
465=head2 list-licence
466
467 project-meta list-licence
468
469Give the list of all the open data licence supported by the project-meta licence database.
[167]470At this time the possible licence are:
[164]471
[167]472 license-ouverte-v2.0
473 open-database-license-v1.0
474
[186]475Note that these licences are dedicated to open data.
476Please do not use open licence that have been written for code or documentation for data.
[167]477
[195]478=head2 make-author
[186]479
[195]480 project-meta make-author
481
482Create or update the F<AUTHORS.txt> file at the current folder.
483Authors data are extracted from the C<PROJECT-META.yml> file.
484
485=head2 make-copyright
486
487 project-meta make-copyright
488
489Create or update the F<COPYRIGHT.txt> file at the current folder.
490Authors, licence and copyright data are extracted from the C<PROJECT-META.yml> file.
491
492
[167]493=head1 METAFILE SPECIFICATION
494
495Each project must have an open data metafile which describe the project : C<PROJECT-META.yml>.
496The file is in YAML format because this is a human readable style of text file.
497
498You can find in the project-meta software a C<PROJECT-META.sample.yml> example.
499This one is actually the master reference specification!
500
501
[188]502=head1 KNOWN BUGS
[171]503
[188]504 - not really check keys and tags before doing action!
[171]505
[168]506=head1 SEE ALSO
507
508yamllint
509
510
[164]511=head1 AUTHOR
512
[168]513Written by Gabriel Moreau, LEGI UMR5519, CNRS, Grenoble - France
[164]514
515=head1 SPECIAL THANKS
516
517The list of people below did not directly contribute to project-meta's source code
518but provided me with some data, returned bugs
[189]519or helped me in another task like having new ideas, specifications...
[164]520Maybe I forgot your contribution in recent years,
521please forgive me in advance and send me an e-mail to correct this.
522
[168]523Joel Sommeria, Julien Chauchat, Cyrille Bonamy, Antoine Mathieu.
[164]524
525
526=head1 LICENSE AND COPYRIGHT
527
528Licence GNU GPL version 2 or later and Perl equivalent
529
[165]530Copyright (C) 2017-2018 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>.
Note: See TracBrowser for help on using the repository browser.