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

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