jekyll-chatgpt-translate 0.0.5 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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