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
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\n";
175   print "find '$dap_folder/$acronym/' -type l -o -type d -delete\n";
176   }
177
178################################################################
179
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
199         $zip->addTree($dataset, "$acronym/$dataset");
200         }
201      elsif (-f $dataset) {
202         # File case
203         $zip->addFile($dataset, "$acronym/$dataset");
204         }
205      else {
206         # Strange case
207         print "Error: entry $dataset doesn't exists\n";
208         }
209      }
210
211   my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime time;
212   $year += 1900;
213   $mon++;
214   my $date = sprintf '%04i%02i%02i-%02i%02i', $year, $mon, $mday, $hour, $min;
215
216   # Save the Zip file
217   unless ($zip->writeToFileNamed("$current_dir/$acronym--$date.zip") == AZ_OK) {
218      die 'Error: zip write error';
219      }
220   }
221
222################################################################
223
224sub cmd_make_author {
225   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
226
227   my $current_dir = Cwd::getcwd();
228
229   my $acronym    = $meta->{'project'}{'acronym'};
230   my $authors_list = $meta->{'project'}{'authors'};
231
232   if (-f "$current_dir/AUTHORS.txt") {
233      # Test for manual or automatically generated file
234      # Automatically generated file by project-meta
235      my $automatic;
236      open my $fh, '<', "$current_dir/AUTHORS.txt" or die $!;
237      for my $line (<$fh>) {
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";
248      }
249
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
258   open my $fh,  '>', "$current_dir/AUTHORS.txt" or die $!;
259   print $fh "$msg_format\n\n";
260   close $fh;
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 - $!";
284
285   print "Info: LICENCE.txt file create\n";
286   return;
287   }
288
289################################################################
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      }
313   
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      );
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################################################################
335
336sub cmd_list_licence {
337   opendir my $dh, '/usr/share/project-meta/licence.d/' or die $!;
338   for my $licence (readdir $dh) {
339      # Keep only file
340      next if not -f "/usr/share/project-meta/licence.d/$licence";
341     
342      # Keep only .txt file
343      next if not $licence =~ m/\.txt$/;
344
345      $licence =~ s/\.txt$//;
346      print "$licence\n";
347      }
348   closedir $dh;
349   }
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
364 project-meta help
365 project-meta version
366 project-meta check
367 project-meta make-link
368 project-meta remove-link
369 project-meta make-zip
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
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.
381
382Everything is declare in the metafile F<PROJECT-META.yml>.
383This YAML file must exist in your root projet folder.
384See L</METAFILE SPECIFICATION>.
385
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
391(like for all the Project-Meta code).
392
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
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
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
415=item * C<public-dap/data-set>: a list of files or folder to push
416
417=back
418
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
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
452=head2 make-licence
453
454 project-meta make-licence
455
456Copy the licence file from the project-meta licence database at the current folder
457with the file name: F<LICENCE.txt>.
458
459The licence is defined in the F<PROJECT-META.yml> specification under the key C<public-dap/data-licence>.
460The list of possible licence is given with the command L</list-licence>.
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.
467At this time the possible licence are:
468
469 license-ouverte-v2.0
470 open-database-license-v1.0
471
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.
474
475
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
485=head1 KNOWN BUGS
486
487 - not really check keys and tags before doing action!
488
489=head1 SEE ALSO
490
491yamllint
492
493
494=head1 AUTHOR
495
496Written by Gabriel Moreau, LEGI UMR5519, CNRS, Grenoble - France
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
502or helped me in another task like having new ideas, specifications...
503Maybe I forgot your contribution in recent years,
504please forgive me in advance and send me an e-mail to correct this.
505
506Joel Sommeria, Julien Chauchat, Cyrille Bonamy, Antoine Mathieu.
507
508
509=head1 LICENSE AND COPYRIGHT
510
511Licence GNU GPL version 2 or later and Perl equivalent
512
513Copyright (C) 2017-2018 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>.
Note: See TracBrowser for help on using the repository browser.