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

Last change on this file since 194 was 194, checked in by g7moreau, 6 years ago
  • Remove in two find command: link and only after dir
  • Property svn:executable set to *
File size: 14.3 KB
Line 
1#!/usr/bin/env perl
2#
3# 2018/01/17 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>
4#
5# apt-get install yamllint libyaml-syck-perl libtemplate-perl libarchive-zip-perl
6
7use strict;
8use warnings;
9
10use File::Copy qw{copy};   
11use YAML::Syck;
12use Getopt::Long();
13use Cwd();
14use Template;
15use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
16
17
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,
29   'remove-link'     => \&cmd_remove_link,
30   'make-zip'        => \&cmd_make_zip,
31   'make-author'     => \&cmd_make_author,
32   'make-licence'    => \&cmd_make_licence,
33   'make-copyright'  => \&cmd_make_copyright,
34   'list-licence'    => \&cmd_list_licence,
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
57sub print_ok {
58   my ($key, $test) = @_;
59   
60   printf "%-35s : %s\n", $key, $test ? 'yes' : 'no';
61   }
62
63################################################################
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################################################################
77# command
78################################################################
79
80sub cmd_help {
81   print <<'END';
82project-meta - opendata project metafile manager
83
84 project-meta help
85 project-meta version
86 project-meta check
87 project-meta make-link
88 project-meta remove-link
89 project-meta make-zip
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
95END
96   }
97
98################################################################
99
100sub cmd_version {
101   print "0.0.2\n";
102   }
103
104################################################################
105
106sub cmd_check {
107   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
108
109   my $acronym     = $meta->{'project'}{'acronym'};
110   my $current_dir = Cwd::getcwd();
111   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
112
113   print_ok 'project/acronym',                  $acronym =~ m{\d\d\w[\w\d_/]+};
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");
124   my $current_dir = Cwd::getcwd();
125   my $acronym     = $meta->{'project'}{'acronym'};
126   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
127   my $data_set    = $meta->{'public-dap'}{'data-set'};
128
129   push @{$data_set}, 'AUTHORS.txt', 'COPYRIGHT.txt', 'LICENCE.txt';
130   {
131      # Remove doublon
132      my %seen = ();
133      @{$data_set} = grep { ! $seen{$_}++ } @{$data_set};
134      }
135
136   # Create a list of the folder
137   my %folders;
138   for my $dataset (@{$data_set}) {
139      addfolder2list(\%folders, $dataset);
140      }
141
142   print "chmod o+rX,o-w '$current_dir'\n";
143   print "mkdir -p '$dap_folder/$acronym'\n" if not -d "$dap_folder/$acronym";
144   for my $folder (sort keys %folders) {
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";
147      }
148
149   for my $dataset (@{$data_set}) {
150      if ($dataset =~ m{/}) {
151         # Folder case
152         my $folder = $dataset =~ s{/[^/]+$}{}r;
153         print "ln --symbolic --target-directory '$dap_folder/$acronym/$folder/' '$current_dir/$dataset'\n";
154         }
155      else {
156         # File case
157         print "ln --symbolic --target-directory '$dap_folder/$acronym/' '$current_dir/$dataset'\n";
158         }
159
160      }
161   print "chmod -R o+rX,o-w '$dap_folder/$acronym/'\n";
162   }
163
164################################################################
165
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 -exec ls -l {} \+\n";
175   print "find '$dap_folder/$acronym/' -type l -delete\n";
176   print "find '$dap_folder/$acronym/' -type d -delete\n";
177   }
178
179################################################################
180
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
200         $zip->addTree($dataset, "$acronym/$dataset");
201         }
202      elsif (-f $dataset) {
203         # File case
204         $zip->addFile($dataset, "$acronym/$dataset");
205         }
206      else {
207         # Strange case
208         print "Error: entry $dataset doesn't exists\n";
209         }
210      }
211
212   my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime time;
213   $year += 1900;
214   $mon++;
215   my $date = sprintf '%04i%02i%02i-%02i%02i', $year, $mon, $mday, $hour, $min;
216
217   # Save the Zip file
218   unless ($zip->writeToFileNamed("$current_dir/$acronym--$date.zip") == AZ_OK) {
219      die 'Error: zip write error';
220      }
221   }
222
223################################################################
224
225sub cmd_make_author {
226   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
227
228   my $current_dir = Cwd::getcwd();
229
230   my $acronym    = $meta->{'project'}{'acronym'};
231   my $authors_list = $meta->{'project'}{'authors'};
232
233   if (-f "$current_dir/AUTHORS.txt") {
234      # Test for manual or automatically generated file
235      # Automatically generated file by project-meta
236      my $automatic;
237      open my $fh, '<', "$current_dir/AUTHORS.txt" or die $!;
238      for my $line (<$fh>) {
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";
249      }
250
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
259   open my $fh,  '>', "$current_dir/AUTHORS.txt" or die $!;
260   print $fh "$msg_format\n\n";
261   close $fh;
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 - $!";
285
286   print "Info: LICENCE.txt file create\n";
287   return;
288   }
289
290################################################################
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      }
314   
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      );
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################################################################
336
337sub cmd_list_licence {
338   opendir my $dh, '/usr/share/project-meta/licence.d/' or die $!;
339   for my $licence (readdir $dh) {
340      # Keep only file
341      next if not -f "/usr/share/project-meta/licence.d/$licence";
342     
343      # Keep only .txt file
344      next if not $licence =~ m/\.txt$/;
345
346      $licence =~ s/\.txt$//;
347      print "$licence\n";
348      }
349   closedir $dh;
350   }
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
365 project-meta help
366 project-meta version
367 project-meta check
368 project-meta make-link
369 project-meta remove-link
370 project-meta make-zip
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
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.
382
383Everything is declare in the metafile F<PROJECT-META.yml>.
384This YAML file must exist in your root projet folder.
385See L</METAFILE SPECIFICATION>.
386
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
392(like for all the Project-Meta code).
393
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
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
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
416=item * C<public-dap/data-set>: a list of files or folder to push
417
418=back
419
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
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
453=head2 make-licence
454
455 project-meta make-licence
456
457Copy the licence file from the project-meta licence database at the current folder
458with the file name: F<LICENCE.txt>.
459
460The licence is defined in the F<PROJECT-META.yml> specification under the key C<public-dap/data-licence>.
461The list of possible licence is given with the command L</list-licence>.
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.
468At this time the possible licence are:
469
470 license-ouverte-v2.0
471 open-database-license-v1.0
472
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.
475
476
477=head1 METAFILE SPECIFICATION
478
479Each project must have an open data metafile which describe the project : C<PROJECT-META.yml>.
480The file is in YAML format because this is a human readable style of text file.
481
482You can find in the project-meta software a C<PROJECT-META.sample.yml> example.
483This one is actually the master reference specification!
484
485
486=head1 KNOWN BUGS
487
488 - not really check keys and tags before doing action!
489
490=head1 SEE ALSO
491
492yamllint
493
494
495=head1 AUTHOR
496
497Written by Gabriel Moreau, LEGI UMR5519, CNRS, Grenoble - France
498
499=head1 SPECIAL THANKS
500
501The list of people below did not directly contribute to project-meta's source code
502but provided me with some data, returned bugs
503or helped me in another task like having new ideas, specifications...
504Maybe I forgot your contribution in recent years,
505please forgive me in advance and send me an e-mail to correct this.
506
507Joel Sommeria, Julien Chauchat, Cyrille Bonamy, Antoine Mathieu.
508
509
510=head1 LICENSE AND COPYRIGHT
511
512Licence GNU GPL version 2 or later and Perl equivalent
513
514Copyright (C) 2017-2018 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>.
Note: See TracBrowser for help on using the repository browser.