From c645b94ff8c053b5a42ac447f3370efa02651036 Mon Sep 17 00:00:00 2001
From: Arnaud Levy <contact@arnaudlevy.com>
Date: Mon, 25 Oct 2021 08:45:49 +0200
Subject: [PATCH] batches

---
 .../communication/website/imported/page.rb    |  1 +
 .../communication/website/imported/website.rb | 10 +++
 app/models/communication/website/page.rb      | 24 +++----
 app/models/communication/website/post.rb      | 10 ++-
 app/models/concerns/with_github.rb            | 13 ++--
 app/services/github.rb                        | 69 ++++++++++++++-----
 .../communication/websites/show.html.erb      |  8 +--
 app/views/admin/layouts/application.html.erb  |  2 +-
 ...to_communication_website_imported_pages.rb |  5 ++
 db/schema.rb                                  |  3 +-
 10 files changed, 94 insertions(+), 51 deletions(-)
 create mode 100644 db/migrate/20211025062028_add_index_to_communication_website_imported_pages.rb

diff --git a/app/models/communication/website/imported/page.rb b/app/models/communication/website/imported/page.rb
index 86bd82880..595e88b64 100644
--- a/app/models/communication/website/imported/page.rb
+++ b/app/models/communication/website/imported/page.rb
@@ -23,6 +23,7 @@
 # Indexes
 #
 #  idx_communication_website_imported_pages_on_featured_medium_id  (featured_medium_id)
+#  index_communication_website_imported_pages_on_identifier        (identifier)
 #  index_communication_website_imported_pages_on_page_id           (page_id)
 #  index_communication_website_imported_pages_on_university_id     (university_id)
 #  index_communication_website_imported_pages_on_website_id        (website_id)
diff --git a/app/models/communication/website/imported/website.rb b/app/models/communication/website/imported/website.rb
index 882be65aa..dff61d59b 100644
--- a/app/models/communication/website/imported/website.rb
+++ b/app/models/communication/website/imported/website.rb
@@ -52,6 +52,7 @@ class Communication::Website::Imported::Website < ApplicationRecord
   end
 
   def sync_pages
+    Communication::Website::Page.skip_callback(:save, :after, :publish_to_github)
     wordpress.pages.each do |data|
       page = pages.where(university: university, identifier: data['id']).first_or_initialize
       page.data = data
@@ -66,6 +67,15 @@ class Communication::Website::Imported::Website < ApplicationRecord
       generated_page.parent = parent.page
       generated_page.save
     end
+    # Batch update all changes (1 query only, good for github API limits)
+    github = Github.with_site website
+    website.pages.find_each do |page|
+      github.add_to_batch path: page.github_path_generated,
+                          previous_path: page.github_path,
+                          data: page.to_jekyll
+    end
+    github.commit_batch '[Page] Batch update from import'
+    Communication::Website::Page.set_callback(:save, :after, :publish_to_github)
   end
 
   def sync_posts
diff --git a/app/models/communication/website/page.rb b/app/models/communication/website/page.rb
index a1900da43..b8b94fb27 100644
--- a/app/models/communication/website/page.rb
+++ b/app/models/communication/website/page.rb
@@ -64,6 +64,18 @@ class Communication::Website::Page < ApplicationRecord
     children.any?
   end
 
+  def github_path_generated
+    "_pages/#{path}/index.html".gsub('//', '/')
+  end
+
+  def to_jekyll
+    ApplicationController.render(
+      template: 'admin/communication/website/pages/jekyll',
+      layout: false,
+      assigns: { page: self }
+    )
+  end
+
   def to_s
     "#{ title }"
   end
@@ -77,16 +89,4 @@ class Communication::Website::Page < ApplicationRecord
   def update_children_paths
     children.each(&:save)
   end
-
-  def github_path_generated
-    "_pages/#{path}/index.html".gsub('//', '/')
-  end
-
-  def to_jekyll
-    ApplicationController.render(
-      template: 'admin/communication/website/pages/jekyll',
-      layout: false,
-      assigns: { page: self }
-    )
-  end
 end
diff --git a/app/models/communication/website/post.rb b/app/models/communication/website/post.rb
index 88e15c76f..9e25cdd85 100644
--- a/app/models/communication/website/post.rb
+++ b/app/models/communication/website/post.rb
@@ -46,12 +46,6 @@ class Communication::Website::Post < ApplicationRecord
 
   validates :title, presence: true
 
-  def to_s
-    "#{title}"
-  end
-
-  protected
-
   def github_path_generated
     "_posts/#{published_at.strftime "%Y/%m"}/#{published_at.strftime "%Y-%m-%d"}-#{slug}.html"
   end
@@ -63,4 +57,8 @@ class Communication::Website::Post < ApplicationRecord
       assigns: { post: self }
     )
   end
+
+  def to_s
+    "#{title}"
+  end
 end
diff --git a/app/models/concerns/with_github.rb b/app/models/concerns/with_github.rb
index 9fe689123..7aa18027e 100644
--- a/app/models/concerns/with_github.rb
+++ b/app/models/concerns/with_github.rb
@@ -28,11 +28,12 @@ module WithGithub
   end
 
   def publish_to_github
-    github.publish  path: github_path_generated,
-                    previous_path: github_path,
-                    commit: github_commit_message,
-                    data: to_jekyll
-    update_column :github_path, github_path_generated
+    if github.publish(path: github_path_generated,
+                      previous_path: github_path,
+                      commit: github_commit_message,
+                      data: to_jekyll)
+      update_column :github_path, github_path_generated
+    end
   end
-  handle_asynchronously :publish_to_github
+  handle_asynchronously :publish_to_github, queue: 'default'
 end
diff --git a/app/services/github.rb b/app/services/github.rb
index ce50a1d6e..dd068ed3f 100644
--- a/app/services/github.rb
+++ b/app/services/github.rb
@@ -26,8 +26,9 @@ class Github
                             commit,
                             file: local_path,
                             sha: file_sha(path)
+    true
   rescue
-    # byebug
+    false
   end
 
   def send_file(attachment, path)
@@ -39,8 +40,44 @@ class Github
                             commit_message,
                             attachment.download,
                             sha: file_sha(path)
+    true
   rescue
-    # byebug
+    false
+  end
+
+  def add_to_batch( path: nil,
+                    previous_path: nil,
+                    data:)
+    @batch ||= []
+    file = find_in_tree previous_path
+    if file.nil? # New file
+      @batch << {
+        path: path,
+        mode: '100644', # https://docs.github.com/en/rest/reference/git#create-a-tree
+        type: 'blob',
+        content: data
+      }
+    else # Existing file
+      @batch << {
+        path: previous_path,
+        mode: file[:mode],
+        type: file[:type],
+        sha: nil
+      }
+      @batch << {
+        path: path,
+        mode: file[:mode],
+        type: file[:type],
+        content: data
+      }
+    end
+  end
+
+  def commit_batch(commit_message)
+    new_tree = client.create_tree repository, @batch, base_tree: tree[:sha]
+    commit = client.create_commit repository, commit_message, new_tree[:sha], branch_sha
+    client.update_branch repository, default_branch, commit[:sha]
+    @tree = nil
   end
 
   def read_file_at(path)
@@ -50,6 +87,8 @@ class Github
     ''
   end
 
+  protected
+
   def pages
     list = client.contents repository, path: '_pages'
     list.map do |hash|
@@ -75,8 +114,6 @@ class Github
     @client ||= Octokit::Client.new access_token: access_token
   end
 
-  protected
-
   # https://medium.com/@obodley/renaming-a-file-using-the-git-api-fed1e6f04188
   def move_file(from, to)
     file = find_in_tree from
@@ -96,11 +133,11 @@ class Github
     new_tree = client.create_tree repository, content, base_tree: tree[:sha]
     message = "Move #{from} to #{to}"
     commit = client.create_commit repository, message, new_tree[:sha], branch_sha
-    [:main, :master].each do |branch|
-      client.update_branch repository, branch, commit[:sha]
-    end
+    client.update_branch repository, default_branch, commit[:sha]
+    @tree = nil
+    true
   rescue
-    ''
+    false
   end
 
   def file_sha(path)
@@ -113,18 +150,12 @@ class Github
     sha
   end
 
+  def default_branch
+    @default_branch ||= client.repo(repository)[:default_branch]
+  end
+
   def branch_sha
-    unless @branch_sha
-      [:main, :master].each do |branch|
-        begin
-          # Crashes if branch does not exist
-          response = client.branch repository, branch
-          @branch_sha = response['commit']['sha']
-        end
-        break if @branch_sha
-      end
-    end
-    @branch_sha
+    @branch_sha ||= client.branch(repository, default_branch)[:commit][:sha]
   end
 
   def tree
diff --git a/app/views/admin/communication/websites/show.html.erb b/app/views/admin/communication/websites/show.html.erb
index 50efde949..2a7724826 100644
--- a/app/views/admin/communication/websites/show.html.erb
+++ b/app/views/admin/communication/websites/show.html.erb
@@ -1,14 +1,10 @@
 <% content_for :title, @website %>
 
 <% content_for :title_right do %>
-  <%= link_to @website.domain_url, @website.domain_url, target: :_blank %>
-<% end %>
-
-
-<p>
+  <%= link_to @website.domain_url, @website.domain_url, target: :_blank %><br>
   <%= I18n.t("activerecord.attributes.communication/website.about_#{@website.about_type}") %>
   <%= link_to @website.about, [:admin, @website.about] unless @website.about.nil? %>
-</p>
+<% end %>
 
 <div class="card mt-5">
   <div class="card-header">
diff --git a/app/views/admin/layouts/application.html.erb b/app/views/admin/layouts/application.html.erb
index 003a54d20..94b603dc8 100644
--- a/app/views/admin/layouts/application.html.erb
+++ b/app/views/admin/layouts/application.html.erb
@@ -31,7 +31,7 @@
         <%= render 'admin/application/top' %>
         <main class="content">
           <div class="container-fluid p-0">
-            <p class="float-end pt-2"><%= yield :title_right %></p>
+            <p class="float-end text-end pt-1"><%= yield :title_right %></p>
             <h1><%= yield :title %></h1>
             <%= yield %>
           </div>
diff --git a/db/migrate/20211025062028_add_index_to_communication_website_imported_pages.rb b/db/migrate/20211025062028_add_index_to_communication_website_imported_pages.rb
new file mode 100644
index 000000000..fa7fd412a
--- /dev/null
+++ b/db/migrate/20211025062028_add_index_to_communication_website_imported_pages.rb
@@ -0,0 +1,5 @@
+class AddIndexToCommunicationWebsiteImportedPages < ActiveRecord::Migration[6.1]
+  def change
+    add_index :communication_website_imported_pages, :identifier
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 3449b82fb..a46467ee3 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 2021_10_23_153416) do
+ActiveRecord::Schema.define(version: 2021_10_25_062028) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "pgcrypto"
@@ -112,6 +112,7 @@ ActiveRecord::Schema.define(version: 2021_10_23_153416) do
     t.jsonb "data"
     t.uuid "featured_medium_id"
     t.index ["featured_medium_id"], name: "idx_communication_website_imported_pages_on_featured_medium_id"
+    t.index ["identifier"], name: "index_communication_website_imported_pages_on_identifier"
     t.index ["page_id"], name: "index_communication_website_imported_pages_on_page_id"
     t.index ["university_id"], name: "index_communication_website_imported_pages_on_university_id"
     t.index ["website_id"], name: "index_communication_website_imported_pages_on_website_id"
-- 
GitLab