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

Last change on this file since 193 was 193, checked in by g7moreau, 6 years ago
  • Forget a , in command list
  • Property svn:executable set to *
File size: 14.2 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
174   print "find '$dap_folder/$acronym/' -type l -o -type d\n";
175   print "find '$dap_folder/$acronym/' -type l -o -type d -delete\n";
176   }
177
178################################################################
179
[178]180sub cmd_make_zip {
181   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
182   my $current_dir = Cwd::getcwd();
183   my $data_set    = $meta->{'public-dap'}{'data-set'};
184   my $acronym     = $meta->{'project'}{'acronym'};
185
186   push @{$data_set}, 'AUTHORS.txt', 'COPYRIGHT.txt', 'LICENCE.txt';
187   {
188      # Remove doublon
189      my %seen = ();
190      @{$data_set} = grep { ! $seen{$_}++ } @{$data_set};
191      }
192
193   # Create a Zip file
194   my $zip = Archive::Zip->new();
195
196   for my $dataset (@{$data_set}) {
197      if (-d $dataset) {
198         # Folder case
[182]199         $zip->addTree($dataset, "$acronym/$dataset");
[178]200         }
201      elsif (-f $dataset) {
202         # File case
[183]203         $zip->addFile($dataset, "$acronym/$dataset");
[178]204         }
205      else {
[179]206         # Strange case
[178]207         print "Error: entry $dataset doesn't exists\n";
208         }
209      }
210
[181]211   my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime time;
[180]212   $year += 1900;
213   $mon++;
214   my $date = sprintf '%04i%02i%02i-%02i%02i', $year, $mon, $mday, $hour, $min;
215
[178]216   # Save the Zip file
[180]217   unless ($zip->writeToFileNamed("$current_dir/$acronym--$date.zip") == AZ_OK) {
[178]218      die 'Error: zip write error';
219      }
220   }
221
222################################################################
223
[150]224sub cmd_make_author {
225   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
[154]226
227   my $current_dir = Cwd::getcwd();
[155]228
[154]229   my $acronym    = $meta->{'project'}{'acronym'};
230   my $authors_list = $meta->{'project'}{'authors'};
[155]231
232   if (-f "$current_dir/AUTHORS.txt") {
[156]233      # Test for manual or automatically generated file
234      # Automatically generated file by project-meta
235      my $automatic;
[157]236      open my $fh, '<', "$current_dir/AUTHORS.txt" or die $!;
237      for my $line (<$fh>) {
[156]238         $line =~ m/Automatically generated .* project-meta/i and $automatic++;
239         }
240      close $fh;
241
242      if (not $automatic) {
243         print "Warning: AUTHORS.txt already exists\n";
244         return;
245         }
246
247      print "Warning: update AUTHORS.txt\n";
[155]248      }
249
[154]250   my $tt = Template->new(INCLUDE_PATH => '/usr/share/project-meta/template.d');
251   my $msg_format = '';
252   $tt->process('AUTHORS.tt',
253      {
254         acronym    => $acronym,
255         authorlist => $authors_list,
256      }, \$msg_format) || die $tt->error;
257
[155]258   open my $fh,  '>', "$current_dir/AUTHORS.txt" or die $!;
259   print $fh "$msg_format\n\n";
260   close $fh;
[150]261   }
262
263################################################################
264
265sub cmd_make_licence {
266   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
267
268   my $current_dir = Cwd::getcwd();
269
270   if (-f "$current_dir/LICENCE.txt") {
271      print "Warning: LICENCE.txt already exists\n";
272      return;
273      }
274
275   my $licence = $meta->{'public-dap'}{'data-licence'};
276
277   if (not -f "/usr/share/project-meta/licence.d/$licence.txt") {
278      print "Error: licence $licence doesn't exists in project-meta database\n";
279      exit 1;
280      }
281
282   copy("/usr/share/project-meta/licence.d/$licence.txt", "$current_dir/LICENCE.txt")
283      or die "Error: licence copy failed - $!";
[154]284
285   print "Info: LICENCE.txt file create\n";
[150]286   return;
287   }
288
289################################################################
[158]290
291sub cmd_make_copyright {
292   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
293
294   my $current_dir = Cwd::getcwd();
295
296   if (-f "$current_dir/COPYRIGHT.txt") {
297      # Test for manual or automatically generated file
298      # Automatically generated file by project-meta
299      my $automatic;
300      open my $fh, '<', "$current_dir/COPYRIGHT.txt" or die $!;
301      for my $line (<$fh>) {
302         $line =~ m/Automatically generated .* project-meta/i and $automatic++;
303         }
304      close $fh;
305
306      if (not $automatic) {
307         print "Warning: COPYRIGHT.txt already exists\n";
308         return;
309         }
310
311      print "Warning: update COPYRIGHT.txt\n";
312      }
[171]313   
[176]314   my $tt = Template->new(
315      INCLUDE_PATH   => '/usr/share/project-meta/template.d',
316      POST_CHOMP     => 1, # Remove space and carriage return after %]
317      );
[158]318   my $msg_format = '';
319   $tt->process('COPYRIGHT.tt',
320      {
321         title       => $meta->{'project'}{'title'},
322         acronym     => $meta->{'project'}{'acronym'},
323         authorlist  => $meta->{'project'}{'authors'},
324         description => $meta->{'project'}{'short-description'},
325         licence     => $meta->{'public-dap'}{'data-licence'},
326         doi         => $meta->{'publication'}{'doi'},
327      }, \$msg_format) || die $tt->error;
328
329   open my $fh,  '>', "$current_dir/COPYRIGHT.txt" or die $!;
330   print $fh "$msg_format\n\n";
331   close $fh;
332   }
333
334################################################################
[161]335
336sub cmd_list_licence {
337   opendir my $dh, '/usr/share/project-meta/licence.d/' or die $!;
338   for my $licence (readdir $dh) {
[163]339      # Keep only file
340      next if not -f "/usr/share/project-meta/licence.d/$licence";
[162]341     
342      # Keep only .txt file
343      next if not $licence =~ m/\.txt$/;
344
[163]345      $licence =~ s/\.txt$//;
[161]346      print "$licence\n";
347      }
348   closedir $dh;
349   }
[164]350
351################################################################
352# documentation
353################################################################
354
355__END__
356
357=head1 NAME
358
359project-meta - opendata project metafile manager
360
361
362=head1 USAGE
363
[177]364 project-meta help
365 project-meta version
[168]366 project-meta check
[164]367 project-meta make-link
[192]368 project-meta remove-link
[186]369 project-meta make-zip
[164]370 project-meta make-author
371 project-meta make-licence
372 project-meta make-copyright
373 project-meta make-licence
374 project-meta list-licence
375
376=head1 DESCRIPTION
377
[188]378Project-Meta is a small tool to maintain a set of open data files.
379In order to help you in this task, C<project-meta> command has a set of action
380to generated and maintain many files in your dataset.
[166]381
[186]382Everything is declare in the metafile F<PROJECT-META.yml>.
383This YAML file must exist in your root projet folder.
[188]384See L</METAFILE SPECIFICATION>.
[186]385
[164]386=head1 COMMANDS
387
388Some command are defined in the source code but are not documented here.
389Theses could be not well defined, not finished, not well tested...
390You can read the source code and use them at your own risk
[188]391(like for all the Project-Meta code).
[164]392
[168]393=head2 check
394
395 project-meta check
396
397Check your F<PROJECT-META.yml> has the good key.
398If your metafile is not a valid YAML file,
399you can use C<yamllint> command to check just it's format.
400
[189]401=head2 make-link
402
403 project-meta make-link
404
405Create UNIX soft links on the OpeNDAP folder to the real data.
406Files F<AUTHORS.txt>, F<LICENCE.txt> and F<COPYRIGHT.txt> are mandatory but could be generated (see below).
407The main keys use in the F<PROJECT-META.yml> are:
408
409=over
410
[190]411=item * C<project/acronym>: the project short acronym, add to the OpeNDAP root folder
412
413=item * C<public-dap/dap-folder>: the OpeNDAP root folder
414
[189]415=item * C<public-dap/data-set>: a list of files or folder to push
416
417=back
418
[192]419Because this command could be dangerous, it does nothing!
420It print on terminal shell command to be done.
421You have to verify ouput before eval it.
422
423 project-meta make-link
424 project-meta make-link | bash
425
426=head2 remove-link
427
428 project-meta remove-link
429
430Remove link in OpeNDAP folder for that projet.
431Because command C<rm> is always dangerous,
432we use here the command C<find> limited to folder and link.
433
434Please verify the returned values before excuted it with the C<-delete> option.
435
[190]436=head2 make-zip
437
438 project-meta make-zip
439
440Create a ZIP archive with the open data set.
441Files F<AUTHORS.txt>, F<LICENCE.txt> and F<COPYRIGHT.txt> are mandatory but could be generated (see below).
442The main keys use in the F<PROJECT-META.yml> are:
443
444=over
445
446=item * C<project/acronym>: the project short acronym, use as root folder
447
448=item * C<public-dap/data-set>: a list of files or folder to push
449
450=back
451
[164]452=head2 make-licence
453
454 project-meta make-licence
455
[189]456Copy the licence file from the project-meta licence database at the current folder
457with the file name: F<LICENCE.txt>.
[164]458
[189]459The licence is defined in the F<PROJECT-META.yml> specification under the key C<public-dap/data-licence>.
[188]460The list of possible licence is given with the command L</list-licence>.
[164]461
462=head2 list-licence
463
464 project-meta list-licence
465
466Give the list of all the open data licence supported by the project-meta licence database.
[167]467At this time the possible licence are:
[164]468
[167]469 license-ouverte-v2.0
470 open-database-license-v1.0
471
[186]472Note that these licences are dedicated to open data.
473Please do not use open licence that have been written for code or documentation for data.
[167]474
[186]475
[167]476=head1 METAFILE SPECIFICATION
477
478Each project must have an open data metafile which describe the project : C<PROJECT-META.yml>.
479The file is in YAML format because this is a human readable style of text file.
480
481You can find in the project-meta software a C<PROJECT-META.sample.yml> example.
482This one is actually the master reference specification!
483
484
[188]485=head1 KNOWN BUGS
[171]486
[188]487 - not really check keys and tags before doing action!
[171]488
[168]489=head1 SEE ALSO
490
491yamllint
492
493
[164]494=head1 AUTHOR
495
[168]496Written by Gabriel Moreau, LEGI UMR5519, CNRS, Grenoble - France
[164]497
498=head1 SPECIAL THANKS
499
500The list of people below did not directly contribute to project-meta's source code
501but provided me with some data, returned bugs
[189]502or helped me in another task like having new ideas, specifications...
[164]503Maybe I forgot your contribution in recent years,
504please forgive me in advance and send me an e-mail to correct this.
505
[168]506Joel Sommeria, Julien Chauchat, Cyrille Bonamy, Antoine Mathieu.
[164]507
508
509=head1 LICENSE AND COPYRIGHT
510
511Licence GNU GPL version 2 or later and Perl equivalent
512
[165]513Copyright (C) 2017-2018 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>.
Note: See TracBrowser for help on using the repository browser.