bas 0.4.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +68 -147
- data/lib/bas/bot/base.rb +74 -0
- data/lib/bas/bot/compare_wip_limit_count.rb +92 -0
- data/lib/bas/bot/fetch_birthdays_from_notion.rb +128 -0
- data/lib/bas/bot/fetch_domains_wip_counts_from_notion.rb +121 -0
- data/lib/bas/bot/fetch_domains_wip_limit_from_notion.rb +134 -0
- data/lib/bas/bot/fetch_emails_from_imap.rb +99 -0
- data/lib/bas/bot/fetch_next_week_birthdays_from_notion.rb +142 -0
- data/lib/bas/bot/fetch_next_week_ptos_from_notion.rb +162 -0
- data/lib/bas/bot/fetch_ptos_from_notion.rb +173 -0
- data/lib/bas/bot/format_birthdays.rb +97 -0
- data/lib/bas/bot/format_emails.rb +124 -0
- data/lib/bas/bot/format_wip_limit_exceeded.rb +97 -0
- data/lib/bas/bot/garbage_collector.rb +85 -0
- data/lib/bas/bot/humanize_pto.rb +117 -0
- data/lib/bas/bot/notify_discord.rb +96 -0
- data/lib/bas/read/base.rb +10 -23
- data/lib/bas/read/default.rb +16 -0
- data/lib/bas/read/postgres.rb +44 -0
- data/lib/bas/read/types/response.rb +18 -0
- data/lib/bas/utils/discord/integration.rb +43 -0
- data/lib/bas/utils/exceptions/function_not_implemented.rb +16 -0
- data/lib/bas/utils/exceptions/invalid_process_response.rb +16 -0
- data/lib/bas/utils/imap/request.rb +76 -0
- data/lib/bas/utils/notion/request.rb +45 -0
- data/lib/bas/utils/openai/run_assistant.rb +99 -0
- data/lib/bas/utils/postgres/request.rb +50 -0
- data/lib/bas/version.rb +1 -1
- data/lib/bas/write/base.rb +12 -17
- data/lib/bas/write/postgres.rb +45 -0
- data/lib/bas/write/postgres_update.rb +49 -0
- data/lib/bas.rb +1 -3
- metadata +31 -68
- data/lib/bas/domain/birthday.rb +0 -25
- data/lib/bas/domain/email.rb +0 -34
- data/lib/bas/domain/exceptions/function_not_implemented.rb +0 -18
- data/lib/bas/domain/issue.rb +0 -22
- data/lib/bas/domain/notification.rb +0 -23
- data/lib/bas/domain/pto.rb +0 -69
- data/lib/bas/domain/work_items_limit.rb +0 -25
- data/lib/bas/formatter/base.rb +0 -53
- data/lib/bas/formatter/birthday.rb +0 -38
- data/lib/bas/formatter/exceptions/invalid_data.rb +0 -15
- data/lib/bas/formatter/notification.rb +0 -34
- data/lib/bas/formatter/pto.rb +0 -89
- data/lib/bas/formatter/support_emails.rb +0 -73
- data/lib/bas/formatter/types/response.rb +0 -16
- data/lib/bas/formatter/work_items_limit.rb +0 -68
- data/lib/bas/process/base.rb +0 -39
- data/lib/bas/process/discord/exceptions/invalid_webhook_token.rb +0 -16
- data/lib/bas/process/discord/implementation.rb +0 -71
- data/lib/bas/process/discord/types/response.rb +0 -22
- data/lib/bas/process/openai/base.rb +0 -72
- data/lib/bas/process/openai/helper.rb +0 -19
- data/lib/bas/process/openai/types/response.rb +0 -27
- data/lib/bas/process/openai/use_case/humanize_pto.rb +0 -53
- data/lib/bas/process/slack/exceptions/invalid_webhook_token.rb +0 -16
- data/lib/bas/process/slack/implementation.rb +0 -70
- data/lib/bas/process/slack/types/response.rb +0 -21
- data/lib/bas/process/types/response.rb +0 -16
- data/lib/bas/read/github/base.rb +0 -57
- data/lib/bas/read/github/types/response.rb +0 -27
- data/lib/bas/read/github/use_case/repo_issues.rb +0 -17
- data/lib/bas/read/imap/base.rb +0 -70
- data/lib/bas/read/imap/types/response.rb +0 -27
- data/lib/bas/read/imap/use_case/support_emails.rb +0 -26
- data/lib/bas/read/notion/base.rb +0 -52
- data/lib/bas/read/notion/exceptions/invalid_api_key.rb +0 -15
- data/lib/bas/read/notion/exceptions/invalid_database_id.rb +0 -15
- data/lib/bas/read/notion/helper.rb +0 -21
- data/lib/bas/read/notion/types/response.rb +0 -26
- data/lib/bas/read/notion/use_case/birthday_next_week.rb +0 -41
- data/lib/bas/read/notion/use_case/birthday_today.rb +0 -29
- data/lib/bas/read/notion/use_case/notification.rb +0 -28
- data/lib/bas/read/notion/use_case/pto_next_week.rb +0 -71
- data/lib/bas/read/notion/use_case/pto_today.rb +0 -30
- data/lib/bas/read/notion/use_case/work_items_limit.rb +0 -37
- data/lib/bas/read/postgres/base.rb +0 -46
- data/lib/bas/read/postgres/helper.rb +0 -16
- data/lib/bas/read/postgres/types/response.rb +0 -42
- data/lib/bas/read/postgres/use_case/pto_today.rb +0 -32
- data/lib/bas/serialize/base.rb +0 -30
- data/lib/bas/serialize/github/issues.rb +0 -57
- data/lib/bas/serialize/imap/support_emails.rb +0 -56
- data/lib/bas/serialize/notion/birthday_today.rb +0 -68
- data/lib/bas/serialize/notion/notification.rb +0 -56
- data/lib/bas/serialize/notion/pto_today.rb +0 -75
- data/lib/bas/serialize/notion/work_items_limit.rb +0 -65
- data/lib/bas/serialize/postgres/pto_today.rb +0 -47
- data/lib/bas/use_cases/types/config.rb +0 -20
- data/lib/bas/use_cases/use_case.rb +0 -42
- data/lib/bas/use_cases/use_cases.rb +0 -465
- data/lib/bas/write/logs/base.rb +0 -33
- data/lib/bas/write/logs/use_case/console_log.rb +0 -22
- data/lib/bas/write/notion/base.rb +0 -36
- data/lib/bas/write/notion/use_case/empty_notification.rb +0 -38
- data/lib/bas/write/notion/use_case/notification.rb +0 -38
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
|
5
|
+
require_relative "./base"
|
6
|
+
require_relative "../read/default"
|
7
|
+
require_relative "../utils/notion/request"
|
8
|
+
require_relative "../write/postgres"
|
9
|
+
|
10
|
+
module Bot
|
11
|
+
##
|
12
|
+
# The Bot::FetchPtosFromNotion class serves as a bot implementation to read PTO's from a
|
13
|
+
# notion database and write them on a PostgresDB table with a specific format.
|
14
|
+
#
|
15
|
+
# <br>
|
16
|
+
# <b>Example</b>
|
17
|
+
#
|
18
|
+
# options = {
|
19
|
+
# process_options: {
|
20
|
+
# database_id: "notion database id",
|
21
|
+
# secret: "notion secret"
|
22
|
+
# },
|
23
|
+
# write_options: {
|
24
|
+
# connection: {
|
25
|
+
# host: "host",
|
26
|
+
# port: 5432,
|
27
|
+
# dbname: "bas",
|
28
|
+
# user: "postgres",
|
29
|
+
# password: "postgres"
|
30
|
+
# },
|
31
|
+
# db_table: "pto",
|
32
|
+
# tag: "FetchPtosFromNotion"
|
33
|
+
# }
|
34
|
+
# }
|
35
|
+
#
|
36
|
+
# bot = Bot::FetchPtosFromNotion.new(options)
|
37
|
+
# bot.execute
|
38
|
+
#
|
39
|
+
class FetchPtosFromNotion < Bot::Base
|
40
|
+
# Read function to execute the default Read component
|
41
|
+
#
|
42
|
+
def read
|
43
|
+
reader = Read::Default.new
|
44
|
+
|
45
|
+
reader.execute
|
46
|
+
end
|
47
|
+
|
48
|
+
# Process function to execute the Notion utility to fetch PTO's from the notion database
|
49
|
+
#
|
50
|
+
def process
|
51
|
+
response = Utils::Notion::Request.execute(params)
|
52
|
+
|
53
|
+
if response.code == 200
|
54
|
+
ptos_list = normalize_response(response.parsed_response["results"])
|
55
|
+
|
56
|
+
{ success: { ptos: ptos_list } }
|
57
|
+
else
|
58
|
+
{ error: { message: response.parsed_response, status_code: response.code } }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Write function to execute the PostgresDB write component
|
63
|
+
#
|
64
|
+
def write
|
65
|
+
write = Write::Postgres.new(write_options, process_response)
|
66
|
+
|
67
|
+
write.execute
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def params
|
73
|
+
{
|
74
|
+
endpoint: "databases/#{process_options[:database_id]}/query",
|
75
|
+
secret: process_options[:secret],
|
76
|
+
method: "post",
|
77
|
+
body:
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def body
|
82
|
+
{ filter: { "or": conditions } }
|
83
|
+
end
|
84
|
+
|
85
|
+
def conditions
|
86
|
+
[
|
87
|
+
today_condition,
|
88
|
+
{ property: "StartDateTime", date: { this_week: {} } },
|
89
|
+
{ property: "EndDateTime", date: { this_week: {} } },
|
90
|
+
{ property: "StartDateTime", date: { next_week: {} } },
|
91
|
+
{ property: "EndDateTime", date: { next_week: {} } }
|
92
|
+
]
|
93
|
+
end
|
94
|
+
|
95
|
+
def today_condition
|
96
|
+
today = Time.now.utc.strftime("%F").to_s
|
97
|
+
{
|
98
|
+
"and": [
|
99
|
+
{ property: "StartDateTime", date: { on_or_before: today } },
|
100
|
+
{ property: "EndDateTime", date: { on_or_after: today } }
|
101
|
+
]
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
def normalize_response(results)
|
106
|
+
return [] if results.nil?
|
107
|
+
|
108
|
+
results.map do |pto|
|
109
|
+
pto_fields = pto["properties"]
|
110
|
+
|
111
|
+
name = extract_description_field_value(pto_fields["Description"])
|
112
|
+
start_date = extract_date_field_value(pto_fields["StartDateTime"])
|
113
|
+
end_date = extract_date_field_value(pto_fields["EndDateTime"])
|
114
|
+
|
115
|
+
description(name, start_date, end_date)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def description(name, start_date, end_date)
|
120
|
+
start = start_description(start_date)
|
121
|
+
finish = end_description(end_date)
|
122
|
+
|
123
|
+
"#{name} will not be working between #{start} and #{finish}. And returns the #{returns(finish)}"
|
124
|
+
end
|
125
|
+
|
126
|
+
def start_description(date)
|
127
|
+
date[:from]
|
128
|
+
end
|
129
|
+
|
130
|
+
def end_description(date)
|
131
|
+
return date[:from] if date[:to].nil?
|
132
|
+
|
133
|
+
date[:to]
|
134
|
+
end
|
135
|
+
|
136
|
+
def returns(date)
|
137
|
+
date.include?("T") ? "#{date} in the afternoon" : next_work_day(date)
|
138
|
+
end
|
139
|
+
|
140
|
+
def next_work_day(date)
|
141
|
+
datetime = DateTime.parse(date)
|
142
|
+
|
143
|
+
return_day = case datetime.wday
|
144
|
+
when 5 then datetime + 3
|
145
|
+
when 6 then datetime + 2
|
146
|
+
else datetime + 1
|
147
|
+
end
|
148
|
+
|
149
|
+
return_day.strftime("%A %B %d of %Y").to_s
|
150
|
+
end
|
151
|
+
|
152
|
+
def extract_description_field_value(data)
|
153
|
+
names = data["title"].map { |name| name["plain_text"] }
|
154
|
+
|
155
|
+
names.join(" ")
|
156
|
+
end
|
157
|
+
|
158
|
+
def extract_date_field_value(date)
|
159
|
+
{
|
160
|
+
from: extract_start_date(date),
|
161
|
+
to: extract_end_date(date)
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
def extract_start_date(data)
|
166
|
+
data["date"]["start"]
|
167
|
+
end
|
168
|
+
|
169
|
+
def extract_end_date(data)
|
170
|
+
data["date"]["end"]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./base"
|
4
|
+
require_relative "../read/postgres"
|
5
|
+
require_relative "../write/postgres"
|
6
|
+
|
7
|
+
module Bot
|
8
|
+
##
|
9
|
+
# The Bot::FormatBirthdays class serves as a bot implementation to read birthdays from a
|
10
|
+
# PostgresDB database, format them with a specific template, and write them on a PostgresDB
|
11
|
+
# table with a specific format.
|
12
|
+
#
|
13
|
+
# <br>
|
14
|
+
# <b>Example</b>
|
15
|
+
#
|
16
|
+
# options = {
|
17
|
+
# read_options: {
|
18
|
+
# connection: {
|
19
|
+
# host: "localhost",
|
20
|
+
# port: 5432,
|
21
|
+
# dbname: "bas",
|
22
|
+
# user: "postgres",
|
23
|
+
# password: "postgres"
|
24
|
+
# },
|
25
|
+
# db_table: "use_cases",
|
26
|
+
# tag: "FetchBirthdaysFromNotion"
|
27
|
+
# },
|
28
|
+
# process_options: {
|
29
|
+
# template: "birthday template message"
|
30
|
+
# },
|
31
|
+
# write_options: {
|
32
|
+
# connection: {
|
33
|
+
# host: "localhost",
|
34
|
+
# port: 5432,
|
35
|
+
# dbname: "bas",
|
36
|
+
# user: "postgres",
|
37
|
+
# password: "postgres"
|
38
|
+
# },
|
39
|
+
# db_table: "use_cases",
|
40
|
+
# tag: "FormatBirthdays"
|
41
|
+
# }
|
42
|
+
# }
|
43
|
+
#
|
44
|
+
# bot = Bot::FormatBirthdays.new(options)
|
45
|
+
# bot.execute
|
46
|
+
#
|
47
|
+
class FormatBirthdays < Bot::Base
|
48
|
+
BIRTHDAY_ATTRIBUTES = %w[name birthday_date].freeze
|
49
|
+
|
50
|
+
# read function to execute the PostgresDB Read component
|
51
|
+
#
|
52
|
+
def read
|
53
|
+
reader = Read::Postgres.new(read_options.merge(conditions))
|
54
|
+
|
55
|
+
reader.execute
|
56
|
+
end
|
57
|
+
|
58
|
+
# Process function to format the notification using a template
|
59
|
+
#
|
60
|
+
def process
|
61
|
+
return { success: { notification: "" } } if unprocessable_response
|
62
|
+
|
63
|
+
birthdays_list = read_response.data["birthdays"]
|
64
|
+
|
65
|
+
notification = birthdays_list.reduce("") do |payload, birthday|
|
66
|
+
"#{payload} #{build_template(BIRTHDAY_ATTRIBUTES, birthday)} \n"
|
67
|
+
end
|
68
|
+
|
69
|
+
{ success: { notification: } }
|
70
|
+
end
|
71
|
+
|
72
|
+
# Write function to execute the PostgresDB write component
|
73
|
+
#
|
74
|
+
def write
|
75
|
+
write = Write::Postgres.new(write_options, process_response)
|
76
|
+
|
77
|
+
write.execute
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def conditions
|
83
|
+
{
|
84
|
+
where: "archived=$1 AND tag=$2 AND stage=$3 ORDER BY inserted_at ASC",
|
85
|
+
params: [false, read_options[:tag], "unprocessed"]
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def build_template(attributes, instance)
|
90
|
+
template = process_options[:template]
|
91
|
+
|
92
|
+
attributes.reduce(template) do |formated_template, attribute|
|
93
|
+
formated_template.gsub("<#{attribute}>", instance[attribute].to_s)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./base"
|
4
|
+
require_relative "../read/postgres"
|
5
|
+
require_relative "../write/postgres"
|
6
|
+
|
7
|
+
module Bot
|
8
|
+
##
|
9
|
+
# The Bot::FormatEmails class serves as a bot implementation to read emails from a
|
10
|
+
# PostgresDB database, format them with a specific template, and write them on a PostgresDB
|
11
|
+
# table with a specific format.
|
12
|
+
#
|
13
|
+
# <br>
|
14
|
+
# <b>Example</b>
|
15
|
+
#
|
16
|
+
# options = {
|
17
|
+
# read_options: {
|
18
|
+
# connection: {
|
19
|
+
# host: "localhost",
|
20
|
+
# port: 5432,
|
21
|
+
# dbname: "bas",
|
22
|
+
# user: "postgres",
|
23
|
+
# password: "postgres"
|
24
|
+
# },
|
25
|
+
# db_table: "use_cases",
|
26
|
+
# tag: "FetchEmailsFromImap"
|
27
|
+
# },
|
28
|
+
# process_options: {
|
29
|
+
# template: "emails template message"
|
30
|
+
# },
|
31
|
+
# write_options: {
|
32
|
+
# connection: {
|
33
|
+
# host: "localhost",
|
34
|
+
# port: 5432,
|
35
|
+
# dbname: "bas",
|
36
|
+
# user: "postgres",
|
37
|
+
# password: "postgres"
|
38
|
+
# },
|
39
|
+
# db_table: "use_cases",
|
40
|
+
# tag: "FormatEmails"
|
41
|
+
# }
|
42
|
+
# }
|
43
|
+
#
|
44
|
+
# bot = Bot::FormatEmails.new(options)
|
45
|
+
# bot.execute
|
46
|
+
#
|
47
|
+
class FormatEmails < Bot::Base
|
48
|
+
EMAIL_ATTRIBUTES = %w[subject sender date].freeze
|
49
|
+
DEFAULT_TIME_ZONE = "+00:00"
|
50
|
+
|
51
|
+
# read function to execute the PostgresDB Read component
|
52
|
+
#
|
53
|
+
def read
|
54
|
+
reader = Read::Postgres.new(read_options.merge(conditions))
|
55
|
+
|
56
|
+
reader.execute
|
57
|
+
end
|
58
|
+
|
59
|
+
# Process function to format the notification using a template
|
60
|
+
#
|
61
|
+
def process
|
62
|
+
return { success: { notification: "" } } if unprocessable_response
|
63
|
+
|
64
|
+
emails_list = read_response.data["emails"]
|
65
|
+
|
66
|
+
notification = process_emails(emails_list).reduce("") do |payload, email|
|
67
|
+
"#{payload} #{build_template(EMAIL_ATTRIBUTES, email)} \n"
|
68
|
+
end
|
69
|
+
|
70
|
+
{ success: { notification: } }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Write function to execute the PostgresDB write component
|
74
|
+
#
|
75
|
+
def write
|
76
|
+
write = Write::Postgres.new(write_options, process_response)
|
77
|
+
|
78
|
+
write.execute
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def conditions
|
84
|
+
{
|
85
|
+
where: "archived=$1 AND tag=$2 AND stage=$3 ORDER BY inserted_at ASC",
|
86
|
+
params: [false, read_options[:tag], "unprocessed"]
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def process_emails(emails)
|
91
|
+
emails.each do |email|
|
92
|
+
date = DateTime.parse(email["date"]).to_time
|
93
|
+
email["date"] = at_timezone(date)
|
94
|
+
end
|
95
|
+
emails.filter! { |email| email["date"] > time_window } unless process_options[:frequency].nil?
|
96
|
+
|
97
|
+
format_timestamp(emails)
|
98
|
+
end
|
99
|
+
|
100
|
+
def format_timestamp(emails)
|
101
|
+
emails.each { |email| email["date"] = email["date"].strftime("%F %r") }
|
102
|
+
end
|
103
|
+
|
104
|
+
def time_window
|
105
|
+
date_time = Time.now - (60 * 60 * process_options[:frequency])
|
106
|
+
|
107
|
+
at_timezone(date_time)
|
108
|
+
end
|
109
|
+
|
110
|
+
def at_timezone(date)
|
111
|
+
timezone = process_options[:timezone] || DEFAULT_TIME_ZONE
|
112
|
+
|
113
|
+
Time.at(date, in: timezone)
|
114
|
+
end
|
115
|
+
|
116
|
+
def build_template(attributes, instance)
|
117
|
+
template = process_options[:template]
|
118
|
+
|
119
|
+
attributes.reduce(template) do |formated_template, attribute|
|
120
|
+
formated_template.gsub("<#{attribute}>", instance[attribute].to_s)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./base"
|
4
|
+
require_relative "../read/postgres"
|
5
|
+
require_relative "../write/postgres"
|
6
|
+
|
7
|
+
module Bot
|
8
|
+
##
|
9
|
+
# The Bot::FormatWipLimitExceeded class serves as a bot implementation to read exceeded domain wip
|
10
|
+
# counts by limits from a PostgresDB database, format them with a specific template, and write them
|
11
|
+
# on a PostgresDB table with a specific format.
|
12
|
+
#
|
13
|
+
# <br>
|
14
|
+
# <b>Example</b>
|
15
|
+
#
|
16
|
+
# options = {
|
17
|
+
# read_options: {
|
18
|
+
# connection: {
|
19
|
+
# host: "localhost",
|
20
|
+
# port: 5432,
|
21
|
+
# dbname: "bas",
|
22
|
+
# user: "postgres",
|
23
|
+
# password: "postgres"
|
24
|
+
# },
|
25
|
+
# db_table: "use_cases",
|
26
|
+
# tag: "CompareWipLimitCount"
|
27
|
+
# },
|
28
|
+
# process_options: {
|
29
|
+
# template: "exceeded wip limit template message"
|
30
|
+
# },
|
31
|
+
# write_options: {
|
32
|
+
# connection: {
|
33
|
+
# host: "localhost",
|
34
|
+
# port: 5432,
|
35
|
+
# dbname: "bas",
|
36
|
+
# user: "postgres",
|
37
|
+
# password: "postgres"
|
38
|
+
# },
|
39
|
+
# db_table: "use_cases",
|
40
|
+
# tag: "FormatWipLimitExceeded"
|
41
|
+
# }
|
42
|
+
# }
|
43
|
+
#
|
44
|
+
# bot = Bot::FormatWipLimitExceeded.new(options)
|
45
|
+
# bot.execute
|
46
|
+
#
|
47
|
+
class FormatWipLimitExceeded < Bot::Base
|
48
|
+
WIP_LIMIT_ATTRIBUTES = %w[domain exceeded].freeze
|
49
|
+
|
50
|
+
# read function to execute the PostgresDB Read component
|
51
|
+
#
|
52
|
+
def read
|
53
|
+
reader = Read::Postgres.new(read_options.merge(conditions))
|
54
|
+
|
55
|
+
reader.execute
|
56
|
+
end
|
57
|
+
|
58
|
+
# Process function to format the notification using a template
|
59
|
+
#
|
60
|
+
def process
|
61
|
+
return { success: { notification: "" } } if unprocessable_response
|
62
|
+
|
63
|
+
exceedded_limits_list = read_response.data["exceeded_domain_count"]
|
64
|
+
|
65
|
+
notification = exceedded_limits_list.reduce("") do |payload, exceedded_limit|
|
66
|
+
"#{payload} #{build_template(WIP_LIMIT_ATTRIBUTES, exceedded_limit)} \n"
|
67
|
+
end
|
68
|
+
|
69
|
+
{ success: { notification: } }
|
70
|
+
end
|
71
|
+
|
72
|
+
# Write function to execute the PostgresDB write component
|
73
|
+
#
|
74
|
+
def write
|
75
|
+
write = Write::Postgres.new(write_options, process_response)
|
76
|
+
|
77
|
+
write.execute
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def conditions
|
83
|
+
{
|
84
|
+
where: "archived=$1 AND tag=$2 AND stage=$3 ORDER BY inserted_at ASC",
|
85
|
+
params: [false, read_options[:tag], "unprocessed"]
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def build_template(attributes, instance)
|
90
|
+
template = process_options[:template]
|
91
|
+
|
92
|
+
attributes.reduce(template) do |formated_template, attribute|
|
93
|
+
formated_template.gsub("<#{attribute}>", instance[attribute].to_s)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./base"
|
4
|
+
require_relative "../read/default"
|
5
|
+
require_relative "../write/postgres"
|
6
|
+
|
7
|
+
module Bot
|
8
|
+
##
|
9
|
+
# The Bot::GarbageCollector class serves as a bot implementation to archive bot records from a
|
10
|
+
# PostgresDB database table and write a response on a PostgresDB table with a specific format.
|
11
|
+
#
|
12
|
+
# <br>
|
13
|
+
# <b>Example</b>
|
14
|
+
#
|
15
|
+
# options = {
|
16
|
+
# process_options: {
|
17
|
+
# connection: {
|
18
|
+
# host: "localhost",
|
19
|
+
# port: 5432,
|
20
|
+
# dbname: "bas",
|
21
|
+
# user: "postgres",
|
22
|
+
# password: "postgres"
|
23
|
+
# },
|
24
|
+
# db_table: "use_cases"
|
25
|
+
# },
|
26
|
+
# write_options: {
|
27
|
+
# connection: {
|
28
|
+
# host: "localhost",
|
29
|
+
# port: 5432,
|
30
|
+
# dbname: "bas",
|
31
|
+
# user: "postgres",
|
32
|
+
# password: "postgres"
|
33
|
+
# },
|
34
|
+
# db_table: "use_cases"
|
35
|
+
# }
|
36
|
+
# }
|
37
|
+
#
|
38
|
+
# bot = Bot::GarbageCollector.new(options)
|
39
|
+
# bot.execute
|
40
|
+
#
|
41
|
+
class GarbageCollector < Bot::Base
|
42
|
+
SUCCESS_STATUS = "PGRES_COMMAND_OK"
|
43
|
+
|
44
|
+
# Read function to execute the default Read component
|
45
|
+
#
|
46
|
+
def read
|
47
|
+
reader = Read::Default.new
|
48
|
+
|
49
|
+
reader.execute
|
50
|
+
end
|
51
|
+
|
52
|
+
# Process function to update records in a PostgresDB database table
|
53
|
+
#
|
54
|
+
def process
|
55
|
+
response = Utils::Postgres::Request.execute(params)
|
56
|
+
|
57
|
+
if response.res_status == SUCCESS_STATUS
|
58
|
+
{ success: { archived: true } }
|
59
|
+
else
|
60
|
+
{ error: { message: response.result_error_message, status_code: response.res_status } }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Write function to execute the PostgresDB write component
|
65
|
+
#
|
66
|
+
def write
|
67
|
+
write = Write::Postgres.new(write_options, process_response)
|
68
|
+
|
69
|
+
write.execute
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def params
|
75
|
+
{
|
76
|
+
connection: process_options[:connection],
|
77
|
+
query:
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def query
|
82
|
+
"UPDATE #{process_options[:db_table]} SET archived=true WHERE archived=false"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./base"
|
4
|
+
require_relative "../read/postgres"
|
5
|
+
require_relative "../write/postgres"
|
6
|
+
require_relative "../utils/openai/run_assistant"
|
7
|
+
|
8
|
+
module Bot
|
9
|
+
##
|
10
|
+
# The Bot::HumanizePto class serves as a bot implementation to read PTO's from a
|
11
|
+
# PostgresDb table, format them using an OpenAI Assistant with the OpenAI API, and
|
12
|
+
# write the response as a notification on a PostgresDB table.
|
13
|
+
#
|
14
|
+
# <br>
|
15
|
+
# <b>Example</b>
|
16
|
+
#
|
17
|
+
# options = {
|
18
|
+
# read_options: {
|
19
|
+
# connection: {
|
20
|
+
# host: "host",
|
21
|
+
# port: 5432,
|
22
|
+
# dbname: "bas",
|
23
|
+
# user: "postgres",
|
24
|
+
# password: "postgres"
|
25
|
+
# },
|
26
|
+
# db_table: "pto",
|
27
|
+
# tag: "FetchPtosFromNotion"
|
28
|
+
# },
|
29
|
+
# process_options: {
|
30
|
+
# secret: "openai secret key",
|
31
|
+
# assistant_id: "assistant_id",
|
32
|
+
# prompt: "optional additional prompt"
|
33
|
+
# },
|
34
|
+
# write_options: {
|
35
|
+
# connection: {
|
36
|
+
# host: "host",
|
37
|
+
# port: 5432,
|
38
|
+
# dbname: "bas",
|
39
|
+
# user: "postgres",
|
40
|
+
# password: "postgres"
|
41
|
+
# },
|
42
|
+
# db_table: "pto",
|
43
|
+
# tag: "HumanizePto"
|
44
|
+
# }
|
45
|
+
# }
|
46
|
+
#
|
47
|
+
# bot = Bot::HumanizePto.new(options)
|
48
|
+
# bot.execute
|
49
|
+
#
|
50
|
+
class HumanizePto < Bot::Base
|
51
|
+
DEFAULT_PROMPT = "{data}"
|
52
|
+
|
53
|
+
# read function to execute the PostgresDB Read component
|
54
|
+
#
|
55
|
+
def read
|
56
|
+
reader = Read::Postgres.new(read_options.merge(conditions))
|
57
|
+
|
58
|
+
reader.execute
|
59
|
+
end
|
60
|
+
|
61
|
+
# process function to execute the OpenaAI utility to process the PTO's
|
62
|
+
#
|
63
|
+
def process
|
64
|
+
return { success: { notification: "" } } if unprocessable_response
|
65
|
+
|
66
|
+
response = Utils::OpenAI::RunAssitant.execute(params)
|
67
|
+
|
68
|
+
if response.code != 200 || (!response["status"].nil? && response["status"] != "completed")
|
69
|
+
return error_response(response)
|
70
|
+
end
|
71
|
+
|
72
|
+
sucess_response(response)
|
73
|
+
end
|
74
|
+
|
75
|
+
# write function to execute the PostgresDB write component
|
76
|
+
#
|
77
|
+
def write
|
78
|
+
write = Write::Postgres.new(write_options, process_response)
|
79
|
+
|
80
|
+
write.execute
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def conditions
|
86
|
+
{
|
87
|
+
where: "archived=$1 AND tag=$2 AND stage=$3 ORDER BY inserted_at ASC",
|
88
|
+
params: [false, read_options[:tag], "unprocessed"]
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def params
|
93
|
+
{
|
94
|
+
assistant_id: process_options[:assistant_id],
|
95
|
+
secret: process_options[:secret],
|
96
|
+
prompt: build_prompt
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
def build_prompt
|
101
|
+
prompt = process_options[:prompt] || DEFAULT_PROMPT
|
102
|
+
ptos_list = read_response.data["ptos"]
|
103
|
+
|
104
|
+
ptos_list_formatted_string = ptos_list.join("\n")
|
105
|
+
|
106
|
+
prompt.gsub("{data}", ptos_list_formatted_string)
|
107
|
+
end
|
108
|
+
|
109
|
+
def sucess_response(response)
|
110
|
+
{ success: { notification: response.parsed_response["data"].first["content"].first["text"]["value"] } }
|
111
|
+
end
|
112
|
+
|
113
|
+
def error_response(response)
|
114
|
+
{ error: { message: response.parsed_response, status_code: response.code } }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|