wco_models 3.1.0.52 → 3.1.0.54

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ae41306d1c2d1a86acdc705f296feae8ba6cb9d31dcef4c1794cabaa69d8b2c
4
- data.tar.gz: 1d34eb80dc069bb504a62a08ee25da17c92cebb5805399ff570954c8bd163c3e
3
+ metadata.gz: 41ed3d61aaea366b6925edbffd7cc4cf955bfffaf21a46f029ab1e3d463e7e17
4
+ data.tar.gz: efcfd16d8db203ab26fcbe58ea9b82cb08a4d32023c986de52a9a9f61d5eb740
5
5
  SHA512:
6
- metadata.gz: 46d410929c0405cffbd123b8b376a3629fc748e75df699b56bd94d70b9576c22c09358e64c32f3bfea59fc6b1bd7ed471cb70af2c9c4cd60170b75599df4a0d6
7
- data.tar.gz: 5b7280694b1ba7790c33e70e1f0c8555733f6d2912e7af2bdaba73516adfe6417e1594384ca84896cf1d7b6ea3f9cdd1dcf419188f38dd19f0fc69ce1f20a37c
6
+ metadata.gz: 2be84afa6c6de2b06ba0257553f100523fe052fabddc365292ae9cbe450070e95bb34e2a152eb026751f582e1167ea03b3253566448e42b4e69d2e485a2a6674
7
+ data.tar.gz: 1a627331788250f7664b617dba05db34a74991aab270bc1604a2c3906860828a1940f8db049fc41ec7c335eaa15d38156bb3e8136bef46471450e62167fd7302
@@ -113,6 +113,10 @@ h5.title {
113
113
  display: flex;
114
114
  }
115
115
 
116
+ .h-500px {
117
+ min-height: 500px;
118
+ }
119
+
116
120
  /* M */
117
121
 
118
122
  .maxwidth {
@@ -7,23 +7,24 @@ class Wco::Gallery
7
7
  include Wco::Utils
8
8
  store_in collection: 'galleries'
9
9
 
10
- PER_PAGE = 6
11
10
 
12
11
  field :name
13
12
  validates :name, :uniqueness => true
14
13
  index({ :name => -1 }) ## 2023-09-23 removed uniqueness
15
14
 
15
+ field :slug
16
+ index({ :slug => -1 }, { :unique => true })
17
+ validates :slug, presence: true, uniqueness: true
18
+ before_validation :set_slug, :on => :create
19
+
16
20
  field :subhead
17
21
  field :descr, :as => :description
18
22
 
19
23
  field :is_public, type: Boolean, default: false
20
24
  # has_and_belongs_to_many :shared_profiles, :class_name => 'Wco::Profile', :inverse_of => :shared_galleries
21
25
 
22
- field :is_trash, type: Boolean, default: false
23
- field :is_done, type: Boolean, default: false
24
-
25
26
  def published
26
- where({ :is_public => true, :is_trash => false }).order_by({ :created_at => :desc })
27
+ where({ :is_public => true }).order_by({ :created_at => :desc })
27
28
  end
28
29
 
29
30
  field :x, :type => Float
@@ -31,15 +32,9 @@ class Wco::Gallery
31
32
  field :z, :type => Float
32
33
 
33
34
  field :lang, :default => 'en'
34
- # field :username
35
- # field :lead_id, type: :integer
36
35
 
37
- field :slug
38
- index({ :slug => -1 }, { :unique => true })
39
- validates :slug, presence: true, uniqueness: true
40
- before_validation :set_slug, :on => :create
41
36
 
42
- def self.list conditions = { :is_trash => false }
37
+ def self.list conditions = {}
43
38
  out = self.unscoped.where( conditions ).order_by( :created_at => :desc )
44
39
  [['', nil]] + out.map { |item| [ "#{item.created_at.strftime('%Y%m%d')} #{item.name}", item.id ] }
45
40
  end
@@ -14,6 +14,12 @@ class Wco::Lead
14
14
  field :address
15
15
 
16
16
  belongs_to :leadset, class_name: 'Wco::Leadset'
17
+ before_validation :set_leadset, on: :create
18
+ def set_leadset
19
+ domain = email.split('@')[1]
20
+ self.leadset ||= Wco::Leadset.find_or_create_by({ company_url: domain })
21
+ end
22
+
17
23
  has_one :photo, class_name: 'Wco::Photo'
18
24
 
19
25
  has_many :email_messages, class_name: '::WcoEmail::Message', inverse_of: :lead
@@ -51,14 +51,10 @@ class Wco::Photo
51
51
  s3_region: ::S3_CREDENTIALS[:region]
52
52
  validates_attachment_content_type :photo, :content_type => ["image/webp", "image/jpg", "image/jpeg", "image/png", "image/gif", 'application/octet-stream' ]
53
53
 
54
- def self.n_per_manager_gallery
55
- 25
56
- end
57
-
58
54
  def export_fields
59
55
  %w|
60
56
  gallery_id
61
- name descr weight is_public is_trash
57
+ name descr weight
62
58
 
63
59
  photo_file_name photo_content_type photo_file_size photo_updated_at photo_fingerprint
64
60
  |
@@ -35,8 +35,10 @@ class WcoEmail::Context
35
35
  def from_email
36
36
  if self[:from_email].presence
37
37
  return self[:from_email]
38
- else
38
+ elsif tmpl&.from_email
39
39
  return tmpl.from_email
40
+ else
41
+ return DEFAULT_FROM_EMAIL
40
42
  end
41
43
  end
42
44
 
@@ -1,4 +1,6 @@
1
1
 
2
+ DEFAULT_FROM_EMAIL = 'victor@wasya.co'
3
+
2
4
  class WcoEmail::EmailTemplate
3
5
  include Mongoid::Document
4
6
  include Mongoid::Timestamps
@@ -86,9 +88,20 @@ class WcoEmail::EmailTemplate
86
88
  'Wasya Co Mailer <no-reply@wco.com.de>',
87
89
  'Wasya Co Mailer <wasyacomailer@gmail.com>',
88
90
  ];
91
+ FROM_EMAILS_2 = [
92
+
93
+ [ 'Victor Pudeyev <piousbox@gmail.com>', 'piousbox@gmail.com' ],
94
+ [ 'Victor Pudeyev <victor@piousbox.com>', 'victor@piousbox.com' ],
95
+ [ 'Victor Pudeyev <no-reply@piousbox.com>', 'no-reply@piousbox.com' ],
96
+
97
+ [ 'WasyaCo Consulting <no-reply@wasya.co>', 'no-reply@wasya.co' ],
98
+ [ 'Victor Pudeyev <victor@wasya.co>', 'victor@wasya.co' ],
99
+
100
+ ];
89
101
  field :from_email
90
- def self.from_email_list
91
- [ [nil, nil] ] + FROM_EMAILS.map { |i| [i, i] }
102
+ def self.from_emails_list
103
+ # [ [nil, nil] ] + FROM_EMAILS.map { |i| [i, i] }
104
+ FROM_EMAILS_2
92
105
  end
93
106
 
94
107
  SIGNATURE = <<~AOL
@@ -118,7 +131,7 @@ class WcoEmail::EmailTemplate
118
131
  def self.blank; self.blank_template; end
119
132
 
120
133
  def self.list
121
- all.map { |p| [ p.id, p.slug ] }
134
+ all.map { |p| [ p.slug, p.id ] }
122
135
  end
123
136
 
124
137
  end
@@ -67,7 +67,7 @@ class WcoEmail::Message
67
67
  def received_at ; date ; end
68
68
 
69
69
  belongs_to :conversation, class_name: 'WcoEmail::Conversation'
70
- def conv ; email_conversation ; end
70
+ def conv ; conversation ; end
71
71
 
72
72
  belongs_to :stub, class_name: 'WcoEmail::MessageStub'
73
73
 
@@ -1,4 +1,10 @@
1
1
 
2
+ =begin
3
+
4
+ key = '01070861907736276273039d9ee-c69a3509-5c85-481d-822e-ba65c204e1ba-000000@eu-central-1.amazonses.com'
5
+
6
+ =end
7
+
2
8
  ##
3
9
  ## Only object_key, no validations.
4
10
  ## 2023-12-28 _vp_ Continue.
@@ -13,9 +19,9 @@ class WcoEmail::MessageStub
13
19
  STATUS_PENDING = 'status_pending'
14
20
  STATUS_PROCESSED = 'status_processed'
15
21
  STATUS_FAILED = 'status_failed'
16
- STATUSES = [ STATUS_PENDING, STATUS_PROCESSED ]
22
+ STATUSES = [ STATUS_PENDING, STATUS_PROCESSED, STATUS_FAILED ]
17
23
  field :status, default: STATUS_PENDING
18
- scope :pending, ->{ where( status: 'status_pending' ) }
24
+ scope :pending, ->{ where( status: STATUS_PENDING ) }
19
25
 
20
26
  field :bucket # 'ish-ses', 'ish-ses-2024'
21
27
 
@@ -31,8 +37,180 @@ class WcoEmail::MessageStub
31
37
  {}
32
38
  AOL
33
39
 
34
- ## This only saves a local messafe from mbox to s3.
35
- def do_process message
40
+ def do_process
41
+ @client ||= Aws::S3::Client.new({
42
+ region: ::S3_CREDENTIALS[:region_ses],
43
+ access_key_id: ::S3_CREDENTIALS[:access_key_id_ses],
44
+ secret_access_key: ::S3_CREDENTIALS[:secret_access_key_ses],
45
+ })
46
+ stub = self
47
+
48
+ raw = @client.get_object( bucket: stub.bucket, key: stub.object_key ).body.read
49
+ the_mail = Mail.new( raw )
50
+
51
+ message_id = the_mail.header['message-id']&.decoded
52
+ message_id ||= "#{the_mail.date&.iso8601}::#{the_mail.from}"
53
+ # puts! message_id, 'message_id'
54
+
55
+ in_reply_to_id = the_mail.header['in-reply-to']&.to_s
56
+ puts! in_reply_to_id, 'in_reply_to_id'
57
+
58
+ the_mail.to = [ 'NO-RECIPIENT' ] if !the_mail.to
59
+ subject = WcoEmail::Message.strip_emoji( the_mail.subject || '(wco-no-subject)' )
60
+ puts! subject, 'subject'
61
+
62
+ ## Conversation
63
+ if in_reply_to_id
64
+ in_reply_to_msg = WcoEmail::Message.where({ message_id: in_reply_to_id }).first
65
+ if !in_reply_to_msg
66
+ conv = WcoEmail::Conversation.find_or_create_by({
67
+ subject: subject,
68
+ })
69
+ in_reply_to_msg = WcoEmail::Message.find_or_create_by({
70
+ message_id: in_reply_to_id,
71
+ conversation: conv,
72
+ })
73
+ end
74
+ conv = in_reply_to_msg.conversation
75
+ else
76
+ conv = WcoEmail::Conversation.find_or_create_by({
77
+ subject: subject,
78
+ })
79
+ end
80
+
81
+
82
+ ## Leadset, Lead
83
+ from = the_mail.from ? the_mail.from[0] : "nobody@unknown-doma.in"
84
+ domain = from.split('@')[1]
85
+ leadset = Wco::Leadset.where( company_url: domain ).first
86
+ leadset ||= Wco::Leadset.create( company_url: domain, email: from )
87
+ lead = Wco::Lead.find_or_create_by( email: from, leadset: leadset )
88
+
89
+
90
+ message = WcoEmail::Message.unscoped.where( message_id: message_id ).first
91
+ if message
92
+ message.message_id = "#{Time.now.strftime('%Y%m%d')}-trash-#{message.message_id}"
93
+ message.object_key = "#{Time.now.strftime('%Y%m%d')}-trash-#{message.object_key}"
94
+ message.save!( validate: false )
95
+ message.delete
96
+ end
97
+ @message = WcoEmail::Message.create!({
98
+ stub: stub,
99
+ conversation: conv,
100
+ lead: lead,
101
+
102
+ message_id: message_id,
103
+ in_reply_to_id: in_reply_to_id,
104
+ object_key: stub.object_key,
105
+
106
+ subject: subject,
107
+ date: the_mail.date,
108
+
109
+ from: from,
110
+ froms: the_mail.from,
111
+
112
+ to: the_mail.to ? the_mail.to[0] : nil,
113
+ tos: the_mail.to,
114
+
115
+ cc: the_mail.cc ? the_mail.cc[0] : nil,
116
+ ccs: the_mail.cc,
117
+ })
118
+
119
+ ## Parts
120
+ the_mail.parts.each do |part|
121
+ @message.churn_subpart( part )
122
+ end
123
+ @message.save
124
+
125
+ if the_mail.parts.length == 0
126
+ body = the_mail.body.decoded.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
127
+ if the_mail.content_type&.include?('text/html')
128
+ @message.part_html = body
129
+ elsif the_mail.content_type&.include?('text/plain')
130
+ @message.part_txt = body
131
+ elsif the_mail.content_type.blank?
132
+ @message.part_txt = body
133
+ else
134
+ @message.logs.push "mail body of unknown type: #{the_mail.content_type}"
135
+ end
136
+ @message.save
137
+ end
138
+
139
+ ## Attachments
140
+ the_mail.attachments.each do |att|
141
+ @message.save_attachment( att )
142
+ end
143
+
144
+ if !@message.save
145
+ puts! @message.errors.full_messages.join(", "), "Could not save @message"
146
+ end
147
+
148
+
149
+ conv.leads.push lead
150
+ conv.save
151
+ # lead.conversations.push conv
152
+ # lead.save
153
+
154
+ the_mail.cc&.each do |cc|
155
+ domain = cc.split('@')[1] rescue 'unknown.domain'
156
+ leadset = Wco::Leadset.find_or_create_by( company_url: domain )
157
+ Wco::Lead.find_or_create_by( email: cc, leadset: leadset )
158
+ end
159
+
160
+ conv.update_attributes({
161
+ status: WcoEmail::Conversation::STATUS_UNREAD,
162
+ latest_at: the_mail.date || Time.now.to_datetime,
163
+ from_emails: ( conv.from_emails + the_mail.from ).uniq,
164
+ preview: @message.preview_str,
165
+ })
166
+
167
+ ##
168
+ ## Tags
169
+ ##
170
+ inbox_tag = Wco::Tag.inbox
171
+ conv.tags.push inbox_tag
172
+ conv.tags.push stub.tags
173
+ conv.save
174
+ # inbox_tag.conversations.push conv
175
+ # inbox_tag.save
176
+
177
+
178
+ ## Actions & Filters
179
+ email_filters = WcoEmail::EmailFilter.active
180
+ email_filters.each do |filter|
181
+ if ( filter.from_regex.blank? || @message.from.match( filter.from_regex ) ) &&
182
+ ( filter.from_exact.blank? || @message.from.downcase.include?( filter.from_exact&.downcase ) ) &&
183
+ ( filter.body_exact.blank? || @message.part_html&.include?( filter.body_exact ) ) &&
184
+ ( filter.subject_regex.blank? || @message.subject.match( filter.subject_regex ) ) &&
185
+ ( filter.subject_exact.blank? || @message.subject.downcase.include?( filter.subject_exact&.downcase ) )
186
+
187
+ # || MiaTagger.analyze( @message.part_html, :is_spammy_recruite ).score > .5
188
+
189
+ puts! "applying filter #{filter} to conv #{conv}" if DEBUG
190
+
191
+ @message.apply_filter( filter )
192
+ end
193
+ end
194
+
195
+ stub.update_attributes({ status: WcoEmail::MessageStub::STATUS_PROCESSED })
196
+
197
+ ## Notification
198
+ config = JSON.parse(stub.config)
199
+ if config['skip_notification']
200
+ ;
201
+ else
202
+ conv = WcoEmail::Conversation.find( conv.id )
203
+ if conv.tags.include? inbox_tag
204
+ out = WcoEmail::ApplicationMailer.forwarder_notify( @message.id.to_s )
205
+ Rails.env.production? ? out.deliver_later : out.deliver_now
206
+ end
207
+ end
208
+
209
+ puts 'ok'
210
+ end
211
+
212
+ ## This only saves a local message from mbox to s3.
213
+ def save_mbox_to_m3 message
36
214
  the_mail = Mail.new(message)
37
215
  key = the_mail.message_id || "no-key-#{Time.now.to_i}.#{rand(1000)}"
38
216
 
@@ -82,7 +260,7 @@ class WcoEmail::MessageStub
82
260
 
83
261
  if message
84
262
  if skip < @count
85
- do_process message
263
+ save_mbox_to_m3 message
86
264
  print "#{@count}."
87
265
  else
88
266
  print "s-#{@count}."
@@ -98,7 +276,7 @@ class WcoEmail::MessageStub
98
276
 
99
277
  if message
100
278
  if skip < @count
101
- do_process message
279
+ save_mbox_to_m3 message
102
280
  print "#{@count}."
103
281
  else
104
282
  print "s-#{@count}."
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wco_models
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0.52
4
+ version: 3.1.0.54
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Pudeyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-10 00:00:00.000000000 Z
11
+ date: 2024-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-s3