3 See the NOTICE file distributed with
this work
for additional information
4 regarding copyright ownership.
6 Licensed under the Apache License, Version 2.0 (the
"License");
7 you may not use
this file except in compliance with the License.
8 You may obtain a copy of the License at
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an
"AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License
for the specific language governing permissions and
16 limitations under the License.
22 A database
class that auto-instantiates a DBIC schema
24 Convenience methods prevent having to delve into DBIC guts for common activities
36 create => 1, # Deploys the schema to the
DB for first use
41 config_file =>
'db.conf' # config options in Config::General format
44 my $dbh = $db->
dbh; # $dbh is a DBI database handle borrowed
for direct SQL
45 $dbh->prepare(
'DROP TABLE dependent_xref');
47 $db->create_db_row(
'Xref',{
50 description =>
'Sample new Xref',
64 use namespace::autoclean;
66 use Config::IniFiles; # FIXME normalise around one config format
72 isa =>
'Xref::Schema',
80 builder =>
'_guess_config'
96 Arg [1] : HashRef of configuation parameters (driver, db, host, port, user, pass)
97 Description: Initialise the core database.
106 $self->_init_config
if ! defined $self->config;
107 $self->_validate_config($self->config);
108 my %conf = %{ $self->config };
109 my $enable_unicode = $conf{enable_unicode}
111 $opts{mysql_enable_utf8} = $enable_unicode
if ($conf{driver} eq
'mysql');
112 $opts{mysql_auto_reconnect} = 1
if ($conf{driver} eq
'mysql');
113 $opts{sqlite_unicode} = $enable_unicode
if($conf{driver} eq
'SQLite');
115 if ($conf{driver} eq
'SQLite') {
116 $dsn = sprintf
'dbi:%s:database=%s',$conf{driver},$conf{file};
117 $self->now_function(
"date('now')");
119 $dsn = sprintf
'dbi:%s:database=%s;host=%s;port=%s', $conf{driver}, $conf{db}, $conf{host}, $conf{port};
122 my %deploy_opts = ();
123 # Example deploy option $deploy_opts{add_drop_table} = 1;
124 my $schema =
Xref::Schema->connect($dsn, $conf{user}, $conf{pass}, \%opts);
126 if ($conf{create} == 1 && $conf{driver} eq
'mysql') {
127 my $dbh = DBI->connect(
128 sprintf(
'DBI:%s:database=;host=%s;port=%s', $conf{driver}, $conf{host}, $conf{port}), $conf{user}, $conf{pass}, \%opts
131 # Remove database if already exists
132 my %dbs =
map {$_->[0] => 1} @{$dbh->selectall_arrayref(
'SHOW DATABASES')};
133 my $dbname = $conf{db};
135 $dbh->do(
"DROP DATABASE $dbname;" );
138 my $db_collation = ( $enable_unicode ) ?
'utf8_general_ci' :
'latin1_swedish_ci';
139 $dbh->do(
"CREATE DATABASE $dbname DEFAULT COLLATE ${db_collation};");
144 $schema->deploy(\%deploy_opts)
if $conf{create} == 1;
147 } ## end sub _init_db
151 Description: Don
't want production use to guess at least at the moment.
152 This mainly exists so TestDB can override and replace with a
161 } ## end sub _guess_config
165 Arg [1] : HashRef of configuation parameters (driver, db, host, port, user, pass)
166 Description: Initialisae the loading of the configuration file.
167 Return type: HashRef - $self->config
175 if (defined $self->config_file) {
176 my $conf = Config::General->new($self->config_file);
177 my %opts = $conf->getall();
178 $self->config(\%opts);
180 confess 'No config or config_file provided to
new(). Cannot execute
';
183 return $self->config;
184 } ## end sub _init_config
187 =head2 _validate_config
188 Arg [1] : HashRef of configuation parameters (driver, db, host, port, user, pass)
189 Description: Configuration file parameter validation
190 Return type: DBI database handle
195 sub _validate_config {
196 my ($self,$config) = @_;
197 my @required_keys = qw/driver/;
198 if ($config->{driver} eq 'mysql
') {
199 push @required_keys, qw/db host port user pass/;
200 } elsif ($config->{driver} eq 'SQLite
') {
201 push @required_keys, qw/file/;
203 confess q(TestDB config requires parameter 'driver
' with value mysql or SQLite);
206 foreach my $constraint (@required_keys) {
207 if (! exists $config->{$constraint}) {
208 push @errors, "Missing argument '$constraint
'";
211 if (scalar @errors > 0) {
212 confess sprintf "%s \n%s",
213 ($self->config_file) ? 'Missing options in
'.$self->config_file. ':
' : 'Missing options in supplied config:
',
216 } ## end sub _validate_config
220 Description: Shortcut for accessing a database handle directly. I get the
221 impression we might be doing this a lot.
222 Return type: DBI database handle
229 return $self->schema->storage->dbh;
235 Arg [2] : arguments : These should be key-value pairs matching the rows in
237 Description: Shortcut for creating things on the fly
244 my ($self,$model, $params) = @_;
245 my $source = $self->schema->resultset($model)->create(
249 } ## end sub create_db_row
252 =head2 populate_metadata
254 Arg [1] : Config file path, normally xref_config.ini
255 Description: Loads species and source information into the schema from the
256 supplied file in Config::IniFiles format
261 # TODO: Provide species AND division in order to limit quantity of madness
263 sub populate_metadata {
264 my ($self, $config_path) = @_;
266 my $config = $self->_load_xref_config($config_path);
268 # Populate species table with species taxa
269 print "Iterating over species groups\n";
272 # First build up records for each potential source
273 foreach my $section ( $config->GroupMembers('source
')) {
275 my ( $source_name ) = $section =~ /
280 $sources{$source_name} = $self->_mangle_source_block($section, $config);
285 # Next, build a config hash from the species entries in the config file
286 # and populate them with relevant source information from the %sources
287 foreach my $section ( $config->GroupMembers('species
') ) {
288 my ( $species_name ) = $section =~ /
295 my $taxon_id = $config->val( $section, 'taxonomy_id
' );
297 my @source_names = $config->val( $section, 'source
', ());
298 my %sources_for_species;
300 foreach my $source_entry (@source_names) {
302 if (! exists $sources{$source_entry}) {
303 confess 'Species config references a source that is not defined in the config:
'.$source_entry;
306 $sources_for_species{$source_entry} = $sources{$source_entry};
309 $compiled_config{$species_name} = {
310 species_id => $taxon_id, # species_id == taxon_id in xref system,
311 taxonomy_id => $taxon_id,
312 sources => \%sources_for_species
316 # Now populate the database with the result each source entry in the config with source-specific info
318 foreach my $species ( keys %compiled_config ) {
320 my $species_record = $self->schema->resultset('Species
')->create({
321 species_id => $compiled_config{$species}{species_id},
323 taxonomy_id => $compiled_config{$species}{taxonomy_id}
326 my $compiled_sources = $compiled_config{$species}{sources};
328 foreach my $source ( keys %$compiled_sources ) {
329 my $parser = delete $compiled_sources->{$source}->{parser};
330 my $source_record = $self->schema->resultset('Source
')->find_or_create(
331 $compiled_sources->{$source} # once trimmed, we can pump the source hash straight into DBIC
333 delete $sources{$source};
335 $source_record->create_related(
338 species_id => $compiled_config{$species}{species_id},
342 } # End foreach source
345 } # End foreach species
347 # Add any sources which are not explicitly linked to a species
349 foreach my $source ( keys %sources ) {
351 my $parser = delete $sources{$source}->{parser};
352 my $source_record = $self->schema->resultset('Source
')->find_or_create(
361 =head2 _load_xref_config
363 Arg [1] : path to xref_config.ini
364 Description: Load the config file and sanity-check to ensure content is
365 properly formatted for loading.
366 FIXME: make parsing into a parser? Better yet switch to a format
367 which can express this grammar correctly without micro-formatting
368 Returntype : Hashref config, as returned by Config::IniFiles
372 sub _load_xref_config {
373 my ($self, $config_path) = @_;
375 if (! -e $config_path) {
376 confess "Unable to open config file $config_path";
378 my $config = Config::IniFiles->new(-file => $config_path);
380 if (! defined $config) {
381 confess "Errors in $config_path, unable to parse: ". join ',
', @Config::IniFiles::errors;
384 my %source_ids; # A tracker for sections we've seen
386 foreach my $section ( $config->GroupMembers(
'source')) {
387 my ( $spaces, $source_name ) = $section =~ /
394 # This validation is how we used to validate this file
395 if ( length($spaces) > 1 ) {
398 "Too many spaces between the words 'source' and '%s'\nwhile reading source section '[%s]'\n",
405 if ( index( $config->val( $section,
'name' ),
"\n" ) != -1 ) {
407 sprintf(
"The source section '[%s]' occurs more than once in \n", $section )
411 $source_ids{$section} = ++$source_id; # Record existence of species->source
414 foreach my $section ( $config->GroupMembers(
'species')) {
415 my ( $spaces, $species_name ) = $section =~ /
422 if ( length($spaces) > 1 ) {
425 "Too many spaces between the words 'species' and '%s'\nwhile reading species section '[%s]'\n",
432 foreach my $source_name (split qr{ \n }msx, $config->val($section,
'source') ) {
434 $source_name =~ s{ \s\z }{}msx; # Config file can easily contain trailling whitespace
435 my $source_section =
"source $source_name";
436 # Check integrity of species to source mentions
437 if ( !exists $source_ids{$source_section} ) {
439 sprintf(
"Can not find source section '[%s]'\nwhile reading species section '[%s]'\n",
452 =head2 _mangle_source_block
454 Arg 1 : String - section header, the name
for the section that identifies
455 the section we need to extract values from
456 Arg 2 : Hashref - config file content, the output of _load_xref_config()
457 Description: Takes a single source record from xref_config.ini and creates a
458 hashref. Some arguments are massaged to get them into the schema
460 Returntype : Hashref of a single source's properties
465 sub _mangle_source_block {
466 my ($self, $section, $config) = @_;
469 # Get the easy ones done
470 foreach my $key (qw/name priority parser/) {
471 my $value = $config->val($section, $key);
472 if (defined $value) {
473 $source_config->{$key} = $value;
477 my $priority_description = $config->val($section,
'prio_descr');
478 if (defined $priority_description) {
479 $source_config->{priority_description} = $priority_description;
482 return $source_config;
485 __PACKAGE__->meta->make_immutable;