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