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

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