aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/alaveteli_file_types.rb92
-rw-r--r--lib/external_command.rb135
-rw-r--r--lib/memory_profiler.rb2
-rw-r--r--lib/public_body_categories.rb35
-rw-r--r--lib/tasks/stats.rake48
-rw-r--r--lib/tasks/temp.rake15
6 files changed, 310 insertions, 17 deletions
diff --git a/lib/alaveteli_file_types.rb b/lib/alaveteli_file_types.rb
new file mode 100644
index 000000000..076f8ffe8
--- /dev/null
+++ b/lib/alaveteli_file_types.rb
@@ -0,0 +1,92 @@
+require 'mahoro'
+
+class AlaveteliFileTypes
+ # To add an image, create a file with appropriate name corresponding to the
+ # mime type in public/images e.g. icon_image_tiff_large.png
+ FileExtensionToMimeType = {
+ "txt" => 'text/plain',
+ "pdf" => 'application/pdf',
+ "rtf" => 'application/rtf',
+ "doc" => 'application/vnd.ms-word',
+ "docx" => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ "xls" => 'application/vnd.ms-excel',
+ "xlsx" => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ "ppt" => 'application/vnd.ms-powerpoint',
+ "pptx" => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ "oft" => 'application/vnd.ms-outlook',
+ "msg" => 'application/vnd.ms-outlook',
+ "tnef" => 'application/ms-tnef',
+ "tif" => 'image/tiff',
+ "gif" => 'image/gif',
+ "jpg" => 'image/jpeg', # XXX add jpeg
+ "png" => 'image/png',
+ "bmp" => 'image/bmp',
+ "html" => 'text/html', # XXX add htm
+ "vcf" => 'text/x-vcard',
+ "zip" => 'application/zip',
+ "delivery-status" => 'message/delivery-status'
+ }
+ # XXX doesn't have way of choosing default for inverse map - might want to add
+ # one when you need it
+ FileExtensionToMimeTypeRev = FileExtensionToMimeType.invert
+
+ class << self
+ def all_extensions
+ return FileExtensionToMimeType.keys
+ end
+
+ # Given file name and its content, return most likely type
+ def filename_and_content_to_mimetype(filename, content)
+ # Try filename
+ ret = filename_to_mimetype(filename)
+ if !ret.nil?
+ return ret
+ end
+
+ # Otherwise look inside the file to work out the type.
+ # Mahoro is a Ruby binding for libmagic.
+ m = Mahoro.new(Mahoro::MIME)
+ mahoro_type = m.buffer(content)
+ mahoro_type.strip!
+ # XXX we shouldn't have to check empty? here, but Mahoro sometimes returns a blank line :(
+ # e.g. for InfoRequestEvent 17930
+ if mahoro_type.nil? || mahoro_type.empty?
+ return nil
+ end
+ # text/plain types sometimes come with a charset
+ mahoro_type.match(/^(.*);/)
+ if $1
+ mahoro_type = $1
+ end
+ # see if looks like a content type, or has something in it that does
+ # and return that
+ # mahoro returns junk "\012- application/msword" as mime type.
+ mahoro_type.match(/([a-z0-9.-]+\/[a-z0-9.-]+)/)
+ if $1
+ return $1
+ end
+ # otherwise we got junk back from mahoro
+ return nil
+ end
+
+ def filename_to_mimetype(filename)
+ if !filename
+ return nil
+ end
+ if filename.match(/\.([^.]+)$/i)
+ lext = $1.downcase
+ if FileExtensionToMimeType.include?(lext)
+ return FileExtensionToMimeType[lext]
+ end
+ end
+ return nil
+ end
+
+ def mimetype_to_extension(mimetype)
+ if FileExtensionToMimeTypeRev.include?(mimetype)
+ return FileExtensionToMimeTypeRev[mimetype]
+ end
+ return nil
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/external_command.rb b/lib/external_command.rb
new file mode 100644
index 000000000..96292854f
--- /dev/null
+++ b/lib/external_command.rb
@@ -0,0 +1,135 @@
+# Run an external command, capturing its stdout and stderr
+# streams into variables.
+#
+# So it’s rather like the `backtick` built-in, except that:
+# - The command is run as-is, rather than being parsed by the shell;
+# - Standard error is also captured.
+#
+# After the run() method has been called, the instance variables
+# out, err and status contain the contents of the process’s stdout,
+# the contents of its stderr, and the exit status.
+#
+# Example usage:
+# require 'external_command'
+# xc = ExternalCommand("ls", "-l").run()
+# puts "Ran ls -l with exit status #{xc.status}"
+# puts "===STDOUT===\n#{xc.out}"
+# puts "===STDERR===\n#{xc.err}"
+#
+# The out and err attributes are writeable. If you assign
+# a string, after calling the constructor and before calling
+# run(), then the subprocess output/error will be appended
+# to this string.
+
+# <rant author="robin">
+# In any sane language, this would be implemented with a
+# single child process. The parent process would block on
+# select(), and when the child process terminated, the
+# select call would be interrupted by a CHLD signal
+# and return EINTR. Unfortunately Ruby goes out of its
+# way to prevent this from working, automatically restarting
+# the select call if EINTR is returned. Therefore we
+# use a parent-child-grandchild arrangement, where the
+# parent blocks on select() and the child blocks on
+# waitpid(). When the child detects that the grandchild
+# has finished, it writes to a pipe that’s included in
+# the parent’s select() for this purpose.
+# </rant>
+
+class ExternalCommand
+ attr_accessor :out, :err
+ attr_reader :status
+
+ def initialize(cmd, *args)
+ @cmd = cmd
+ @args = args
+
+ # Strings to collect stdout and stderr from the child process
+ # These may be replaced by the caller, to append to existing strings.
+ @out = ""
+ @err = ""
+ @fin = ""
+ end
+
+ def run()
+ # Pipes for parent-child communication
+ @out_read, @out_write = IO::pipe
+ @err_read, @err_write = IO::pipe
+ @fin_read, @fin_write = IO::pipe
+
+ @pid = fork do
+ # Here we’re in the child process.
+ child_process
+ end
+
+ # Here we’re in the parent process.
+ parent_process
+
+ return self
+ end
+
+ private
+
+ def child_process()
+ # Reopen stdout and stderr to point at the pipes
+ STDOUT.reopen(@out_write)
+ STDERR.reopen(@err_write)
+
+ # Close all the filehandles other than the ones we intend to use.
+ ObjectSpace.each_object(IO) do |fh|
+ fh.close unless (
+ [STDOUT, STDERR, @fin_write].include?(fh) || fh.closed?)
+ end
+
+ Process::waitpid(fork { grandchild_process })
+ @fin_write.puts($?.exitstatus.to_s)
+
+ exit! 0
+ end
+
+ def grandchild_process()
+ exec(@cmd, *@args)
+
+ # This is only reached if the exec fails
+ @err_write.print("Failed to exec: #{[@cmd, *@args].join(' ')}")
+ exit! 99
+ end
+
+ def parent_process()
+ # Close the writing ends of the pipes
+ @out_write.close
+ @err_write.close
+ @fin_write.close
+
+ @fhs = {@out_read => @out, @err_read => @err, @fin_read => @fin}
+
+ while @fin.empty?
+ ok = read_data
+ if !ok
+ raise "select() timed out even with a nil (infinite) timeout"
+ end
+ end
+
+ while read_data(0)
+ # Pull out any data that’s left in the pipes
+ end
+
+ Process::waitpid(@pid)
+ @status = @fin.to_i
+ @out_read.close
+ @err_read.close
+ end
+
+ def read_data(timeout=nil)
+ ready_array = IO.select(@fhs.keys, [], [], timeout)
+ return false if ready_array.nil?
+ ready_array[0].each do |fh|
+ begin
+ @fhs[fh] << fh.readpartial(8192)
+ rescue EOFError
+ @fhs.delete fh
+ end
+ end
+ return true
+ end
+end
diff --git a/lib/memory_profiler.rb b/lib/memory_profiler.rb
index a1cb64d34..15e8457ee 100644
--- a/lib/memory_profiler.rb
+++ b/lib/memory_profiler.rb
@@ -65,7 +65,7 @@ class MemoryProfiler
prev.update curr
GC.start
rescue Exception => err
- STDERR.puts "** memory_profiler error: #{err}"
+ $stderr.puts "** memory_profiler error: #{err}"
end
sleep opt[:delay]
end
diff --git a/lib/public_body_categories.rb b/lib/public_body_categories.rb
index e01ce8bf7..e30ce8707 100644
--- a/lib/public_body_categories.rb
+++ b/lib/public_body_categories.rb
@@ -12,27 +12,27 @@ module PublicBodyCategories
"Miscellaneous",
[ "other", "Miscellaneous", "miscellaneous" ],
"Central government",
- [ "department", "Ministerial departments", "a ministerial department" ],
- [ "non_ministerial_department", "Non-ministerial departments", "a non-ministerial department" ],
- [ "executive_agency", "Executive agencies", "an executive agency" ],
- [ "government_office", "Government offices for the regions", "a government office for the regions" ],
+ [ "department", "Ministerial departments", "a ministerial department" ],
+ [ "non_ministerial_department", "Non-ministerial departments", "a non-ministerial department" ],
+ [ "executive_agency", "Executive agencies", "an executive agency" ],
+ [ "government_office", "Government offices for the regions", "a government office for the regions" ],
[ "advisory_committee", "Advisory committees", "an advisory committee" ],
[ "awc", "Agricultural wages committees", "an agriculatural wages committee" ],
[ "adhac", "Agricultural dwelling house advisory committees", "an agriculatural dwelling house advisory committee" ],
[ "newdeal", "New Deal for Communities partnership", "a New Deal for Communities partnership" ],
"Local and regional",
[ "local_council", "Local councils", "a local council" ],
- [ "parish_council", "Town and Parish councils", "a town or parish council"],
+ [ "parish_council", "Town and Parish councils", "a town or parish council"],
[ "housing_association", "Housing associations", "a housing association"],
[ "almo", "Housing ALMOs", "a housing ALMO"],
[ "municipal_bank", "Municipal bank", "a municipal bank"],
[ "nsbody", "North/south bodies", "a north/south body"],
[ "pbo", "Professional buying organisations", "a professional buying organisation"],
- [ "regional_assembly", "Regional assemblies", "a regional assembly"],
- [ "rda", "Regional development agencies", "a regional development agency" ],
+ [ "regional_assembly", "Regional assemblies", "a regional assembly"],
+ [ "rda", "Regional development agencies", "a regional development agency" ],
"Education",
[ "university", "Universities", "a university" ],
- [ "university_college", "University colleges", "a university college" ],
+ [ "university_college", "University colleges", "a university college" ],
[ "cambridge_college", "Cambridge colleges", "a Cambridge college" ],
[ "durham_college", "Durham colleges", "a Durham college" ],
[ "oxford_college", "Oxford colleges", "an Oxford college or permanent private hall" ],
@@ -45,12 +45,11 @@ module PublicBodyCategories
[ "lib_board", "Education and library boards", "an education and library board" ],
[ "rbc", "Regional Broadband Consortia", "a Regional Broadband Consortium" ],
"Environment",
- [ "npa", "National park authorities", "a national park authority" ],
+ [ "npa", "National park authorities", "a national park authority" ],
[ "rpa", "Regional park authorities", "a regional park authority" ],
- [ "sea_fishery_committee", "Sea fisheries committees", "a sea fisheries committee" ],
[ "watercompanies", "Water companies", "a water company" ],
+ [ "ifca", "Inshore fisheries and conservation authorities", "an inshore fisheries and conservation authority" ],
[ "idb", "Internal drainage boards", "an internal drainage board" ],
- [ "rfdc", "Regional flood defence committees", "a regional flood defence committee" ],
[ "wda", "Waste disposal authorities", "a waste disposal authority" ],
[ "zoo", "Zoos", "a zoo" ],
"Health",
@@ -70,22 +69,26 @@ module PublicBodyCategories
[ "military_college", "Military colleges", "a military college" ],
[ "security_services", "Security services", "a security services body" ],
"Emergency services and the courts",
- [ "police", "Police forces", "a police force" ],
- [ "police_authority", "Police authorities", "a police authority" ],
+ [ "police", "Police forces", "a police force" ],
+ [ "police_authority", "Police authorities", "a police authority" ],
[ "dpp", "District policing partnerships", "a district policing partnership" ],
[ "fire_service", "Fire and rescue services", "a fire and rescue service" ],
- [ "prob_board", "Probation boards", "a probation board" ],
+ [ "probation_trust", "Probation trusts", "a probation trust" ],
[ "rules_committee", "Rules commitees", "a rules committee" ],
[ "tribunal", "Tribunals", "a tribunal"],
"Transport",
[ "npte", "Passenger transport executives", "a passenger transport executive" ],
[ "port_authority", "Port authorities", "a port authority" ],
[ "scp", "Safety Camera Partnerships", "a safety camera partnership" ],
- [ "srp", "Safer Roads Partnership", "a safer roads partnership" ]
+ [ "srp", "Safer Roads Partnership", "a safer roads partnership" ],
+ "Defunct",
+ [ "prob_board", "Probation boards", "a probation board" ],
+ [ "rfdc", "Regional flood defence committees", "a regional flood defence committee" ],
+ [ "sea_fishery_committee", "Sea fisheries committees", "a sea fisheries committee" ],
]
# Arranged in different ways for different sorts of displaying
- CATEGORIES_WITH_DESCRIPTION = CATEGORIES_WITH_HEADINGS.select() { |a| a.instance_of?(Array) }
+ CATEGORIES_WITH_DESCRIPTION = CATEGORIES_WITH_HEADINGS.select() { |a| a.instance_of?(Array) }
CATEGORIES = CATEGORIES_WITH_DESCRIPTION.map() { |a| a[0] }
CATEGORIES_BY_TAG = Hash[*CATEGORIES_WITH_DESCRIPTION.map() { |a| a[0..1] }.flatten]
CATEGORY_SINGULAR_BY_TAG = Hash[*CATEGORIES_WITH_DESCRIPTION.map() { |a| [a[0],a[2]] }.flatten]
diff --git a/lib/tasks/stats.rake b/lib/tasks/stats.rake
new file mode 100644
index 000000000..e206ce951
--- /dev/null
+++ b/lib/tasks/stats.rake
@@ -0,0 +1,48 @@
+namespace :stats do
+
+ desc 'Produce transaction stats'
+ task :show => :environment do
+ month_starts = (Date.new(2009, 1)..Date.new(2011, 5)).select { |d| d.day == 1 }
+ headers = ['Period',
+ 'Requests sent',
+ 'Annotations added',
+ 'Track this request email signups',
+ 'Comments on own requests',
+ 'Follow up messages sent']
+ puts headers.join("\t")
+ month_starts.each do |month_start|
+ month_end = month_start.end_of_month
+ period = "#{month_start}-#{month_end}"
+ date_conditions = ['created_at >= ?
+ AND created_at < ?',
+ month_start, month_end+1]
+ request_count = InfoRequest.count(:conditions => date_conditions)
+ comment_count = Comment.count(:conditions => date_conditions)
+ track_conditions = ['track_type = ?
+ AND track_medium = ?
+ AND created_at >= ?
+ AND created_at < ?',
+ 'request_updates', 'email_daily', month_start, month_end+1]
+ email_request_track_count = TrackThing.count(:conditions => track_conditions)
+ comment_on_own_request_conditions = ['comments.user_id = info_requests.user_id
+ AND comments.created_at >= ?
+ AND comments.created_at < ?',
+ month_start, month_end+1]
+ comment_on_own_request_count = Comment.count(:conditions => comment_on_own_request_conditions,
+ :include => :info_request)
+
+ followup_conditions = ['message_type = ?
+ AND created_at >= ?
+ AND created_at < ?',
+ 'followup', month_start, month_end+1]
+ follow_up_count = OutgoingMessage.count(:conditions => followup_conditions)
+ puts [period,
+ request_count,
+ comment_count,
+ email_request_track_count,
+ comment_on_own_request_count,
+ follow_up_count].join("\t")
+ end
+ end
+
+end \ No newline at end of file
diff --git a/lib/tasks/temp.rake b/lib/tasks/temp.rake
new file mode 100644
index 000000000..ce04c7ddd
--- /dev/null
+++ b/lib/tasks/temp.rake
@@ -0,0 +1,15 @@
+namespace :temp do
+
+ desc "Remove plaintext passwords from post_redirect params"
+ task :remove_post_redirect_passwords => :environment do
+ PostRedirect.find_each(:conditions => ['post_params_yaml is not null']) do |post_redirect|
+ if post_redirect.post_params && post_redirect.post_params[:signchangeemail] && post_redirect.post_params[:signchangeemail][:password]
+ params = post_redirect.post_params
+ params[:signchangeemail].delete(:password)
+ post_redirect.post_params = params
+ post_redirect.save!
+ end
+ end
+ end
+
+end