aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/models/incoming_message.rb33
-rw-r--r--app/models/info_request_event.rb5
-rw-r--r--db/migrate/060_add_cached_main_text.rb9
-rw-r--r--db/schema.rb3
-rw-r--r--todo.txt2
-rw-r--r--vendor/plugins/attr_lazy/init.rb9
-rw-r--r--vendor/plugins/attr_lazy/lib/attr_lazy.rb94
7 files changed, 145 insertions, 10 deletions
diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb
index 385657704..653097bab 100644
--- a/app/models/incoming_message.rb
+++ b/app/models/incoming_message.rb
@@ -18,7 +18,7 @@
# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
# Email: francis@mysociety.org; WWW: http://www.mysociety.org/
#
-# $Id: incoming_message.rb,v 1.120 2008-07-16 23:45:41 francis Exp $
+# $Id: incoming_message.rb,v 1.121 2008-07-17 03:30:54 francis Exp $
# TODO
# Move some of the (e.g. quoting) functions here into rblib, as they feel
@@ -110,6 +110,11 @@ class IncomingMessage < ActiveRecord::Base
has_many :outgoing_message_followups, :foreign_key => 'incoming_message_followup_id', :class_name => 'OutgoingMessage'
+ # Some emails are large (10Mb), making things like search results and the
+ # front page list of requests slow to display as the data is transferred from
+ # the database.
+ attr_lazy :raw_data
+
# Return the structured TMail::Mail object
# Documentation at http://i.loveruby.net/en/projects/tmail/doc/
def mail
@@ -121,12 +126,21 @@ class IncomingMessage < ActiveRecord::Base
end
# Number the attachments in depth first tree order, for use in URLs.
+ # XXX This fills in part.rfc822_attachment and part.url_part_number within
+ # all the parts of the email (see TMail monkeypatch above for how these
+ # attributes are added). ensure_parts_counted must be called before using
+ # the attributes. This calculation is done only when required to avoid
+ # having to load and parse the email unnecessarily.
def after_initialize
- if !self.mail.nil?
+ @parts_counted = false
+ end
+ def ensure_parts_counted
+ if not @parts_counted
@count_parts_count = 0
count_parts_recursive(self.mail)
# we carry on using these numeric ids for attachments uudecoded from within text parts
@count_first_uudecode_count = @count_parts_count
+ @parts_counted = true
end
end
def count_parts_recursive(part)
@@ -147,8 +161,8 @@ class IncomingMessage < ActiveRecord::Base
end
end
# And look up by URL part number to get an attachment
+ # XXX relies on get_attachments_for_display calling ensure_parts_counted
def self.get_attachment_by_url_part_number(attachments, found_url_part_number)
- @count_parts_count = 0
attachments.each do |a|
if a.url_part_number == found_url_part_number
return a
@@ -378,7 +392,7 @@ class IncomingMessage < ActiveRecord::Base
end
# If the part is an attachment of email in text form
if curr_mail.content_type == 'message/rfc822'
- # This has been expanded from text to an email in count_parts_recursive above
+ ensure_parts_counted # fills in rfc822_attachment variable
leaves_found += get_attachment_leaves_recursive(curr_mail.rfc822_attachment)
else
# Store leaf
@@ -390,7 +404,14 @@ class IncomingMessage < ActiveRecord::Base
# Returns body text from main text part of email, converted to UTF-8, with uudecode removed
def get_main_body_text
- text = get_main_body_text_internal
+ # Cached as loading raw_data can be quite huge, and need this for just
+ # search results
+ if self.cached_main_body_text.nil?
+ text = self.get_main_body_text_internal
+ self.cached_main_body_text = text
+ self.save!
+ end
+ text = self.cached_main_body_text
# Strip the uudecode parts from main text
text = text.split(/^begin.+^`\n^end\n/sm).join(" ")
@@ -530,6 +551,8 @@ class IncomingMessage < ActiveRecord::Base
# Returns all attachments for use in display code
def get_attachments_for_display
+ ensure_parts_counted
+
main_part = get_main_body_text_part
leaves = get_attachment_leaves
attachments = []
diff --git a/app/models/info_request_event.rb b/app/models/info_request_event.rb
index a5eb308c6..7a76649ba 100644
--- a/app/models/info_request_event.rb
+++ b/app/models/info_request_event.rb
@@ -20,7 +20,7 @@
# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
# Email: francis@mysociety.org; WWW: http://www.mysociety.org/
#
-# $Id: info_request_event.rb,v 1.47 2008-07-16 23:45:41 francis Exp $
+# $Id: info_request_event.rb,v 1.48 2008-07-17 03:30:54 francis Exp $
class InfoRequestEvent < ActiveRecord::Base
belongs_to :info_request
@@ -69,7 +69,8 @@ class InfoRequestEvent < ActiveRecord::Base
[ :variety, 'V', "variety" ]
],
:if => :indexed_by_search,
- :eager_load => [ { :incoming_message => { :info_request => :public_body }}, :outgoing_message, { :info_request => [ :user, :public_body ] } ]
+ :eager_load => [ :incoming_message, :outgoing_message, { :info_request => [ :user, :public_body ] } ]
+
def requested_by
self.info_request.user.url_name
end
diff --git a/db/migrate/060_add_cached_main_text.rb b/db/migrate/060_add_cached_main_text.rb
new file mode 100644
index 000000000..c372b471c
--- /dev/null
+++ b/db/migrate/060_add_cached_main_text.rb
@@ -0,0 +1,9 @@
+class AddCachedMainText < ActiveRecord::Migration
+ def self.up
+ add_column :incoming_messages, :cached_main_body_text, :text
+ end
+
+ def self.down
+ remove_column :incoming_messages, :cached_main_body_text
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 939a07c2b..3212913c8 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -9,7 +9,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 59) do
+ActiveRecord::Schema.define(:version => 60) do
create_table "acts_as_xapian_jobs", :force => true do |t|
t.string "model", :null => false
@@ -25,6 +25,7 @@ ActiveRecord::Schema.define(:version => 59) do
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.text "cached_attachment_text"
+ t.text "cached_main_body_text"
end
create_table "info_request_events", :force => true do |t|
diff --git a/todo.txt b/todo.txt
index 55be3e5e7..aa0ee5f0b 100644
--- a/todo.txt
+++ b/todo.txt
@@ -55,7 +55,6 @@ Maybe remove public tracking completely
Comments interleaved with body
Antispam on contact form
-Incoming email to unknown FOI is a bit rubbish
Do something about shared spreadsheet
@@ -220,7 +219,6 @@ http://www.ico.gov.uk/Home/tools_and_resources/decision_notices.aspx
Description for each body as to what info it holds
Link to:
- Website itself (for general search) XXX can get this from domain of email and some scanning, with override needed
Aliases (not just short name, but multiple real names e.g. for museums)
Disclosure logs
Publication schemes (http://www.ico.gov.uk/what_we_cover/freedom_of_information/publication_schemes.aspx)
diff --git a/vendor/plugins/attr_lazy/init.rb b/vendor/plugins/attr_lazy/init.rb
new file mode 100644
index 000000000..7bd84b894
--- /dev/null
+++ b/vendor/plugins/attr_lazy/init.rb
@@ -0,0 +1,9 @@
+# attr_lazy/init.rb:
+#
+# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
+# Email: francis@mysociety.org; WWW: http://www.mysociety.org/
+#
+# $Id: init.rb,v 1.1 2008-07-17 03:30:55 francis Exp $
+
+require 'attr_lazy'
+
diff --git a/vendor/plugins/attr_lazy/lib/attr_lazy.rb b/vendor/plugins/attr_lazy/lib/attr_lazy.rb
new file mode 100644
index 000000000..eaa700ff9
--- /dev/null
+++ b/vendor/plugins/attr_lazy/lib/attr_lazy.rb
@@ -0,0 +1,94 @@
+# Taken from here, initial code at top (the refactoring doesn't work).
+# http://refactormycode.com/codes/219-activerecord-lazy-attribute-loading-plugin-for-rails
+
+module AttrLazy
+
+ def self.included(base_class)
+ base_class.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ def attr_lazy(*parameters)
+ columns = parameters
+ cattr_accessor :attr_lazy_configuration
+ self.attr_lazy_configuration = {
+ :columns => columns
+ }
+
+ # prevent barfing if the plugin is reloaded/loaded twice
+ unless self.respond_to? :column_names_for_join_base_without_attr_lazy
+ self.extend AttrLazy::SingletonMethods
+ class << self
+ alias_method_chain :construct_finder_sql, :attr_lazy
+ alias_method_chain :column_names_for_join_base, :attr_lazy
+ end
+ end
+
+ include AttrLazy::InstanceMethods
+ columns.each do |col|
+ class_eval("def #{col}; read_lazy_attribute :#{col}; end", __FILE__, __LINE__)
+ end
+
+ end
+
+ def column_names_for_join_base
+ column_names
+ end
+
+ def column_names_for_join_base_with_attr_lazy
+ @column_names_for_join_base ||= columns.collect{|c|
+ c.name unless attr_lazy_configuration[:columns].include?(c.name.to_sym)
+ }.compact
+ end
+ end
+
+ module SingletonMethods
+
+ def construct_finder_sql_with_attr_lazy(options)
+ options = {:select => unlazy_column_list}.merge(options)
+ construct_finder_sql_without_attr_lazy(options)
+ end
+
+ def unlazy_column_list
+ @unlazy ||= columns.collect do |c|
+ "#{quoted_table_name}.#{connection.quote_column_name(c.name)}" unless attr_lazy_columns.include?(c.name.to_sym)
+ end.compact.join ','
+ end
+
+ def attr_lazy_columns
+ attr_lazy_configuration[:columns]
+ end
+
+ end
+
+ module InstanceMethods
+
+ def read_lazy_attribute(att)
+ @lazy_attribute_values ||= {}
+ if attribute_names.include?(att.to_s)
+ read_attribute att
+ else
+ @lazy_attribute_values[att] ||= self.class.find(id, :select => att)[att]
+ end
+ end
+
+ end
+
+end
+
+class ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase
+ def column_names_with_alias
+ unless @column_names_with_alias
+ @column_names_with_alias = []
+ ([active_record.primary_key] + (active_record.column_names_for_join_base - [active_record.primary_key])).each_with_index do |column_name, i|
+ @column_names_with_alias << [column_name, "#{ aliased_prefix }_r#{ i }"]
+ end
+ end
+ return @column_names_with_alias
+ end
+end
+
+ActiveRecord::Base.send :include, AttrLazy
+
+
+