jekyll-chatgpt-translate 0.0.5 → 0.0.7

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: 8e80c34ee1b28dc84b884ad53ef3f68d1b5bb9d0ba9620e1c50e7fe97abe6785
4
- data.tar.gz: d47885e2ac998c1d57004c784baba4ecb3988ec39d697771a0082a76a46097a2
3
+ metadata.gz: 951ecc1df96633e4552be477e42c43977b0187282978d9cb1c07bedc3913e8fa
4
+ data.tar.gz: 9fb20f291797eaee903f0ca20aaf0adcc7ed65fd77626dda6afbe3c6390418cd
5
5
  SHA512:
6
- metadata.gz: 5cc86659f819d55cab262ce1658a70e25b1ba361ec46a03d43063e644bb2ba01d9e0e986a29fd1c85ca0867e87e00bd7dfb5852c448ec29ba9bd72f1c6d998a9
7
- data.tar.gz: b1735adc1ac05a53bb65a9e49467bd4aa79299ab9a0c9de1e68d199ce8852820ff8d9f260f8f9d5f7137432fbd27d69b69d18f3cbbaa3d5730e7439253764523
6
+ metadata.gz: d94a2be708d58bb23427a707660276ce7d42dc90372924644b76558933c631834d3eeece4b1bb46c5b89221c3d758b62e71958137e355edb02451ef671fc9f03
7
+ data.tar.gz: 623f0e383d89805288678b37d9304d365b3adf6995d60c9bec4e7a9e6c85e2be44cb51e4a1001892cb5919a55799f99dfe9e174c5edf931f9f2e641d24ffed37
data/Gemfile CHANGED
@@ -32,3 +32,4 @@ gem 'rake', '13.0.6', require: false
32
32
  gem 'rubocop', '1.55.1', require: false
33
33
  gem 'rubocop-rspec', '2.23.2', require: false
34
34
  gem 'simplecov', '0.22.0', require: false
35
+ gem 'webmock', '3.18.1', require: false
data/features/cli.feature CHANGED
@@ -13,11 +13,11 @@ Feature: Simple site building
13
13
  targets:
14
14
  -
15
15
  language: cn
16
- permalink: :year-:month-:day-:title-chinese.html
16
+ permalink: :year-:month-:day-:slug-chinese.html
17
17
  layout: chinese-translated
18
18
  -
19
19
  language: fr
20
- permalink: :year/:title-french.html
20
+ permalink: :year/:slug-french.html
21
21
  """
22
22
  And I have a "_layouts/default.html" file with content:
23
23
  """
@@ -30,13 +30,15 @@ Feature: Simple site building
30
30
  And I have a "_posts/2023-01-01-hello.md" file with content:
31
31
  """
32
32
  ---
33
+ title: Hello, world!
33
34
  layout: default
34
35
  ---
35
36
  Hello, world!
36
37
  """
37
38
  Then I build Jekyll site
39
+ Then File "_chatgpt-translated/cn/2023-01-01-hello-cn.md" exists
38
40
  Then File "_site/2023/01/01/hello.html" exists
39
- Then File "_site/2023-01-01-Hello-chinese.html" exists
40
- Then File "_site/2023/Hello-french.html" exists
41
+ Then File "_site/2023-01-01-hello-chinese.html" exists
42
+ Then File "_site/2023/hello-french.html" exists
41
43
  And Exit code is zero
42
44
 
@@ -50,7 +50,7 @@ Then(/^Stdout contains "([^"]*)"$/) do |txt|
50
50
  end
51
51
 
52
52
  Then(/^File "([^"]*)" exists$/) do |name|
53
- raise unless File.exist?(name)
53
+ raise "The file \"#{name}\" is absent:\n#{`tree`}" unless File.exist?(name)
54
54
  end
55
55
 
56
56
  Then(/^Stdout is empty$/) do
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
28
28
  s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
29
29
  s.required_ruby_version = '>= 2.6'
30
30
  s.name = 'jekyll-chatgpt-translate'
31
- s.version = '0.0.5'
31
+ s.version = '0.0.7'
32
32
  s.license = 'MIT'
33
33
  s.summary = 'Translate Jekyll Pages Through ChatGPT'
34
34
  s.description = [
@@ -65,7 +65,7 @@ class GptTranslate::ChatGPT
65
65
  private
66
66
 
67
67
  def translate_par(par)
68
- Time.now
68
+ start = Time.now
69
69
  t = nil
70
70
  attempt = 0
71
71
  begin
@@ -80,26 +80,21 @@ class GptTranslate::ChatGPT
80
80
  }
81
81
  )
82
82
  t = response.dig('choices', 0, 'message', 'content')
83
- rescue StandardError
83
+ rescue StandardError => e
84
84
  attempt += 1
85
85
  retry if attempt < 4
86
+ raise e
86
87
  end
87
- if t.nil?
88
- Jekyll.logger.error("Failed to translate #{par.split.count}
89
- #{@source.upcase} words after #{attempt} attempts :(")
90
- 'FAILED TO TRANSLATE THIS PARAGRAPH'
91
- else
92
- Jekyll.logger.debug("Translated #{par.split.count} #{@source.upcase} words
93
- to #{t.split.count} #{@target.upcase} words
94
- through #{@model} in #{(Time.now - pstart).round(2)}s")
95
- t
96
- end
88
+ Jekyll.logger.info("Translated #{par.split.count} #{@source.upcase} words \
89
+ to #{t.split.count} #{@target.upcase} words \
90
+ through #{@model} in #{(Time.now - start).round(2)}s")
91
+ t
97
92
  end
98
93
 
99
94
  def prompt
100
- if (source == 'ru') && (target == 'en')
95
+ if (@source == 'ru') && (@target == 'en')
101
96
  'Пожалуйста, переведи этот параграф на английский язык'
102
- elsif (source == 'en') && (target == 'ru')
97
+ elsif (@source == 'en') && (@target == 'ru')
103
98
  'Please, translate this paragraph to Russian'
104
99
  else
105
100
  [
@@ -24,6 +24,7 @@
24
24
 
25
25
  require 'jekyll'
26
26
  require_relative 'chatgpt'
27
+ require_relative 'permalink'
27
28
  require_relative 'ping'
28
29
  require_relative 'plain'
29
30
  require_relative 'version'
@@ -42,11 +43,12 @@ class GptTranslate::Generator < Jekyll::Generator
42
43
  # Main plugin action, called by Jekyll-core
43
44
  def generate(site)
44
45
  @site = site
46
+ home = '_chatgpt-translated'
45
47
  key = ENV.fetch('OPENAI_API_KEY', nil)
46
48
  if key.nil? && Jekyll.env == 'development'
47
- Jekyll.logger.info('OPENAI_API_KEY environment variable is not set and
48
- we are in development mode, no actual translation will happen,
49
- but pages will be generated')
49
+ Jekyll.logger.info("OPENAI_API_KEY environment variable is not set and \
50
+ we are in development mode, no actual translation will happen, \
51
+ but pages will be generated")
50
52
  key = ''
51
53
  end
52
54
  if key.nil?
@@ -60,7 +62,7 @@ but pages will be generated')
60
62
  site.posts.docs.each do |doc|
61
63
  plain = GptTranslate::Plain.new(doc.content).to_s
62
64
  config['targets'].each do |target|
63
- link = permalink(doc, target['permalink'])
65
+ link = GptTranslate::Permalink.new(doc, target['permalink']).to_path
64
66
  next if GptTranslate::Ping.new(site, link).exists?
65
67
  lang = target['language']
66
68
  raise 'Language must be defined for each target' if target.nil?
@@ -76,7 +78,7 @@ but pages will be generated')
76
78
  lang
77
79
  )
78
80
  translated = gpt.translate(plain)
79
- path = "_chatgpt-translated/#{doc.basename}"
81
+ path = File.join(home, lang, doc.basename.gsub(/\.md$/, "-#{lang}.md"))
80
82
  FileUtils.mkdir_p(File.dirname(path))
81
83
  File.write(
82
84
  path,
@@ -97,7 +99,7 @@ but pages will be generated')
97
99
  end
98
100
  break if total >= threshold
99
101
  end
100
- Jekyll.logger.info("#{total} pages generated in #{(Time.now - start).round(2)}s")
102
+ Jekyll.logger.info("#{total} pages translated in #{(Time.now - start).round(2)}s")
101
103
  end
102
104
 
103
105
  private
@@ -106,13 +108,4 @@ but pages will be generated')
106
108
  def config
107
109
  @config ||= @site.config['chatgpt-translate'] || {}
108
110
  end
109
-
110
- def permalink(doc, template)
111
- raise 'permalink must be defined for each target' if template.nil?
112
- template
113
- .gsub(':year', format('%04d', doc['date'].year))
114
- .gsub(':month', format('%02d', doc['date'].month))
115
- .gsub(':day', format('%02d', doc['date'].day))
116
- .gsub(':title', doc['title'])
117
- end
118
111
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (The MIT License)
4
+ #
5
+ # Copyright (c) 2023 Yegor Bugayenko
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the 'Software'), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+
25
+ require 'cgi'
26
+
27
+ # The module we are in.
28
+ module GptTranslate; end
29
+
30
+ # Permalink.
31
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
32
+ # Copyright:: Copyright (c) 2023 Yegor Bugayenko
33
+ # License:: MIT
34
+ class GptTranslate::Permalink
35
+ def initialize(doc, template)
36
+ @doc = doc
37
+ raise 'permalink must be defined for each target' if template.nil?
38
+ @template = template
39
+ end
40
+
41
+ def to_path
42
+ path = @template
43
+ .gsub(':year', format('%04d', @doc['date'].year))
44
+ .gsub(':month', format('%02d', @doc['date'].month))
45
+ .gsub(':day', format('%02d', @doc['date'].day))
46
+ .gsub(':title', CGI.escape(@doc['title']))
47
+ .gsub(':slug', CGI.escape(@doc['slug']))
48
+ path = "/#{path}" unless path.start_with?('/')
49
+ path
50
+ end
51
+ end
@@ -40,15 +40,16 @@ module GptTranslate; end
40
40
  # License:: MIT
41
41
  class GptTranslate::Ping
42
42
  # Ctor.
43
- def initialize(site, link)
43
+ def initialize(site, path)
44
44
  @site = site
45
- @link = link
45
+ raise 'Permalink must start with a slash' unless path.start_with?('/')
46
+ @path = path
46
47
  end
47
48
 
48
49
  def exists?
49
50
  home = @site.config['url']
50
51
  return false if home.nil?
51
- uri = Iri.new(home).path(@link)
52
+ uri = Iri.new(home).path(@path)
52
53
  before = Net::HTTP.get_response(URI(uri.to_s))
53
54
  if before.is_a?(Net::HTTPSuccess) && before.body.include?("/#{GptTranslate::VERSION}")
54
55
  Jekyll.logger.info("No need to translate, page exists at #{uri} (#{before.body.split.count} words)")
data/test/test_chatgpt.rb CHANGED
@@ -23,6 +23,7 @@
23
23
  # SOFTWARE.
24
24
 
25
25
  require 'minitest/autorun'
26
+ require 'webmock/minitest'
26
27
  require_relative '../lib/jekyll-chatgpt-translate/chatgpt'
27
28
 
28
29
  # ChatGPT test.
@@ -44,4 +45,11 @@ class GptTranslate::ChatGPTTest < Minitest::Test
44
45
  chat = GptTranslate::ChatGPT.new('fake-key', 'gpt-3.5-turbo', 'en', 'ru')
45
46
  assert_equal('<img src="a"/>', chat.translate('<img src="a"/>'))
46
47
  end
48
+
49
+ def test_through_webmock
50
+ stub_request(:any, 'https://api.openai.com/v1/chat/completions')
51
+ .to_return(body: '{"choices":[{"message":{"content": "boom!"}}]}')
52
+ chat = GptTranslate::ChatGPT.new('fake-key', 'gpt-3.5-turbo', 'en', 'ru')
53
+ assert_equal('boom!', chat.translate('This is the text to send to OpenAI'))
54
+ end
47
55
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (The MIT License)
4
+ #
5
+ # Copyright (c) 2023 Yegor Bugayenko
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the 'Software'), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+
25
+ require 'minitest/autorun'
26
+ require_relative '../lib/jekyll-chatgpt-translate/permalink'
27
+
28
+ # Permalink test.
29
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
+ # Copyright:: Copyright (c) 2023 Yegor Bugayenko
31
+ # License:: MIT
32
+ class GptTranslate::PermalinkTest < Minitest::Test
33
+ def test_simple_link
34
+ assert_equal(
35
+ '/2023.html',
36
+ GptTranslate::Permalink.new(
37
+ { 'date' => Time.parse('2023-01-01'), 'title' => 'Hello', 'slug' => 'hello' },
38
+ ':year.html'
39
+ ).to_path
40
+ )
41
+ end
42
+
43
+ def test_unicode_link
44
+ assert_equal(
45
+ '/2023-%23%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82.html',
46
+ GptTranslate::Permalink.new(
47
+ { 'date' => Time.parse('2023-01-01'), 'title' => '#привет', 'slug' => 'hello' },
48
+ ':year-:title.html'
49
+ ).to_path
50
+ )
51
+ end
52
+ end
data/test/test_ping.rb CHANGED
@@ -23,6 +23,7 @@
23
23
  # SOFTWARE.
24
24
 
25
25
  require 'minitest/autorun'
26
+ require 'webmock/minitest'
26
27
  require 'jekyll'
27
28
  require_relative '../lib/jekyll-chatgpt-translate/ping'
28
29
 
@@ -40,8 +41,22 @@ class GptTranslate::PingTest < Minitest::Test
40
41
  end
41
42
 
42
43
  def test_when_exists
44
+ stub_request(:any, 'https://www.yegor256.com/about-me.html')
43
45
  site = FakeSite.new({ 'url' => 'https://www.yegor256.com/' })
44
46
  ping = GptTranslate::Ping.new(site, '/about-me.html')
45
47
  assert(!ping.exists?)
46
48
  end
49
+
50
+ def test_when_not_exists
51
+ stub_request(:any, 'https://www.yegor256.com/absent.html').to_return(status: 404)
52
+ site = FakeSite.new({ 'url' => 'https://www.yegor256.com/' })
53
+ ping = GptTranslate::Ping.new(site, '/absent.html')
54
+ assert(!ping.exists?)
55
+ end
56
+
57
+ def test_relative_path
58
+ assert_raises do
59
+ GptTranslate::Ping.new({}, '404.html')
60
+ end
61
+ end
47
62
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-chatgpt-translate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-20 00:00:00.000000000 Z
11
+ date: 2023-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: iri
@@ -121,6 +121,7 @@ files:
121
121
  - lib/jekyll-chatgpt-translate.rb
122
122
  - lib/jekyll-chatgpt-translate/chatgpt.rb
123
123
  - lib/jekyll-chatgpt-translate/generator.rb
124
+ - lib/jekyll-chatgpt-translate/permalink.rb
124
125
  - lib/jekyll-chatgpt-translate/ping.rb
125
126
  - lib/jekyll-chatgpt-translate/plain.rb
126
127
  - lib/jekyll-chatgpt-translate/version.rb
@@ -128,6 +129,7 @@ files:
128
129
  - test/test__helper.rb
129
130
  - test/test_chatgpt.rb
130
131
  - test/test_generator.rb
132
+ - test/test_permalink.rb
131
133
  - test/test_ping.rb
132
134
  - test/test_plain.rb
133
135
  homepage: https://github.com/yegor256/jekyll-chatgpt-translate