# Raw daily data MVC model. # Copyright 2007, 2008, 2009, 2010, 2011 Kevin Ryde # This file is part of Chart. # # Chart is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # Chart is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with Chart. If not, see . package App::Chart::RawDailyModel; use strict; use warnings; use Glib; use Gtk2; use Carp; use Gtk2::Ex::TreeModel::ImplBits; use Locale::TextDomain ('App-Chart'); use App::Chart::Gtk2::Symlist; use Glib::Object::Subclass 'Glib::Object', interfaces => [ Gtk2::TreeModel:: ], properties => [ Glib::ParamSpec->string ('symbol', __('Symbol'), 'The symbol for the data to present.', '', # default Glib::G_PARAM_READWRITE) ]; use constant { COL_DATE => 0, COL_OPEN => 1, COL_HIGH => 2, COL_LOW => 3, COL_CLOSE => 4, COL_VOLUME => 5, COL_OPENINT => 6, NUM_COLUMNS => 7 }; my @field_name = ('date', 'open', 'high', 'low', 'close', 'volume', 'openint'); my $select_sql = 'SELECT ' . join(',', @field_name) . 'FROM daily WHERE symbol=? ORDER BY date ASC'; sub _do_data_changed { my ($self, $symbol_hash) = @_; } sub INIT_INSTANCE { my ($self) = @_; Gtk2::Ex::TreeModel::ImplBits::random_stamp ($self); App::Chart::chart_dirbroadcast()->connect_for_object ('data-changed', \&_do_data_changed, $self); } sub SET_PROPERTY { my ($self, $pspec, $newval) = @_; my $pname = $pspec->get_name; $self->{$pname} = $newval; # per default GET_PROPERTY if ($pname eq 'symbol') { delete $self->{'data'}; } } # gtk_tree_model_get_flags # sub GET_FLAGS { return [ 'list-only' ]; } # gtk_tree_model_get_n_columns # sub GET_N_COLUMNS { return NUM_COLUMNS; } # gtk_tree_model_get_column_type # sub GET_COLUMN_TYPE { # my ($self, $index) = @_; return 'Glib::String'; } # gtk_tree_model_get_iter # sub GET_ITER { my ($self, $path) = @_; if ($path->get_depth != 1) { die "RawDailyModel depth is only 1"; } my ($n) = $path->get_indices; return [ $self->{'stamp'}, $n, undef, undef ]; } # gtk_tree_model_get_path # sub GET_PATH { my ($self, $iter) = @_; my $n = $iter->[1]; return Gtk2::TreePath->new_from_indices ($n); } sub data { my ($self) = @_; if (exists $self->{'data'}) { return $self->{'data'}; } my $symbol = $self->{'symbol'}; if (! $symbol) { return ($self->{'data'} = []); } ### raw read: $symbol require App::Chart::DBI; my $dbh = App::Chart::DBI->instance; my $sth = $dbh->prepare_cached ($select_sql); my $data = $dbh->selectall_arrayref ($sth, undef, $symbol); return ($self->{'data'} = $data); } sub length { my ($self) = @_; return @{$self->data()}; } # gtk_tree_model_get_value # sub GET_VALUE { my ($self, $iter, $col) = @_; my $n = $iter->[1]; #### get_value: $n,$col return $self->data->[$n]->[$col]; } # gtk_tree_model_iter_next # sub ITER_NEXT { my ($self, $iter) = @_; my $n = $iter->[1]; if ($n >= $self->length - 1) { # at last record return undef; } return [ $self->{'stamp'}, $n + 1, undef, undef ]; } # gtk_tree_model_iter_children # sub ITER_CHILDREN { my ($self, $iter) = @_; if ($iter) { # no children of any nodes return undef; } else { # $iter==NULL means first toplevel return [ $self->{'stamp'}, 0, undef, undef ]; } } # gtk_tree_model_iter_has_child # sub ITER_HAS_CHILD { # my ($self, $iter) = @_; return 0; } # gtk_tree_model_iter_n_children # sub ITER_N_CHILDREN { my ($self, $iter) = @_; if ($iter) { # nothing under actual rows return 0; } else { # $iter==NULL asks about toplevel return $self->length; } } # gtk_tree_model_iter_nth_child # sub ITER_NTH_CHILD { my ($self, $iter, $n) = @_; if ($iter) { # nothing unde actual rows return undef; } else { # $iter==NULL means nth toplevel if ($n < 0 || $n >= $self->length) { # out of range return undef; } return [ $self->{'stamp'}, $n, undef, undef ]; } } # gtk_tree_model_iter_parent # sub ITER_PARENT { # my ($self, $iter) = @_; return undef; } sub set_value { my ($self, $iter, $col, $value) = @_; $iter = $iter->to_arrayref ($self->{'stamp'}); my $n = $iter->[1]; print "set_value $self $iter, $col, $value\n"; my $symbol = $self->{'symbol'}; my $data = $self->{'data'}; my $date = $data->[$n]->[0]; my $field = $field_name[$col]; require App::Chart::DBI; my $dbh = App::Chart::DBI->instance; print "write $symbol $date $field $value\n"; my $sth = $dbh->prepare_cached ("UPDATE daily SET $field=? WHERE symbol=? AND date=?"); $sth->execute ($value, $symbol, $date); delete $self->{'data'}; $self->row_changed ($self->get_path($iter), $iter); } 1; __END__ =head1 NAME App::Chart::RawDailyModel -- raw daily data model =head1 SYNOPSIS use App::Chart::RawDailyModel; my $model = App::Chart::RawDailyModel->new (symlist => $symlist); =head1 OBJECT HIERARCHY C is a subclass of C, Glib::Object App::Chart::RawDailyModel The following GInterfaces are implemented Gtk2::TreeModel Gtk2::Buildable (inherited) =head1 DESCRIPTION A C object presents database daily data in C form. =head1 FUNCTIONS =over 4 =item C<< App::Chart::RawDailyModel->new (key => value, ...) >> Create and return a C object. Optional key/value pairs can be given to set initial properties as per C<< Glib::Object->new >>. =item C<< $model->set_value ($iter, $col, $value) >> Set the value in row C<$iter> and column C<$col> to C<$value>. This updates the database (and emits the C signal). =back =head1 CONSTANTS The following constants give column numbers in the model, App::Chart::RawDailyModel::COL_DATE App::Chart::RawDailyModel::COL_OPEN App::Chart::RawDailyModel::COL_HIGH App::Chart::RawDailyModel::COL_LOW App::Chart::RawDailyModel::COL_CLOSE App::Chart::RawDailyModel::COL_VOLUME App::Chart::RawDailyModel::COL_OPENINT =head1 PROPERTIES =over 4 =item C (string, default empty "") The symbol to present data for. Currently the intention is that this is "construct-only", ie. to be set only when first constructing the model. Perhaps in the future something tricky can be done to update the data with updates for all rows and insert/deletes to try to keep date rows common to the new and old symbol. (But maintaining a date position is probably much more easily done by the view.) =back =head1 SEE ALSO L =cut