require 'rubygems' require 'sequel' require 'wx' DB_NAME = File.join(File.dirname(__FILE__), "circadian.db") # In order to more easily set the primary key we need to un-restrict it. This will do it in a global fashion: Sequel::Model.unrestrict_primary_key # We need to connect to the database DB = Sequel.sqlite(DB_NAME) # Above can be done on a per class basis too. For Example: # Computer.unrestrict_primary_key # The table classes for Sequel class Computer < Sequel::Model set_schema do primary_key :name, :type => :text, :auto_increment => false varchar :os varchar :ip varchar :mac foreign_key :location, :labs, :type => :text end many_to_one :lab, :key => :location end # Computer class class Lab < Sequel::Model set_schema do primary_key :name, :type => :text, :auto_increment => false varchar :building varchar :room end one_to_many :computers one_to_many :up_times one_to_many :down_times end # Lab class class MainFrame < Wx::Frame def initialize(parent, id = -1, title = "Circadian Main Frame", pos = Wx::DEFAULT_POSITION, size = Wx::DEFAULT_SIZE, style = Wx::DEFAULT_FRAME_STYLE) super(parent, id, title, pos, size, style) @notebook = TableNotebook.new(self, -1, Wx::Size.new(625, 455)) # Create the sizer for the main window. Notebook has to be added so it will scale with window. main_sizer = Wx::BoxSizer.new(Wx::VERTICAL) main_sizer.add(@notebook, 1, Wx::EXPAND|Wx::ALL, 5) self.sizer = main_sizer end # initialize end # MainFrame # This is the tabbed frame/window for the database tables. class TableNotebook < Wx::Notebook def initialize(parent, id, size) super(parent, id, Wx::DEFAULT_POSITION, size, Wx::NB_TOP) #Lets get pretty table names for the tabs DB.tables.each do |table| table_name = "" table.to_s.split("_").each do |word| table_name << word.capitalize! if table.to_s =~ /_/ table_name << " " end end page = Table.new(self, table) page.table = DbTableBase.new(DB[table].entries.length, DB[table].columns.length, table.to_s) add_page(page, table_name) end end # initialize end # TableNotebook class # This is a grid class that handles the details of the underlying database tables class Table < Wx::Grid def initialize(parent, target_table) # target_table is a string consisting of the name of the target db table super(parent, -1) # Store the name of the associated database table for future reference @db_table = target_table.to_s end # initialize def get_table_name # We want to retrieve the name of the associated db table @db_table end end # Table class # Class for making the tables more dynamic. class DbTableBase < Wx::GridTableBase attr_reader :rows, :cols def initialize(rows, cols, db_table) super() @rows = rows @cols = cols @db_table = db_table @table_as_sym = db_table.to_sym @column_array = DB[@table_as_sym].columns @column_array.each_with_index do |title, col| set_col_label_value(col, title.to_s) end end # Because the docs say this must be defined def get_number_rows @rows end # Because the docs say this must be defined def get_number_cols @cols end # This lets the grid retrieve values. Luckily we can get an array of objects # from Sequel easily as we need the row number which is otherwise arbitrary # when the pkey isn't a pointless fucking integer. def get_value(row, col) DB[@table_as_sym].limit(row..row).first[@column_array[col]].to_s end # get_vaule # This really only does anything when the cell is in the foreign key column. In # that case it creates a select list (GridCellChoiceEditor) for the values. def get_attr(row, col, attr_kind) attr = Wx::GridCellAttr.new if @db_table == "computers" and col == 4 attr.set_editor(Wx::GridCellChoiceEditor.new(["CAD", "GIS", "CMT"])) @editors ||= [] @editors << Wx::GridCellChoiceEditor.new(["CAD", "GIS", "CMT"]) attr.set_editor(@editors.last) end attr end # get_attr # Last required definition def is_empty_cell(row, col) if get_value(row, col) == "" # get_value should return "" if the db field is nil return true else return false end end # is_empty_cell # This method handles the changing of a value in the interface. The method should # change the corresponding value in the data base. Special care is needed for changing # primary key data. def set_value(row, col, value) table_class = get_table_class() old_value = get_value(row, col) # Get the old value for logging purposes # We have the class name now we need to get the primary key field name and values of # the target record. pk = eval("#{table_class}.primary_key") col_num = @column_array.index(pk.to_sym) pk_value = get_value(row, col_num) record = eval("#{table_class}.find(pk => pk_value)") field = @column_array[col].to_s # Let's change the value in the record. eval("record.#{field} = \"#{value}\"") # Save the changes to the database record.save_changes # Log changes log_file = File.open("log.txt", "a") log_file.puts "Modified #{table_class} #{pk_value}: changed #{field} from #{old_value} to #{value}\t#{Time.now.to_s}" log_file.close end # set_value # Again I paste this code. It should probably go somewhere once and stay there. def get_table_class() class_name = "" @db_table.split("_").each do |word| class_name << word.capitalize! end class_name[0..-2] end # get_table_class # We need this in order to set the grid column titles appropriately def get_col_label_value(col) @column_array[col].to_s end # get_col_label_value end # DbTableBase class CircadiaApp < Wx::App def on_init unless DB.tables.length > 0 Lab.create_table Computer.create_table UpTime.create_table DownTime.create_table end main_frame = MainFrame.new(nil, -1, "Circadian", Wx::Point.new(10, 100), Wx::Size.new(630, 480)) set_top_window(main_frame) main_frame.show end # on_init end # CircadiaApp CircadiaApp.new.main_loop()