1#  Copyright (c) 1997-2021
2#  Ewgenij Gawrilow, Michael Joswig, and the polymake team
3#  Technische Universität Berlin, Germany
4#  https://polymake.org
5#
6#  This program is free software; you can redistribute it and/or modify it
7#  under the terms of the GNU General Public License as published by the
8#  Free Software Foundation; either version 2, or (at your option) any
9#  later version: http://www.gnu.org/licenses/gpl.txt.
10#
11#  This program is distributed in the hope that it will be useful,
12#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14#  GNU General Public License for more details.
15#-------------------------------------------------------------------------------
16#
17#  This file is part of the polymake database interface polyDB.
18#
19#   @author Silke Horn, Andreas Paffenholz
20#   http://www.mathematik.tu-darmstadt.de/~paffenholz
21#
22
23CREDIT polyDB
24
25package PolyDB::Client;
26use Term::ANSIColor;
27use Term::ReadKey;
28use Text::Wrap;
29
30# @category Database Access
31# Get a list of all collection names
32# @param String section only return names of collections from the given section
33# @option String filter regular expression for more complex filtering of collection names
34#  Think of correct escaping of special characters: \\. for a literal dot, \\w for a name character.
35user_method list_collections(;$ { filter => undef }) {
36   $_[2]->{recursive} = -1;
37   &get_collection_names;
38}
39
40# @category Database Access
41# Get a list of section names
42# @param String section return names of sub-sections of the given section;
43#   by default, names of top-level sections are returned
44# @option Bool recursive return names of sub-sections  on all levels
45# @option String filter regular expression for more complex filtering of section names
46#  Think of correct escaping of special characters: \\. for a literal dot, \\w for a name character.
47user_method list_sections(;$ { filter => undef, recursive => false }) {
48   &get_collection_names;
49}
50
51# @category Database Administration
52# Common options for writing section doc
53options %doc_options = (
54   # String section section name
55   section => $PolyDB::default::db_section_name,
56   # Bool update default false
57   update => false,
58   # Bool verbose default false
59   verbose => false,
60   # Bool replace default false
61   replace => false
62);
63
64# @category Database Administration
65# Set documentation for a section
66# @param HASH doc the documentation
67# @options %doc_options
68user_method set_section_doc($; %doc_options) {
69   my ($self, $doc, $options) = @_;
70
71   my $version = $doc->{polydb_version} // $PolyDB::default::db_polydb_version;
72   $doc->{"_id"} = $options->{section}.".$version";
73   $doc->{section} = split /\./, $options->{section};
74   $doc->{sectionDepth} = scalar($doc->{section});
75
76   my $db = $self->SUPER::get_database($default::db_name);
77   my $col = $db->get_collection("_sectionInfo.".$options->{section});
78
79   if ( $options->{replace} ) { $options->{update} = true; }
80   my $output = $col->find_one({'_id' => $doc->{id}});
81   die "documentation already set but neither option update nor replace given\n" if ( $output && !$options->{update} );
82   if ( !$output ) { $options->{update} = false; }
83
84   if ( !$options->{update} ) {
85      $output = $col->insert_one($doc);
86   } else {
87      if ( $options->{replace} ) {
88         $output = $col->replace_one({'_id' => $doc->{id}}, $doc);
89      } else {
90         $output = $col->update_one({'_id' => $doc->{id}}, {'$set' => $doc});
91      }
92   }
93   if ($options->{verbose}) {
94      if ($output->acknowledged) {
95         print "successfully set documentation for $options->{section}\n"
96      } else {
97         print "an error occurred when trying to set the documentation for $options->{section}:\n$output\n";
98      }
99   }
100}
101
102# @category Database Access
103# Print information about available databases and collections.
104# @option String section name of the database, default: all available databases
105# @option String collection name of the collection, default: all available collections
106# @option Int info_level 0: only names,
107#                        1: short description (default if no collection is given),
108#                        2: description,
109#                        3: description, authors, maintainers,
110#                        4: full info
111# @option Bool colored
112user_method info(; {section => undef, collection => undef, info_level => undef, colored => true}) {
113   my ($self, $options) = @_;
114   $options->{info_level} = defined($options->{collection}) ? 5 : 1;
115   if ( !defined($options->{section}) && defined($options->{collection}) ) {
116      ($options->{section}, $options->{collection}) = $options->{collection} =~ /([\w.]+)\.([\w]+)/;
117   }
118
119   print "===============\navailable polydb collections\n===============\n";
120
121   $self->print_sections_at_level($options->{section}, $options->{collection}, $self, $options->{info_level}, 0, $options->{colored});
122}
123
124sub print_formatted {
125   my ($indent, $text, $colored) = @_;
126   state $in_terminal = ( -t STDIN &&  $PolyDB::default::pretty_print_doc ) ? true : false;
127
128   if ( $in_terminal ) {
129      my $initial_tab = "\t"x$indent;
130      my $subsequent_tab = "\t"x$indent;
131      $subsequent_tab .= "  ";
132      my ($w) = GetTerminalSize();
133      local($Text::Wrap::columns) = $w;
134
135      if ( $colored ) {
136         if ( $text =~ /SECTION/ ) {
137            print color($PolyDB::default::db_section_color);
138         } elsif ($text =~ /COLLECTION/) {
139            print color($PolyDB::default::db_collection_color);
140         } else {
141            print color('reset');
142         }
143      }
144      print wrap($initial_tab, $subsequent_tab, $text);
145      print color('reset');
146   } else {
147      print $text;
148   }
149   print "\n";
150}
151
152sub print_name_with_info {
153   my ($name) = @_;
154   my $ret = "  ".$name->{name};
155   for my $tag (("affiliation", "email")) {
156      $ret .= ", ".$name->{$tag} if defined($name->{$tag});
157   }
158   $ret .= " (".$name->{remark}.")" if defined($name->{remark});
159   return $ret;
160}
161
162sub print_list_of_names {
163   my ($names, $title, $indent) = @_;
164   if ( ref($names) eq "ARRAY" ) {
165      print_formatted($indent, $title.": ");
166      foreach my $name (@{$names}) {
167         print_formatted($indent, print_name_with_info($name));
168      }
169   } else {
170      print_formatted($indent, $title.": ".$names);
171   }
172}
173
174sub print_references {
175   my ($res,$indent) = @_;
176   foreach my $ref (@$res) {
177      print_formatted($indent, "Cite:" );
178      if ( ref($ref) eq "HASH" ) {
179         print_formatted($indent+1, $ref->{authors}.", ".$ref->{title}.", ".$ref->{bib});
180         if ( defined($ref->{links}) ) {
181            print_formatted($indent+1, "  obtain at:");
182            foreach my $link (@{$ref->{links}}) {
183               print_formatted($indent+2,$link->{type}.": ".$link->{link});
184            }
185         }
186      } else {
187         print_formatted($indent,$ref);
188      }
189   }
190}
191
192sub print_webpage {
193   my ($res,$indent) = @_;
194
195   foreach my $ref (@$res) {
196      print_formatted($indent,"Online Resources:");
197      if ( ref($ref) eq "HASH" ) {
198         print_formatted($indent+1,$ref->{description}.": ".$ref->{address});
199      } else {
200         print_formatted($indent+1,$ref);
201      }
202   }
203}
204
205sub print_collections_at_level {
206   my ($self,$sectionname, $collectionname, $idb, $info_level, $indent, $colored) = @_;
207
208   my $filter = "_collectionInfo.".$sectionname;
209   if ( $collectionname eq "" ) {
210      $filter .= '\.[\w]+$';
211   } else {
212      $filter .= "\\.".$collectionname;
213   }
214   my $db = $self->SUPER::get_database($default::db_name);
215   my @collections = map { $_->{name} }
216         $db->list_collections({ name => { '$regex' => $filter } }, { authorizedCollections => true, nameOnly => true })->all;
217   my $polydb_version = $PolyDB::default::db_polydb_version;
218
219   return if !scalar(@collections);
220
221   foreach my $colname (sort { lc($a) cmp lc($b) } @collections) {
222      my $id = $colname.".".$polydb_version;
223      $id =~ s/_collectionInfo.//;
224      my $icol = $db->get_collection($colname);
225      my $res  = $icol->find_one({ "_id" => $id });
226
227      next if !defined($res);
228      print "\n";
229      print_formatted($indent,"COLLECTION: ".$res->{collection}, $colored);
230
231      next if $info_level < 1;
232      print_formatted($indent, $info_level == 1 ? $res->{short_description} : $res->{description}, $colored);
233
234      next if $info_level < 3;
235      print_list_of_names($res->{author},     "Author(s)",     $indent) if defined($res->{author});
236      print_list_of_names($res->{contributor},"Contributor(s)",$indent) if defined($res->{contributor});
237      print_list_of_names($res->{maintainer}, "Maintainer(s)", $indent) if defined($res->{maintainer});
238
239      next if $info_level < 4;
240      print_references($res->{references}) if defined($res->{references});
241      print_webpage($res->{webpage}, $indent) if defined($res->{webpage});
242   }
243}
244
245sub print_sections_at_level {
246   my ($self, $sectionname, $collectionname, $idb, $info_level, $indent, $colored) = @_;
247
248   my $polydb_version = $PolyDB::default::db_polydb_version;
249
250   foreach my $sectionname (sort { lc($a) cmp lc($b) } $self->list_sections(filter=>$sectionname) ) {
251      my $id = "$sectionname.$polydb_version";
252      my $db = $self->SUPER::get_database($default::db_name);
253      my $icol = $db->get_collection("_sectionInfo.$sectionname");
254      my $res  = $icol->find_one({ "_id" => $id });
255
256      print "\n";
257      print_formatted($indent, "SECTION: ".$sectionname, $colored);
258      if ( $info_level > 0 ) {
259         print_formatted($indent, $info_level == 1 ? $res->{short_description} : $res->{description}, $colored);
260      }
261
262      $self->print_sections_at_level($sectionname.".", $collectionname, $idb, $info_level,$indent+1, $colored);
263      $self->print_collections_at_level($sectionname,  $collectionname, $idb, $info_level,$indent+1, $colored);
264   }
265}
266
267
268
269
270
271
272
273
274package PolyDB::Collection;
275
276# @category Database Access
277# Get a JSON validation schema for objects stored in the collection
278# @return Schema
279user_method get_schema() { &get_own_schema }
280
281# @category Database Collection Administration
282# Common options for writing collection doc
283options %doc_options = (
284   # Bool update default false
285   update => false,
286   # Bool verbose default false
287   verbose => false,
288   # Bool replace default false
289   replace => false
290);
291
292# @category Database Collection Administration
293# Set documentation for a collection
294# @param HASH doc the documentation
295# @options %doc_options
296user_method set_collection_doc ($; \%doc_options) {
297   my ($self, $doc, $options) = @_;
298   my $collection = $self->{name};
299   my $version = $doc->{version} // $PolyDB::default::db_polydb_version;
300   my $id = "$collection.$version";
301   $doc->{"_id"} = $id;
302
303   my $col = $self->get_own_info_collection();
304
305   if ( $options->{replace} ) { $options->{update} = true; }
306   my $output = $col->find_one({'_id' => $id});
307   die "documentation already set but neither option update nor replace given\n" if ( $output && !$options->{update} );
308   if ( !$output ) { $options->{update} = false; }
309
310   if ( !$options->{update} ) {
311      $output = $col->insert_one($doc);
312   } else {
313      if ( $options->{replace} ) {
314         $output = $col->replace_one({'_id' => $id}, $doc);
315      } else {
316         $output = $col->update_one({'_id' => $id}, {'$set' => $doc});
317      }
318   }
319   if ($options->{verbose}) {
320      if ($output->acknowledged) {
321         print "successfully updated documentation for $collection in section $section\n"
322      } else {
323         print "an error occurred when trying to update the documentation for $collection in section $section:\n$output\n";
324      }
325   }
326}
327
328# Local Variables:
329# mode: perl
330# cperl-indent-level:3
331# indent-tabs-mode:nil
332# End:
333