inbox_items 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 31c4e1e25a30d5a4a4431ac38a466ee880a50d1d
4
+ data.tar.gz: 627dd5a1fd65947d636aaf3857d16e95787b43b4
5
+ SHA512:
6
+ metadata.gz: 857cc4f293527dd8e845d601a717c7c29c8a90f740115f5150e0f0b40b78f62537ebcc98a6d33ae438ac5da9d0d779240d26123ca106a20bc31c7e8b22d5ec68
7
+ data.tar.gz: 1f1c47a6a70acc8c024e724063e3b981ec3b7a52de385a77c8c140eea1b96bd99f8cf1beda135f533c3a14540c53babf9124a0ee1ce421a8ccd4888a5fa0f4e9
data/NO-LICENSE ADDED
@@ -0,0 +1 @@
1
+ Copyright 2015 Gordon B. Isnor
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'InboxItems'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task default: :test
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,11 @@
1
+ var logItemsReady = function () {
2
+
3
+ $('input[name="inbox_item[convert]"]').on('switchChange.bootstrapSwitch', function (event, state) {
4
+ var target = $(this).data('target');
5
+ $(target).toggleClass('invisible');
6
+ });
7
+
8
+ };
9
+
10
+ $(document).ready(logItemsReady);
11
+ $(document).on('page:load', logItemsReady);
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,4 @@
1
+ class InboxItems::ApplicationController < ApplicationController
2
+ end
3
+
4
+
@@ -0,0 +1,47 @@
1
+ module InboxItems
2
+ class InboxItemsController < ApplicationController
3
+
4
+ def index
5
+ @inbox_items = current_user.inbox_items.order(id: :desc).unconverted.all
6
+ end
7
+
8
+ def edit
9
+ @inbox_item = current_user.inbox_items.find(params[:id])
10
+ @inbox_item.mark_as_read! if @inbox_item.may_mark_as_read?
11
+ @inbox_item.convert = true unless @inbox_item.converted?
12
+ @inbox_item.log_type = 'lab_notes'
13
+ @inbox_item.date = @inbox_item.created_at.to_date
14
+ @projects = current_user.projects.all.collect { |p| [p.project_name, p.id] }
15
+ @inbox_item.project_id = @projects.first[1] if @projects.count == 1
16
+ end
17
+
18
+ def update
19
+ @inbox_item = current_user.inbox_items.find(params[:id])
20
+
21
+ if @inbox_item.update_attributes(item_params)
22
+ @inbox_item.convert_to_log_item! if item_params[:convert] == '1'
23
+ redirect_to inbox_items_path, notice: 'Updated'
24
+ end
25
+ end
26
+
27
+ def destroy
28
+ @inbox_item = current_user.inbox_items.find(params[:id])
29
+ @inbox_item.destroy
30
+ redirect_to inbox_items_path, notice: t(:inbox_item_was_removed)
31
+ end
32
+
33
+ def file_redirect
34
+ @inbox_item = current_user.inbox_items.find(params[:id])
35
+ @file_upload = @inbox_item.file_uploads.find(params[:file_upload_id])
36
+ redirect_to @file_upload.public_url if @file_upload.url.present?
37
+ end
38
+
39
+ def item_params
40
+ params.require(:inbox_item).permit(:subject, :body, :project_id, :log_time, :convert, :log_type, :date)
41
+ end
42
+ private :item_params
43
+
44
+ end
45
+ end
46
+
47
+
@@ -0,0 +1,4 @@
1
+ module InboxItems
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,10 @@
1
+ module InboxItems
2
+ class MoveEmailAttachmentToS3Job < ActiveJob::Base
3
+ queue_as :default
4
+
5
+ def perform(file_upload)
6
+ InboxItems::MoveFileUploadToS3.call(file_upload)
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,54 @@
1
+ require 'aasm'
2
+
3
+ class InboxItem < ActiveRecord::Base
4
+ include AASM
5
+
6
+ belongs_to :user
7
+ belongs_to :log_entry
8
+ has_many :file_uploads
9
+
10
+ attr_accessor :convert, :project_id, :log_time, :log_type, :date
11
+
12
+ aasm whiny_transitions: true do
13
+ state :unread, initial: true
14
+ state :read
15
+ state :converted
16
+ event :mark_as_read do
17
+ transitions from: :unread, to: :read
18
+ end
19
+ event :convert_to_log_item do
20
+ transitions from: :read, to: :converted
21
+ before do
22
+ do_conversion
23
+ end
24
+ end
25
+ end
26
+
27
+ def do_conversion
28
+ transaction do
29
+ project = user.projects.find(project_id)
30
+
31
+ l = project.log_entries.build({
32
+ user_id: user.id,
33
+ log_time: self.log_time,
34
+ date: self.date,
35
+ log_type: self.log_type,
36
+ task_name: self.subject,
37
+ task_description: self.body
38
+ })
39
+
40
+ l.save!
41
+ update_attribute(:log_entry_id, l.id)
42
+ file_uploads.update_all(log_entry_id: l.id)
43
+ end
44
+ end
45
+
46
+ def self.unread
47
+ where(aasm_state: 'unread')
48
+ end
49
+
50
+ def self.unconverted
51
+ where(aasm_state: ['unread', 'read'])
52
+ end
53
+
54
+ end
@@ -0,0 +1,77 @@
1
+ class EmailProcessor
2
+
3
+ def initialize(email)
4
+ @email = email
5
+ @user = User.find_by_email(@email.from[:email])
6
+ end
7
+
8
+ def process
9
+
10
+ inbox_item = create_inbox_item
11
+
12
+ @email.attachments.each_with_index do |attachment,i|
13
+ begin
14
+ file_upload = create_file_upload(attachment, inbox_item)
15
+ InboxItems::MoveEmailAttachmentToS3Job.perform_later(file_upload) if file_upload
16
+ rescue => e
17
+ Rails.logger.info "Rescue from error at EmailProcessor attachments each with index line 17: #{e}"
18
+ next
19
+ end
20
+ end # end each attachment
21
+
22
+ end # end process
23
+
24
+ def create_inbox_item
25
+ inbox_item = @user.inbox_items.new(subject: formatted_subject, body: @email.raw_body)
26
+ inbox_item.save
27
+ return inbox_item
28
+ rescue => e
29
+ Rails.logger.info "Rescue at Email Processor create_inbox_item, line 28: #{e}"
30
+ return nil
31
+ end
32
+
33
+ def formatted_subject
34
+ @email.subject.gsub(/FWD:|Fwd:|Fw:|Re:|RE:/, '').strip
35
+ end
36
+
37
+ def formatted_body
38
+ email.body.gsub(/Sent from my iPhone/, '')
39
+ end
40
+
41
+ def directory
42
+ File.join(Rails.root, "public", "system", "email_attachments")
43
+ end
44
+
45
+ def create_file_upload(attachment, inbox_item)
46
+ raise "No inbox item" unless inbox_item.present?
47
+
48
+ path = store_file_locally(attachment)
49
+
50
+ file_upload = FileUpload.new({
51
+ inbox_item_id: inbox_item.id,
52
+ filename: attachment.original_filename,
53
+ mime: attachment.content_type,
54
+ file_type: attachment.content_type.split('/')[0],
55
+ local_file: path,
56
+ path: path
57
+ })
58
+ file_upload.save
59
+ return file_upload
60
+ rescue => e
61
+ Rails.logger.info "Rescue at Email Processor create_file_upload line 48: #{e}"
62
+ return false
63
+ end
64
+
65
+ def store_file_locally(attachment)
66
+ name = attachment.original_filename
67
+ path = File.join(directory, name)
68
+ File.open(path, "wb") { |f| f.write(attachment.read) }
69
+ return path
70
+ rescue => e
71
+ Rails.logger.info "Rescue at EmailProcessor store_file_locally line 57: #{e}"
72
+ end
73
+
74
+ end # end email process class
75
+
76
+
77
+
@@ -0,0 +1,23 @@
1
+ module InboxItems
2
+
3
+ class MoveFileUploadToS3
4
+
5
+ def self.call(file_upload)
6
+
7
+ path = "file_uploads/#{file_upload.id}_#{file_upload.created_at.to_i}/#{file_upload.filename}"
8
+
9
+ s3file = Amazon.put({file: file_upload.local_file, path: path, bucket: Settings.s3_bucket})
10
+
11
+ if s3file
12
+ url = s3file.url_for(:read, {secure: false}).to_s.split('?')[0]
13
+ file_upload.update_attributes(url: url, path: path)
14
+ File.unlink(file_upload.local_file)
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+
23
+
@@ -0,0 +1,23 @@
1
+ %tr{data: { toggle: 'collapse', target: "#collapse_#{inbox_item.id}"}, class: "#{'unread' if inbox_item.unread?}" }
2
+
3
+ %td.subject= link_to inbox_item.subject, edit_inbox_item_path(inbox_item)
4
+ %td.body
5
+ = inbox_item.body
6
+ %div.collapse.collapse-panel{id: "collapse_#{inbox_item.id}"}
7
+ - if inbox_item.file_uploads.any?
8
+ %ul.list-unstyled
9
+ - inbox_item.file_uploads.each do |file|
10
+ %li= link_to file.filename, file_redirect_inbox_item_path(inbox_item, file_upload_id: file.id), target: '_blank'
11
+
12
+ %td.file.text-center= glyph(:file) if inbox_item.file_uploads.present?
13
+ %td.edit.text-center
14
+ = link_to edit_inbox_item_path(inbox_item), title: t(:edit) do
15
+ %span.glyphicon.glyphicon-import
16
+ %td.remove.text-center
17
+ = link_to inbox_item_path(inbox_item), method: :delete, data:{confirm: t(:are_you_sure)}, title: t(:remove) do
18
+ = content_tag :i, nil, class: 'ion-close-circled'
19
+
20
+
21
+
22
+
23
+
@@ -0,0 +1,51 @@
1
+ :css
2
+ .unread * {
3
+ font-weight: bold;
4
+ }
5
+ #conversion {
6
+ -webkit-transition: opacity 0.25s;
7
+ transition: opacity 0.25s;
8
+ }
9
+ #conversion.invisible {
10
+ opacity: 0;
11
+ }
12
+
13
+ %br
14
+ .alert.alert-info Feel free to edit this email into a more concise log
15
+
16
+ = simple_nested_form_for @inbox_item do |f|
17
+ .menubar
18
+ .sidebar-toggler.visible-xs
19
+ %i.ion-navicon
20
+ .page-title= t(:inbox)
21
+ .content-wrapper
22
+ .row
23
+ .col-sm-12= f.input :subject
24
+ .col-sm-12= f.input :body, input_html: {rows: 4}
25
+
26
+ - unless @inbox_item.converted?
27
+ .row
28
+ .col-sm-3
29
+ = f.label :convert, 'Import'
30
+ %br
31
+ = f.check_box :convert, data: {target: '#conversion'}, class: 'switch'
32
+ %br
33
+ #conversion.col-sm-9
34
+ .row
35
+ .col-sm-4= f.input :project_id, collection: @projects, prompt: 'Project...', input_html: {class: 'chosen'}
36
+ .col-sm-4= f.input :date, input_html: {class: 'datepicker'}
37
+ .col-sm-4
38
+ %br
39
+ .btn-group{data:{toggle: 'buttons'}}
40
+ %label.btn-xs.btn.btn-primary{class: "#{'active' if f.object.log_type == 'lab_notes'}"}
41
+ = f.radio_button :log_type, 'lab_notes', autocomplete: 'off'
42
+ = t(:lab_notes)
43
+ %label.btn.btn-xs.btn-primary{class: "#{'active' if f.object.log_type == 'log_data'}"}
44
+ = f.radio_button :log_type, 'log_data', autocomplete: 'off'
45
+ = t(:log_data)
46
+ %label.btn.btn-xs.btn-primary{class: "#{'active' if f.object.log_type == 'progress'}"}
47
+ = f.radio_button :log_type, 'progress', autocomplete: 'off'
48
+ = t(:progress)
49
+
50
+ %hr
51
+ = f.submit 'Convert', class: 'btn btn-success', disable_with: t('please_wait')
@@ -0,0 +1,23 @@
1
+ .menubar
2
+ .sidebar-toggler.visible-xs
3
+ %i.ion-navicon
4
+ .page-title= t(:inbox)
5
+
6
+ .content-wrapper
7
+ %br
8
+ - if @inbox_items.blank?
9
+ %h2= t(:no_items_found)
10
+ - else
11
+ .chart
12
+ .table-responsive
13
+ %table#inbox_items.table.table-striped
14
+ %thead
15
+ %tr
16
+ %th= t(:subject)
17
+ %th= t(:body)
18
+ %th.text-center= t(:file)
19
+ %th.text-center= t(:import)
20
+ %th.text-center= t(:delete)
21
+ %tbody= render @inbox_items
22
+
23
+ = content_for :body_id, "reports"
@@ -0,0 +1,5 @@
1
+ en:
2
+ inbox: Inbox
3
+ import: Import
4
+ delete: Delete
5
+ no_items_found: No items found
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ InboxItems::Engine.routes.draw do
2
+
3
+ resources :inbox_items, path: '/' do
4
+ member do
5
+ get :file_redirect
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,13 @@
1
+ class CreateInboxItems < ActiveRecord::Migration
2
+ def change
3
+ create_table :inbox_items do |t|
4
+ t.string :subject
5
+ t.text :body
6
+ t.integer :user_id
7
+ t.integer :log_entry_id
8
+ t.string :state
9
+ t.timestamps null: false
10
+ end
11
+ add_index :inbox_items, :user_id
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class AddInboxItemIdToFileUploads < ActiveRecord::Migration
2
+ def change
3
+ add_column :file_uploads, :inbox_item_id, :integer
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class RenameStateToAasmState < ActiveRecord::Migration
2
+ def change
3
+ rename_column :inbox_items, :state, :aasm_state
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddLocalFileToFileUploads < ActiveRecord::Migration
2
+ def change
3
+ add_column :file_uploads, :local_file, :string
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ module InboxItems
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace InboxItems
4
+ config.generators do |g|
5
+ g.test_framework :rspec
6
+ g.fixture_replacement :factory_girl, :dir => 'spec/factories'
7
+ end
8
+ initializer :append_migrations do |app|
9
+ unless app.root.to_s.match root.to_s
10
+ config.paths["db/migrate"].expanded.each do |expanded_path|
11
+ app.config.paths["db/migrate"] << expanded_path
12
+ end
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module InboxItems
2
+ VERSION = "0.1.6"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "inbox_items/engine"
2
+
3
+ module InboxItems
4
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :inbox_items do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: inbox_items
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.6
5
+ platform: ruby
6
+ authors:
7
+ - Gordon B. Isnor
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: aasm
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Inbox items engine
56
+ email:
57
+ - gordonbisnor@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - NO-LICENSE
63
+ - Rakefile
64
+ - app/assets/javascripts/inbox_items/application.js
65
+ - app/assets/javascripts/inbox_items/inbox_items.js
66
+ - app/assets/stylesheets/inbox_items/application.css
67
+ - app/controllers/inbox_items/application_controller.rb
68
+ - app/controllers/inbox_items/inbox_items_controller.rb
69
+ - app/helpers/inbox_items/application_helper.rb
70
+ - app/jobs/move_email_attachment_to_s3_job.rb
71
+ - app/models/inbox_item.rb
72
+ - app/services/email_processor.rb
73
+ - app/services/move_file_upload_to_s3.rb
74
+ - app/views/inbox_items/inbox_items/_inbox_item.html.haml
75
+ - app/views/inbox_items/inbox_items/edit.html.haml
76
+ - app/views/inbox_items/inbox_items/index.html.haml
77
+ - config/locales/inbox_items.yml
78
+ - config/routes.rb
79
+ - db/migrate/20150530190623_create_inbox_items.rb
80
+ - db/migrate/20150531032517_add_inbox_item_id_to_file_uploads.rb
81
+ - db/migrate/20150601204341_rename_state_to_aasm_state.rb
82
+ - db/migrate/20150602183807_add_local_file_to_file_uploads.rb
83
+ - lib/inbox_items.rb
84
+ - lib/inbox_items/engine.rb
85
+ - lib/inbox_items/version.rb
86
+ - lib/tasks/inbox_items_tasks.rake
87
+ homepage: http://github.com/gordonbisnor/inbox_items
88
+ licenses:
89
+ - NO LICENSE
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.2.2
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Inbox items engine
111
+ test_files: []