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
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 libyaml-syck-perl libtemplate-perl libarchive-zip-perl
6# apt-get install yamllint libyaml-shell-perl # check YAML files
7
8use strict;
9use warnings;
10use version; our $VERSION = version->declare('0.0.8');
11
12use File::Copy qw(copy);   
13use YAML::Syck;
14use Getopt::Long();
15use Cwd();
16use Template;
17use Archive::Zip qw(:ERROR_CODES :CONSTANTS);
18
19
20my ($verbose);
21Getopt::Long::GetOptions(
22   'verbose' => \$verbose,
23   );
24
25
26my %CMD_DB = (
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,
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
60sub print_ok {
61   my ($key, $test) = @_;
62   
63   printf "%-35s : %s\n", $key, $test ? 'yes' : 'no';
64   }
65
66################################################################
67
68sub addfolder2list {
69   my ($folderdb, $folder) = @_;
70   
71   return if $folder !~ m{/};
72   
73   $folder =~ s{/[^/]+$}{};
74
75   $folderdb->{$folder}++;
76   return addfolder2list($folderdb, $folder);
77   }
78
79################################################################
80# command
81################################################################
82
83sub cmd_help {
84   print <<'END';
85project-meta - opendata project metafile manager
86
87 project-meta help
88 project-meta version
89 project-meta check
90 project-meta make-link
91 project-meta remove-link
92 project-meta make-zip
93 project-meta make-allfiles
94 project-meta list-license
95 project-meta make-file-license
96 project-meta make-file-author
97 project-meta make-file-copyright
98END
99   }
100
101################################################################
102
103sub cmd_version {
104   print "$VERSION\n";
105   }
106
107################################################################
108
109sub cmd_check {
110   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
111
112   my $acronym     = $meta->{'project'}{'acronym'};
113   my $current_dir = Cwd::getcwd();
114   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
115
116   print_ok 'project/acronym',                  $acronym =~ m{\d\d\w[\w\d_/]+};
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");
127   my $current_dir = Cwd::getcwd();
128   my $acronym     = $meta->{'project'}{'acronym'};
129   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
130   my $data_set    = $meta->{'public-dap'}{'data-set'};
131
132   push @{$data_set}, 'AUTHORS.txt', 'COPYRIGHT.txt', 'LICENSE.txt';
133   {
134      # Remove doublon
135      my %seen = ();
136      @{$data_set} = grep { ! $seen{$_}++ } @{$data_set};
137      }
138
139   # Create a list of the folder
140   my %folders;
141   for my $dataset (@{$data_set}) {
142      addfolder2list(\%folders, $dataset);
143      }
144
145   print "chmod o+rX,o-w '$current_dir'\n";
146   print "mkdir -p '$dap_folder/$acronym'\n" if not -d "$dap_folder/$acronym";
147   for my $folder (sort keys %folders) {
148      print "chmod o+rX,o-w '$current_dir/$folder'\n";
149      print "mkdir '$dap_folder/$acronym/$folder'\n" if -d "$current_dir/$folder";
150      }
151
152   for my $dataset (@{$data_set}) {
153      if ($dataset =~ m{/}) {
154         # sub-folder case
155         my $folder = $dataset =~ s{/[^/]+$}{}r;
156         print "chmod -R o+rX,o-w '$current_dir/$dataset'\n";
157         print "ln --symbolic --target-directory '$dap_folder/$acronym/$folder/' '$current_dir/$dataset'\n";
158         }
159      else {
160         # Root case
161         print "ln --symbolic --target-directory '$dap_folder/$acronym/' '$current_dir/$dataset'\n";
162         }
163
164      }
165   print "chmod -R o+rX,o-w '$dap_folder/$acronym/'\n";
166   }
167
168################################################################
169
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
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";
181   }
182
183################################################################
184
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
191   push @{$data_set}, 'AUTHORS.txt', 'COPYRIGHT.txt', 'LICENSE.txt';
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
204         $zip->addTree($dataset, "$acronym/$dataset");
205         }
206      elsif (-f $dataset) {
207         # File case
208         $zip->addFile($dataset, "$acronym/$dataset");
209         }
210      else {
211         # Strange case
212         print "Error: entry $dataset doesn't exists\n";
213         }
214      }
215
216   my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime time;
217   $year += 1900;
218   $mon++;
219   my $date = sprintf '%04i%02i%02i-%02i%02i', $year, $mon, $mday, $hour, $min;
220
221   # Save the Zip file
222   unless ($zip->writeToFileNamed("$current_dir/$acronym--$date.zip") == AZ_OK) {
223      die 'Error: zip write error';
224      }
225   }
226
227################################################################
228
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 {
238   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
239
240   my $current_dir = Cwd::getcwd();
241
242   my $acronym    = $meta->{'project'}{'acronym'};
243   my $authors_list = $meta->{'project'}{'authors'};
244
245   if (-f "$current_dir/AUTHORS.txt") {
246      # Test for manual or automatically generated file
247      # Automatically generated file by project-meta
248      my $automatic;
249      open my $fh, '<', "$current_dir/AUTHORS.txt" or die $!;
250      for my $line (<$fh>) {
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";
261      }
262
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
271   open my $fh,  '>', "$current_dir/AUTHORS.txt" or die $!;
272   print $fh "$msg_format\n\n";
273   close $fh;
274   }
275
276################################################################
277
278sub cmd_make_file_license {
279   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
280
281   my $current_dir = Cwd::getcwd();
282
283   if (-f "$current_dir/LICENSE.txt") {
284      print "Warning: LICENSE.txt already exists\n";
285      return;
286      }
287
288   my $license = $meta->{'public-dap'}{'data-license'};
289
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";
292      exit 1;
293      }
294
295   copy("/usr/share/project-meta/license.d/$license.txt", "$current_dir/LICENSE.txt")
296      or die "Error: license copy failed - $!";
297
298   print "Info: LICENSE.txt file create\n";
299   return;
300   }
301
302################################################################
303
304sub cmd_make_file_copyright {
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      }
326   
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      );
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'},
338         license     => $meta->{'public-dap'}{'data-license'},
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################################################################
348
349sub cmd_list_license {
350   opendir my $dh, '/usr/share/project-meta/license.d/' or die $!;
351   for my $license (readdir $dh) {
352      # Keep only file
353      next if not -f "/usr/share/project-meta/license.d/$license";
354     
355      # Keep only .txt file
356      next if not $license =~ m/\.txt$/;
357
358      $license =~ s/\.txt$//;
359      print "$license\n";
360      }
361   closedir $dh;
362   }
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
377 project-meta help
378 project-meta version
379 project-meta check
380 project-meta make-link
381 project-meta remove-link
382 project-meta make-zip
383 project-meta list-license
384 project-meta make-file-license
385 project-meta make-file-author
386 project-meta make-file-copyright
387
388
389=head1 DESCRIPTION
390
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.
394
395Everything is declare in the metafile F<PROJECT-META.yml>.
396This YAML file must exist in your root projet folder.
397See L</METAFILE SPECIFICATION>.
398
399
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
405(like for all the Project-Meta code).
406
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,
413you can use C<yamllint> or C<ysh> commands to check just it's format.
414
415=head2 make-link
416
417 project-meta make-link
418
419Create UNIX soft links on the OpeNDAP folder to the real data.
420Files F<AUTHORS.txt>, F<LICENSE.txt> and F<COPYRIGHT.txt> are mandatory but could be generated (see below).
421The main keys use in the F<PROJECT-META.yml> are:
422
423=over
424
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
429=item * C<public-dap/data-set>: a list of files or folder to push
430
431=back
432
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
450=head2 make-zip
451
452 project-meta make-zip
453
454Create a ZIP archive with the open data set.
455Files F<AUTHORS.txt>, F<LICENSE.txt> and F<COPYRIGHT.txt> are mandatory but could be generated (see below).
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
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
474=head2 list-license
475
476 project-meta list-license
477
478Give the list of all the open data licenses supported by the project-meta license database.
479At this time the possible licenses are:
480
481=over
482
483=item * L<creative-common-zero-v1.0|https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt>
484        (like public domain)
485
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
489=item * L<licence-ouverte-v2.0|https://www.etalab.gouv.fr/wp-content/uploads/2017/04/ETALAB-Licence-Ouverte-v2.0.pdf>
490        (copyleft - opendata french goverment)
491
492=item * L<open-database-license-v1.0|https://opendatacommons.org/files/2018/02/odbl-10.txt>
493        (copyleft - allow users to freely share, modify, and use while maintaining this same freedom for others)
494
495=back
496
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.
499
500=head2 make-file-license
501
502 project-meta make-file-license
503
504Copy the license file from the project-meta license database at the current folder
505with the file name: F<LICENSE.txt>.
506
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>.
509
510=head2 make-file-author
511
512 project-meta make-file-author
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
517=head2 make-file-copyright
518
519 project-meta make-file-copyright
520
521Create or update the F<COPYRIGHT.txt> file at the current folder.
522Authors, license and copyright data are extracted from the C<PROJECT-META.yml> file.
523
524
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
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.
532This one is actually the master reference specification!
533
534
535=head1 KNOWN BUGS
536
537 - not really check keys and tags before doing action!
538
539
540=head1 SEE ALSO
541
542yamllint(1), ysh(1), YAML, Archive::Zip
543
544In Debian GNU/Linux distribution, packages for C<yamllint> and C<ysh> are:
545
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
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
568=head1 AUTHOR
569
570Written by Gabriel Moreau, LEGI UMR5519, CNRS, Grenoble - France
571
572
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
577or helped me in another task like having new ideas, specifications...
578Maybe I forgot your contribution in recent years,
579please forgive me in advance and send me an e-mail to correct this.
580
581Joel Sommeria, Julien Chauchat, Cyrille Bonamy, Antoine Mathieu.
582
583
584=head1 LICENSE AND COPYRIGHT
585
586License GNU GPL version 2 or later and Perl equivalent
587
588Copyright (C) 2017-2018 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>.
Note: See TracBrowser for help on using the repository browser.