wco_models 3.4.0.3 → 3.4.0.4

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: '0274939884ef534ba5826efd3adcbbd3fa0605a03b93d95333c2251432098f7c'
4
- data.tar.gz: bc0e8ed07d318199cfb9c7fbaff36d35c42900bfb93377ce99ea32162751b2f8
3
+ metadata.gz: b67195527c6adb04bc55d19eb97695989275618888d1f64857aa6876009d1119
4
+ data.tar.gz: e6534767809ec2d2d81f776df6005dc523dc8e8500ab42eb22f882c9cd16110a
5
5
  SHA512:
6
- metadata.gz: 61f84d2f853ca2b8a8b88ae421045245770c2ce183da46ea19ab15212927cad4110ea2b6ecff6316c10482ff8f9b48c70e0f7a83c2d2e2ddf47d05babe2fb35f
7
- data.tar.gz: 27f0896d5d76acfd21f00946f91ddd4b7c8bc95e073d75292ac1ed3601ab95d250c01d3d52773082f0cfc0b351b402763be0515a53c387ea2e6e749a26ef4204
6
+ metadata.gz: 100de488e7b2a4d5a54f9eaecc1049c38f848264550546b9f8ff7c57bf297418894218dbebde164334f8d5460fe547a94bb794be4ed7c821745401369de3db01
7
+ data.tar.gz: ff5d054c4befbf19f7b3f2ae948c83aebf1c9b6b87575cd25a9ca1b175f85053c05e55dcda01f2230a1a9ad4b1f9b81489a9cefe296c96263767986cbbe39711
@@ -343,11 +343,12 @@ table thead.sticky {
343
343
  .w-300px {
344
344
  width: 300px;
345
345
  }
346
-
347
346
  .w-400px {
348
347
  width: 400px;
349
348
  }
350
-
351
349
  .w-500px {
352
350
  width: 500px;
353
351
  }
352
+ .w-600px {
353
+ width: 500px;
354
+ }
@@ -25,12 +25,15 @@ class Wco::OfficeActionTemplatesController < Wco::ApplicationController
25
25
  def perform
26
26
  @oat = OAT.find params[:id]
27
27
  authorize! :run, @oat
28
+
28
29
  @conversations = WcoEmail::Conversation.find( params[:conversation_ids] ) if params[:conversation_ids].present?
30
+
29
31
  if 'rb' == @oat.action_type
30
32
  out = eval( @oat.action_exe )
31
33
  elsif 'sh' == @oat.action_type
32
34
  out = `#{@oat.action_exe}`
33
35
  end
36
+
34
37
  flash_notice out
35
38
  redirect_to action: :index
36
39
  end
@@ -12,20 +12,6 @@ class Wco::PublishersController < Wco::ApplicationController
12
12
  redirect_to action: 'index'
13
13
  end
14
14
 
15
- ## this is becoming obsolete - try using do_run_any
16
- def do_run
17
- @publisher = Wco::Publisher.find params[:id]
18
- authorize! :do_run, @publisher
19
-
20
- @publisher.props = OpenStruct.new( JSON.parse params[:publisher][:props] )
21
- # @publisher.do_run binding
22
- @publisher.do_run
23
-
24
- flash_notice "Probably ok"
25
-
26
- redirect_to action: 'index'
27
- end
28
-
29
15
  def do_run_any
30
16
  @publisher = Wco::Publisher.find params[:id]
31
17
  authorize! :do_run, @publisher
@@ -33,7 +19,7 @@ class Wco::PublishersController < Wco::ApplicationController
33
19
  @publisher.props = OpenStruct.new( JSON.parse params[:props] )
34
20
  @publisher.do_run
35
21
 
36
- flash_notice "Probably ok"
22
+ flash_notice "#do_run_any Probably ok"
37
23
  redirect_to request.referrer || { action: 'index' }
38
24
  end
39
25
 
@@ -40,7 +40,7 @@ class Wco::SitesController < Wco::ApplicationController
40
40
 
41
41
  def index
42
42
  authorize! :index, Wco::Site
43
- @sites = Wco::Site.all
43
+ @sites = Wco::Site.all.includes( :tags )
44
44
  end
45
45
 
46
46
  def new
@@ -32,7 +32,7 @@ class Wco::TagsController < Wco::ApplicationController
32
32
 
33
33
  def index
34
34
  authorize! :index, Wco::Tag
35
- @tags = Wco::Tag.all.order_by( slug: :asc )
35
+ @tags = Wco::Tag.where( site: nil ).order_by( slug: :asc )
36
36
 
37
37
  tags = Wco::Tag.all.to_a.group_by(&:parent_id)
38
38
  build_tree = lambda do |parent_id|
@@ -8,7 +8,7 @@ class Iro::Option
8
8
  attr_accessor :recompute
9
9
 
10
10
  belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :strategies
11
- def ticker; stock.ticker; end
11
+ field :ticker, type: :string
12
12
 
13
13
  CALL = 'CALL'
14
14
  PUT = 'PUT'
@@ -67,14 +67,47 @@ class Iro::Option
67
67
  return options
68
68
  end
69
69
 
70
+ field :symbol, type: :string
71
+ validates :symbol, uniqueness: true
72
+ index({ symbol: -1 }, { unique: true, name: 'symbol' })
73
+ # def symbol
74
+ # return self[:symbol] if self[:symbol]
75
+ # self[:symbol] = generate_symbol
76
+ # end
77
+
70
78
  ## for schwab, eg:
71
79
  ## "COST 260306C01030000"
72
- def symbol
80
+ def generate_symbol
73
81
  p_c_ = put_call == 'PUT' ? 'P' : 'C'
74
82
  strike_ = format("%08d", (strike.to_f * 1000).round)
75
83
  sym = "#{stock.ticker.ljust(6)}#{expires_on.strftime("%y%m%d")}#{p_c_}#{strike_}"
76
84
  end
77
85
 
86
+ def self.find_or_create_by_symbol symbol
87
+ option = Iro::Option.where( symbol: symbol ).first
88
+ return option if option
89
+ h = Iro::Option.symbol_to_h symbol
90
+ item = Iro::Option.new({
91
+ stock: Iro::Stock.find_by( ticker: h[:ticker] ),
92
+ ticker: h[:ticker],
93
+ put_call: h[:put_call],
94
+ strike: h[:strike],
95
+ expires_on: h[:expires_on],
96
+ symbol: symbol,
97
+ })
98
+ item.save
99
+ return item
100
+ end
101
+
102
+ ## trash, I can save the symbol every time. 2026-10-04
103
+ # def self.find_by_symbol symbol
104
+ # option = self.where( symbol: symbol ).first
105
+ # return option if option
106
+ # h = Iro::Option.symbol_to_h symbol
107
+ # Iro::Option.where( h ).first
108
+ # end
109
+
110
+
78
111
  =begin
79
112
  symbol = "META 260424P00500000"
80
113
  =end
@@ -83,7 +116,7 @@ class Iro::Option
83
116
  date_str = symbol[6,6]
84
117
  type = symbol[12] == 'P' ? 'PUT' : 'CALL'
85
118
  strike_str = symbol[13,8]
86
- expires_on = Date.strptime(date_str, "%y%m%d")
119
+ expires_on = Date.strptime(date_str, "%y%m%d").to_s
87
120
  strike = strike_str.to_i / 1000.0
88
121
  return {
89
122
  ticker: ticker,
@@ -21,18 +21,19 @@ class Iro::Priceitem
21
21
  field :bidSize, type: Integer
22
22
  field :ask, type: Float
23
23
  field :askSize, type: Integer
24
- field :last, type: Float ## this is important
24
+ field :last, type: Float ## this is important?
25
+ field :mark, type: Float ## no, this is important. (bid+ask)/2
25
26
 
26
27
  field :openPrice, type: Float
27
28
  field :lowPrice, type: Float
28
29
  field :highPrice, type: Float
29
30
  field :closePrice, type: Float
30
31
 
31
- field :quote_at, type: DateTime ## this is important, the timestamp mostly used for analysis
32
+ field :quote_at, type: DateTime ## this is important?
32
33
  field :quoteTimeInLong, type: Integer
33
- field :timestamp, type: Integer ## do not use?!
34
+ field :timestamp, type: Integer ## do not use?
34
35
  field :totalVolume, type: Integer
35
- field :mark, type: Float
36
+
36
37
  field :exchangeName, type: String
37
38
  field :volatility, type: Float
38
39
 
@@ -43,6 +44,63 @@ class Iro::Priceitem
43
44
  field :openInterest, type: Integer
44
45
  field :strikePrice, type: Float
45
46
 
47
+ def self.create_from_chains!
48
+ Iro::Iro.schwab_exec_sync ## should be schwab_data_sync()
49
+
50
+ stocks = Iro::Stock.active
51
+ fridays = 3.times.map { |i| ( Date.current.next_occurring(:friday) + i.weeks ).to_s }
52
+
53
+ stocks.each do |stock|
54
+ puts "+++ Getting #{stock.ticker}..."
55
+ response = Tda::Option.get_chains({
56
+ ticker: stock.ticker,
57
+ fromDate: fridays[0],
58
+ toDate: fridays.last,
59
+ strikeCount: 10,
60
+ })
61
+ # puts! response.keys, 'response.keys'
62
+ # puts! response['symbol'], 'response symbol'
63
+ # sleep 10
64
+
65
+ first_val = nil
66
+ ['callExpDateMap', 'putExpDateMap'].each do |which_map|
67
+
68
+ response[which_map].each do |_date, strikes|
69
+ if fridays.include?( _date.split(':')[0] )
70
+ strikes.each do |_strike, vals|
71
+ vals.each do |val|
72
+ if !first_val
73
+ first_val = val
74
+ puts! val.keys, 'val keys'
75
+ end
76
+
77
+ option = Iro::Option.find_or_create_by_symbol( val['symbol'] )
78
+
79
+ # opt = OpenStruct.new val
80
+ pi = Iro::Priceitem.new( val.slice( 'expirationDate',
81
+ 'putCall', 'symbol', 'exchangeName', 'bid', 'ask',
82
+ 'last', 'mark', 'bidSize', 'askSize', 'totalVolume', 'quoteTimeInLong', 'volatility',
83
+ 'delta', 'gamma', 'theta', 'openInterest', 'strikePrice' ) )
84
+ pi.stock = stock
85
+ pi.option = option
86
+ pi.quote_at = Time.at( val['quoteTimeInLong'] / 1000 )
87
+ pi.ticker = stock.ticker
88
+ pi.save!
89
+
90
+ # puts! pi
91
+ print '.'
92
+
93
+
94
+ end
95
+ end
96
+ end ## end fridays.include?
97
+ end
98
+
99
+ end ## which_map
100
+ end
101
+
102
+ end
103
+
46
104
  def self.my_find props={}
47
105
  lookup = { '$lookup': {
48
106
  'from': 'iro_price_items',
@@ -1,16 +1,7 @@
1
1
 
2
2
  require 'httparty'
3
3
 
4
- =begin
5
- class Schwab
6
- include HTTParty
7
- debug_output $stdout
8
- base_uri 'https://api.schwabapi.com/marketdata/v1'
9
- end
10
- =end
11
-
12
4
  class Tda::Option
13
-
14
5
  include ::HTTParty
15
6
  # debug_output $stdout
16
7
  base_uri 'https://api.schwabapi.com/marketdata/v1'
@@ -18,7 +9,7 @@ class Tda::Option
18
9
 
19
10
  ##
20
11
  ## Get entire chains for a ticker
21
- ## params: { ticker, force }
12
+ ## params: { ticker, force, strikeCount, fromDate (yyyy-mm-dd), toDate }
22
13
  ##
23
14
  ## 2024-08-09 :: Continue
24
15
  ## 2024-08-21 :: Continue : )
@@ -29,7 +20,10 @@ class Tda::Option
29
20
  return JSON.parse File.read filename
30
21
 
31
22
  else
32
- query = { symbol: params[:ticker] } ## use 'GME' as symbol here even though a symbol is eg 'GME_021023P2.5'
23
+ query = { symbol: params[:ticker] } ## says query[:symbol], not ticker.
24
+ query[:strikeCount] = params[:strikeCount] if params[:strikeCount]
25
+ query[:fromDate] = params[:fromDate] if params[:fromDate]
26
+ query[:toDate] = params[:toDate] if params[:toDate]
33
27
  # puts! query, 'query'
34
28
 
35
29
  headers = {
@@ -37,40 +31,30 @@ class Tda::Option
37
31
  Authorization: "Bearer #{Iro::Iro.schwab_data_token}",
38
32
  }
39
33
  path = "/chains"
40
- out = self.get path, {
41
- headers: headers,
42
- query: query }
43
- timestamp = DateTime.parse out.headers['date']
44
- out = out.parsed_response
45
- # puts! out, 'outs'
34
+ response = self.get path, { headers: headers, query: query }
35
+ timestamp = DateTime.parse response.headers['date']
36
+ response = response.parsed_response
37
+ puts! response, '/chains got this response'
46
38
 
47
39
  outs = []
48
40
  %w| put call |.each do |contractType|
49
- _out = out["#{contractType}ExpDateMap"]
50
- _out.each do |date, vs| ## date="2023-02-10:5"
41
+ _out = response["#{contractType}ExpDateMap"]
42
+ _out.each do |_date, vs| ## date="2023-02-10:5"
51
43
  vs.each do |strike, _v| ## strike="18.5"
52
44
  _v = _v[0] ## weird, keep
53
45
  # puts! _v, '_v'
54
46
 
55
- v = {
56
- putCall: _v['putCall'],
57
- symbol: _v['symbol'],
58
- bid: _v['bid'],
59
- ask: _v['ask'],
60
- last: _v['last'],
61
- totalVolume: _v['totalVolume'],
62
- openInterest: _v['openInterest'],
63
- strikePrice: _v['strikePrice'],
64
- expirationDate: _v['expirationDate'],
65
- }
47
+ v = _v.slice( 'putCall', 'symbol', 'bid', 'ask', 'last', 'mark', 'totalVolume', 'openInterest',
48
+ 'strikePrice', 'expirationDate' )
66
49
  v.each do |k, i|
67
50
  if i == 'NaN'
68
51
  v[k] = nil
69
52
  end
70
53
  end
71
54
 
72
- v[:timestamp] = timestamp
73
- v[:ticker] = params[:ticker]
55
+ v[:timestamp] = timestamp # _v['quoteTimeInLong']/1000.0
56
+ v[:quote_at] = Time.at(_v['quoteTimeInLong'] / 1000.0)
57
+ v[:ticker] = params[:ticker]
74
58
  outs.push( v )
75
59
  end
76
60
  end
@@ -83,8 +67,8 @@ class Tda::Option
83
67
  end
84
68
  end
85
69
 
86
- File.write filename, out.to_json
87
- return out
70
+ File.write filename, JSON.pretty_generate(response)
71
+ return response
88
72
  end
89
73
  end
90
74
 
@@ -96,6 +80,8 @@ class Tda::Option
96
80
  OpenStruct.new ::Tda::Option.get_quotes(params)[0]
97
81
  end
98
82
 
83
+ ## ...for a single symbol.
84
+ ## but use get_quotes_h instead?!
99
85
  ##
100
86
  ## params: contractType, strike, expirationDate, ticker
101
87
  ##
@@ -250,19 +236,23 @@ class Tda::Option
250
236
  end
251
237
 
252
238
  def self.close_credit_call
239
+ throw 'r40 - not implemented'
253
240
  end
254
241
  def self.close_long_debit_call_spread
242
+ throw 'r41 - not implemented'
255
243
  end
256
244
  def self.close_short_debit_put_spread
245
+ throw 'r42 - not implemented'
257
246
  end
258
247
 
259
- def self.get_token
260
- opts = {
261
- grant_type: 'authorization_code',
262
- access_type: 'offline',
263
- code: ::TD_AMERITRADE[:code],
264
- }
265
- end
248
+ ## trash? remove.
249
+ # def self.get_token
250
+ # opts = {
251
+ # grant_type: 'authorization_code',
252
+ # access_type: 'offline',
253
+ # code: ::TD_AMERITRADE[:code],
254
+ # }
255
+ # end
266
256
 
267
257
  def self.create_credit_call outer:, inner:, q:, price:
268
258
  query = {
@@ -307,13 +297,21 @@ class Tda::Option
307
297
  # puts! out, 'created credit call?'
308
298
  end
309
299
  def self.create_long_debit_call_spread
300
+ throw 'r43 - not implemented'
310
301
  end
311
302
  def self.create_short_debit_put_spread
303
+ throw 'r44 - not implemented'
312
304
  end
313
305
 
314
306
 
315
307
  end
316
308
 
309
+
310
+
311
+
312
+
313
+
314
+
317
315
  ##
318
316
  ## From: https://developer.tdameritrade.com/content/place-order-samples
319
317
  ## Buy Limit: Vertical Call Spread
@@ -23,22 +23,25 @@ class Wco::Publisher
23
23
  attr_accessor :props
24
24
 
25
25
  def do_run
26
+ publisher = self
27
+
26
28
  @site = site
27
29
  @headers = {}
28
30
  @ctx = OpenStruct.new
29
31
 
30
- # puts! context_eval, 'context_eval'
32
+ puts! context_eval, 'context_eval'
31
33
  eval( context_eval )
32
- # puts! @ctx, '@ctx'
34
+ puts! @ctx, '@ctx'
33
35
 
34
- tmpl = ERB.new post_body_tmpl
36
+ tmpl = ERB.new publisher.post_body_tmpl
35
37
  body = JSON.parse tmpl.result(binding)
36
- # puts! body, 'body'
38
+ puts! body, 'body'
37
39
 
38
40
  out = Wco::HTTParty.post( "#{@site.origin}#{post_path}", {
39
41
  body: body.to_json,
40
42
  headers: @headers,
41
43
  basic_auth: { username: @site.username, password: @site.password },
44
+ debug_output: $stdout,
42
45
  })
43
46
  puts! out.response, 'publisher httparty response'
44
47
  puts! out.body, 'publisher httparty body'
@@ -54,12 +57,17 @@ class Wco::Publisher
54
57
  raise out.body
55
58
  end
56
59
 
57
- eval( after_eval ) if after_eval.present?
60
+ if after_eval.present?
61
+ puts! after_eval, 'after_eval'
62
+ eval( after_eval )
63
+ end
64
+
65
+ return true
58
66
  end
59
67
 
60
68
 
61
69
  def to_s
62
- "#{slug}: #{kind} => #{site}"
70
+ "#{site} :: #{slug}"
63
71
  end
64
72
 
65
73
  def self.list
@@ -32,7 +32,7 @@ class Wco::Report
32
32
  field :y, :type => Float
33
33
  field :z, :type => Float
34
34
 
35
- belongs_to :author, class_name: 'Wco::Profile'
35
+ belongs_to :author, class_name: 'Wco::Profile', optional: true
36
36
 
37
37
  has_one :image_thumb, class_name: 'Wco::Photo', inverse_of: :report, dependent: :destroy
38
38
  accepts_nested_attributes_for :image_thumb
@@ -18,7 +18,7 @@ class Wco::Site
18
18
  has_many :logs, inverse_of: :obj
19
19
  has_many :publishers # , class_name: 'Wco::Publisher'
20
20
  has_many :sitemap_paths, class_name: 'Wco::SitemapPath'
21
- has_many :tags, class_name: 'Wco::Tag'
21
+ has_many :tags, class_name: 'Wco::Tag', inverse_of: :site
22
22
 
23
23
  field :slug
24
24
  validates :slug, presence: true, uniqueness: true
@@ -34,78 +34,12 @@ class Wco::Site
34
34
 
35
35
  def to_s
36
36
  slug
37
- # origin
38
37
  end
39
38
 
40
39
  def self.list
41
40
  [[nil,nil]] + all.map { |s| [ s.slug, s.id ] }
42
41
  end
43
42
 
44
- def body
45
- {
46
- "_links": {
47
- "type":{"href":"http://pi.local/rest/type/node/article"}
48
- },
49
- "title":[{"value":"Node +++ 123 bac +++" }],
50
- "body":[{"value": "<b>hello, wor</b>ld!", "format": "full_html" }],
51
- "type":[{"target_id":"article"}],
52
- "status": [{"value": 1}],
53
- "_embedded": {
54
- "http://pi.local/rest/relation/node/article/field_issue": [
55
- { "uuid": [{ "value": "56229a95-d675-43e1-99b1-f9e11b5579c5" }] }
56
- ],
57
- "http://pi.local/rest/relation/node/article/field_tags": [
58
- { "uuid": [{ "value": "45646a7d-1a16-42e8-b758-f6e1c8d976f7" }] },
59
- { "uuid": [{ "value": "834e34e2-05ae-498d-b876-453798872ce1" }] }
60
- ]
61
- }
62
-
63
- }
64
- end
65
-
66
- def do_post
67
- HTTParty.post( post_url,
68
- body: JSON.generate( body ),
69
- headers: { 'Content-Type' => 'application/hal+json' },
70
- basic_auth: { username: username, password: password },
71
- )
72
- end
73
-
74
- def wp_import
75
- site = self
76
-
77
- root_tag = Wco::Tag.find_or_create_by slug: "#{site.slug}_wp-import", site_id: site.id
78
- url = "#{site.origin}/wp-json/wp/v2/posts"
79
- pi_admin = Wco::Profile.find_or_create_by email: 'admin@piousbox.com'
80
- n_pages = 12
81
- per_page = 100
82
-
83
- (1..n_pages).each do |page|
84
- print "Page #{page}"
85
-
86
- posts = HTTParty.get url, query: { per_page: per_page, page: page }
87
- posts.each do |post|
88
- report = Wco::Report.new({
89
- legacy_id: post['id'],
90
- created_at: post['date'],
91
- slug: post['link'].sub(site.origin, ''),
92
- title: post['title']['rendered'],
93
- subtitle: post['excerpt']['rendered'],
94
- body: post['content']['rendered'],
95
- author: pi_admin,
96
- tag_ids: ( [ root_tag ] + site.tags.where( :legacy_id.in => post['categories'] ) ).map(&:id),
97
- })
98
-
99
- if report.save
100
- print '^'
101
- else
102
- puts report.errors.messages
103
- end
104
- end
105
- end
106
- puts "ok"
107
- end
108
-
109
43
  def check_sitemap
110
44
  @results = []
111
45
  @total_count = 0
@@ -0,0 +1,65 @@
1
+
2
+ def body
3
+ {
4
+ "_links": {
5
+ "type":{"href":"http://pi.local/rest/type/node/article"}
6
+ },
7
+ "title":[{"value":"Node +++ 123 bac +++" }],
8
+ "body":[{"value": "<b>hello, wor</b>ld!", "format": "full_html" }],
9
+ "type":[{"target_id":"article"}],
10
+ "status": [{"value": 1}],
11
+ "_embedded": {
12
+ "http://pi.local/rest/relation/node/article/field_issue": [
13
+ { "uuid": [{ "value": "56229a95-d675-43e1-99b1-f9e11b5579c5" }] }
14
+ ],
15
+ "http://pi.local/rest/relation/node/article/field_tags": [
16
+ { "uuid": [{ "value": "45646a7d-1a16-42e8-b758-f6e1c8d976f7" }] },
17
+ { "uuid": [{ "value": "834e34e2-05ae-498d-b876-453798872ce1" }] }
18
+ ]
19
+ }
20
+
21
+ }
22
+ end
23
+
24
+ def do_post
25
+ HTTParty.post( post_url,
26
+ body: JSON.generate( body ),
27
+ headers: { 'Content-Type' => 'application/hal+json' },
28
+ basic_auth: { username: username, password: password },
29
+ )
30
+ end
31
+
32
+ def wp_import
33
+ site = self
34
+
35
+ root_tag = Wco::Tag.find_or_create_by slug: "#{site.slug}_wp-import", site_id: site.id
36
+ url = "#{site.origin}/wp-json/wp/v2/posts"
37
+ pi_admin = Wco::Profile.find_or_create_by email: 'admin@piousbox.com'
38
+ n_pages = 12
39
+ per_page = 100
40
+
41
+ (1..n_pages).each do |page|
42
+ print "Page #{page}"
43
+
44
+ posts = HTTParty.get url, query: { per_page: per_page, page: page }
45
+ posts.each do |post|
46
+ report = Wco::Report.new({
47
+ legacy_id: post['id'],
48
+ created_at: post['date'],
49
+ slug: post['link'].sub(site.origin, ''),
50
+ title: post['title']['rendered'],
51
+ subtitle: post['excerpt']['rendered'],
52
+ body: post['content']['rendered'],
53
+ author: pi_admin,
54
+ tag_ids: ( [ root_tag ] + site.tags.where( :legacy_id.in => post['categories'] ) ).map(&:id),
55
+ })
56
+
57
+ if report.save
58
+ print '^'
59
+ else
60
+ puts report.errors.messages
61
+ end
62
+ end
63
+ end
64
+ puts "ok"
65
+ end
@@ -9,15 +9,17 @@ class Wco::Tag
9
9
  index({ slug: -1 })
10
10
 
11
11
  field :weight, type: :string, default: 'jjj'
12
+ field :external_id, type: :string
13
+ def eid; external_id; end
12
14
 
13
15
  belongs_to :parent, class_name: '::Wco::Tag', inverse_of: :sons, optional: true
14
16
  has_many :sons, class_name: '::Wco::Tag', inverse_of: :parent
15
17
 
16
- belongs_to :site, class_name: '::Wco::Site', optional: true
17
- belongs_to :sidebar_profile, class_name: 'Wco::Profile', optional: true
18
- has_many :email_filters, class_name: '::WcoEmail::EmailFilter', inverse_of: :tag
19
- has_many :email_templates, class_name: '::WcoEmail::EmailTemplate', inverse_of: :tag
20
- has_many :ajects, class_name: '::WcoEmail::EmailFilterAction', inverse_of: :aject
18
+ belongs_to :site, class_name: '::Wco::Site', optional: true, inverse_of: :tags
19
+ belongs_to :sidebar_profile, class_name: 'Wco::Profile', optional: true
20
+ has_many :email_filters, class_name: '::WcoEmail::EmailFilter', inverse_of: :tag
21
+ has_many :email_templates, class_name: '::WcoEmail::EmailTemplate', inverse_of: :tag
22
+ has_many :ajects, class_name: '::WcoEmail::EmailFilterAction', inverse_of: :aject
21
23
 
22
24
  has_and_belongs_to_many :conversations, class_name: '::WcoEmail::Conversation', index: true
23
25
  has_and_belongs_to_many :galleries, class_name: '::Wco::Gallery'
@@ -15,7 +15,7 @@
15
15
  &nbsp;
16
16
  = link_to oat.slug, office_action_template_path(oat)
17
17
  &nbsp;
18
- = button_to 'Run w/convos', oat_perform_with_conversations_path(oat), data: { confirm: 'Are you sure?' }
18
+ = button_to 'Perform', oat_perform_path(oat), data: { confirm: 'Are you sure?' }
19
19
  .col-6
20
20
  = render '/wco/office_action_templates/ties', ties: oat.ties, oat: oat
21
21
 
@@ -3,5 +3,5 @@
3
3
  %h5 Office Action
4
4
  .d-inline-block
5
5
  = select_tag :office_action_template, options_for_select(@office_action_templates_list), class: 'select2'
6
- %a.btn.office-action-templates-perform-btn{ href: "javascript: void(0)", data: { url: wco.oat_perform_with_conversations_path } }
6
+ %a.btn.office-action-templates-perform-btn{ href: "javascript: void(0)", data: { url: wco.oats_perform_path } }
7
7
  Perform
@@ -20,11 +20,15 @@
20
20
  .d-flex
21
21
  %label post_path
22
22
  = f.text_field :post_path, class: 'w-100'
23
- .field
23
+ .field.d-flex.flex-column
24
24
  %label.d-block context_eval
25
+ .descr
26
+ @headers, @props
25
27
  = f.text_area :context_eval, class: 'monospace'
26
- .field
28
+ .field.d-flex.flex-column
27
29
  %label.d-block post_body_tmpl
30
+ .descr
31
+ @headers, @props and #context_eval
28
32
  = f.text_area :post_body_tmpl, class: 'monospace'
29
33
  .field
30
34
  %label.d-block after_eval
@@ -1,7 +1,7 @@
1
1
 
2
2
  .publishers--post-with
3
3
  = form_tag run_any_publisher_path do
4
- .d-flex
4
+ .d-flex.w-500px
5
5
  = select_tag :id, options_for_select(@publishers_list), class: 'select2'
6
6
  = hidden_field_tag :props, props.to_json
7
7
  = submit_tag 'Go', data: { confirm: 'Are you sure?' }
@@ -11,6 +11,7 @@
11
11
  -# %th kind
12
12
  %th origin
13
13
  -# %th post_path
14
+ %th tags
14
15
  - @sites.each do |site|
15
16
  %tr
16
17
  %td
@@ -22,3 +23,8 @@
22
23
  -# %td= site.kind
23
24
  %td= site.origin
24
25
  -# %td= site.post_path
26
+ %td
27
+ - site.tags.map do |t|
28
+ = link_to t.slug, tag_path(t)
29
+ ,
30
+
@@ -8,6 +8,9 @@
8
8
  .field
9
9
  %label slug
10
10
  = f.text_field :slug
11
+ .field
12
+ %label external_id
13
+ = f.text_field :external_id
11
14
 
12
15
  .field.d-flex
13
16
  %label parent
data/config/routes.rb CHANGED
@@ -95,8 +95,8 @@ Wco::Engine.routes.draw do
95
95
 
96
96
  resources :obfuscated_redirects
97
97
 
98
- post 'office_action_templates', to: 'office_action_templates#update'
99
- post 'office_action_templates/perform', to: 'office_action_templates#perform', as: :oat_perform_with_conversations ## from the mailbox, the oat_id is passed as a body param.
98
+ post 'office_action_templates', to: 'office_action_templates#update'
99
+ post 'office_action_templates/perform', to: 'office_action_templates#perform', as: :oats_perform ## from the mailbox, the oat id is passed as a body param.
100
100
  post 'office_action_templates/:id/perform', to: 'office_action_templates#perform', as: :oat_perform
101
101
  get 'office_action_templates/:id/perform', to: 'office_action_templates#perform'
102
102
  resources :office_action_templates
data/lib/wco_models.rb CHANGED
@@ -43,7 +43,7 @@ end
43
43
 
44
44
  ActiveSupport.escape_html_entities_in_json = true
45
45
 
46
-
46
+ ## for newsproducer
47
47
  HEIGHT_SEC = 100.0
48
48
  HEIGHT_MS = HEIGHT_SEC/1000
49
49
 
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.4.0.3
4
+ version: 3.4.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Pudeyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-04 00:00:00.000000000 Z
11
+ date: 2026-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-s3
@@ -544,6 +544,7 @@ files:
544
544
  - app/models/wco/report.rb
545
545
  - app/models/wco/setting.rb
546
546
  - app/models/wco/site.rb
547
+ - app/models/wco/site.rb-trash
547
548
  - app/models/wco/sitemap_path.rb
548
549
  - app/models/wco/subscription.rb
549
550
  - app/models/wco/tag.rb