Skip to content

Commit

Permalink
Merge pull request #708 from pil0u/main
Browse files Browse the repository at this point in the history
Release 2024.11.8
  • Loading branch information
pil0u authored Nov 27, 2024
2 parents 6e72685 + ce10f91 commit caf76c0
Show file tree
Hide file tree
Showing 18 changed files with 237 additions and 41 deletions.
19 changes: 14 additions & 5 deletions app/components/snippets/box_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
<div id="<%= @snippet.id %>"
class="w-full p-4 flex flex-col self-center gap-y-4 bg-aoc-gray-darkest border border-aoc-gray-dark group"
data-controller="show-more">
<div class="flex text-xs gap-x-3">
<%= link_to "permalink", snippet_path(day: @snippet.day, challenge: @snippet.challenge, anchor: @snippet.id), class: "link-explicit" %>

<% if @user_can_edit_snippet %>
<%= link_to "edit", edit_snippet_path(id: @snippet.id), class: "link-explicit" %>
<div class="flex items-center justify-between text-xs">
<div class="flex gap-x-3">
<%= link_to "permalink", snippet_path(day: @snippet.day, challenge: @snippet.challenge, anchor: @snippet.id), class: "link-explicit" %>

<% if @user_can_edit_snippet %>
<%= link_to "edit", edit_snippet_path(id: @snippet.id), class: "link-explicit" %>
<% end %>
</div>

<% if @user_can_discuss_snippet %>
<%= button_to discuss_snippet_path(@snippet), method: :patch, form: { target: "_blank" } do %>
<span class="link-explicit link-slack">discuss on Slack</span>
<% end %>
<% end %>
</div>

Expand All @@ -27,7 +36,7 @@
class="flex items-center gap-x-1"
data-controller="reactions"
data-reactions-snippet-id-value="<%= @snippet.id %>"
data-reactions-vote-value="<%= @snippet.reactions.find_by(user_id: @user.id).to_json %>">
data-reactions-vote-value="<%= @user_reaction_vote_value.to_json %>">
<% Reaction::TYPES.each do |type| %>
<%= render Snippets::ReactionComponent.new(snippet: @snippet, reaction_type: type.to_sym, user: @user) %>
<% end %>
Expand Down
2 changes: 2 additions & 0 deletions app/components/snippets/box_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ def initialize(snippet:, user:)
@snippet = snippet

@user_can_edit_snippet = @snippet.user == @user
@user_can_discuss_snippet = @snippet.user != @user && @snippet.user.slack_linked?
@user_reaction_vote_value = @snippet.reactions.find { _1.user_id == @user.id }
end
end
end
2 changes: 1 addition & 1 deletion app/components/snippets/reaction_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<span data-reactions-target="counter"
data-reaction-type="<%= @reaction_type %>"
class="font-semibold">
<%= @reactions.count %>
<%= @reactions.size %>
</span>
<% end %>
</div>
2 changes: 1 addition & 1 deletion app/components/snippets/reaction_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def initialize(snippet:, reaction_type:, user:)
@user = user
@snippet = snippet
@reaction_type = reaction_type
@reactions = @snippet.reactions.where(reaction_type:)
@reactions = @snippet.send(:"#{reaction_type}_reactions")
end
end
end
8 changes: 8 additions & 0 deletions app/controllers/days_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ def show
@participants = presenter.get

@puzzle = Puzzle.by_date(Aoc.begin_time.change(day: @day))

@part_1 = Puzzle::DIFFICULTY_LEVELS[@puzzle&.difficulty_part_1] || Puzzle::DEFAULT_DIFFICULTY
@part_2 = Puzzle::DIFFICULTY_LEVELS[@puzzle&.difficulty_part_2] || Puzzle::DEFAULT_DIFFICULTY
@difficulty_title = <<~TEXT
Estimated difficulty (experimental)
Part 1: #{@part_1[:difficulty]} #{@part_1[:colour]}
Part 2: #{@part_2[:difficulty]} #{@part_2[:colour]}
TEXT
end

private
Expand Down
52 changes: 40 additions & 12 deletions app/controllers/snippets_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,26 @@ def show
@language = params[:language]

@snippet = Snippets::Builder.call(language: current_user.favourite_language)
@snippets = Snippet.includes(:user, :reactions).where(day: @day, challenge: @challenge).order(created_at: :desc)

@languages = @snippets.pluck(:language).uniq.sort
@snippets = @snippets.where(language: @language) if @language
reaction_relations = Reaction::TYPES.map { |type| :"#{type}_reactions" }
base_snippets = Snippet.includes(:user, :reactions, *reaction_relations).where(day: @day, challenge: @challenge)
@languages = base_snippets.pluck(:language).uniq.sort

snippets_scope = @language.present? ? base_snippets.where(language: @language) : base_snippets

@snippets = snippets_scope.sort_by do |snippet|
total_reactions = snippet.reactions.size
learning_reactions = snippet.learning_reactions.size
hours_since_publish = (Time.current - snippet.created_at) / 1.hour

(1 + learning_reactions) * (1 + total_reactions) / ((1 + hours_since_publish)**1.8)
end.reverse

@text_area_placeholder = <<~TEXT
This box is super smart.
Paste your code directly here, it will work.
Write a super nice guide in Markdown, it will work too.
If your Markdown tutorial features Python code, choose Python as a language.
Paste in your code directly here. Most common languages are supported.
Markdown is supported: you can write an entire guide about your approach.
In that case, pick the language featured in your guide.
TEXT
end

Expand Down Expand Up @@ -49,21 +59,39 @@ def update
end
end

def discuss
@snippet = Snippet.find(params[:id])
return redirect_to @snippet.slack_url if @snippet.slack_url.present?

text = "`SOLUTION` Hey <@#{@snippet.user.slack_id}>, some people want to discuss your :#{@snippet.language}-hd: #{solution_markdown} on puzzle #{@snippet.day} part #{@snippet.challenge}"
message = client.chat_postMessage(channel: ENV.fetch("SLACK_CHANNEL", "#aoc-dev"), text:)
slack_thread = client.chat_getPermalink(channel: message["channel"], message_ts: message["message"]["ts"])
@snippet.update(slack_url: slack_thread[:permalink])

redirect_to @snippet.slack_url
end

private

def set_snippet
@snippet = current_user.snippets.find(params[:id])
def client
@client ||= Slack::Web::Client.new
end

def post_slack_message
client = Slack::Web::Client.new
puzzle = Puzzle.by_date(Aoc.begin_time.change(day: params[:day]))
username = "<#{helpers.profile_url(current_user.uid)}|#{current_user.username}>"
solution = "<#{helpers.snippet_url(day: @snippet.day, challenge: @snippet.challenge, anchor: @snippet.id)}|solution>"
text = "#{username} submitted a new #{solution} for part #{params[:challenge]} in :#{@snippet.language}-hd:"
text = "#{username} submitted a new #{solution_markdown} for part #{params[:challenge]} in :#{@snippet.language}-hd:"
client.chat_postMessage(channel: ENV.fetch("SLACK_CHANNEL", "#aoc-dev"), text:, thread_ts: puzzle.thread_ts)
end

def set_snippet
@snippet = current_user.snippets.find(params[:id])
end

def solution_markdown
"<#{helpers.snippet_url(day: @snippet.day, challenge: @snippet.challenge, anchor: @snippet.id)}|solution>"
end

def snippet_params
params.require(:snippet).permit(:code, :language)
end
Expand Down
55 changes: 55 additions & 0 deletions app/jobs/update_puzzles_difficulty_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

class UpdatePuzzlesDifficultyJob < ApplicationJob
queue_as :default

def perform
puzzles = Puzzle.where(is_difficulty_final: false)

if puzzles.empty?
Rails.logger.info "✔ No puzzle difficulty to update"
return
end

client = Slack::Web::Client.new
channel = "#aoc-dev"

puzzles.each do |puzzle|
day = puzzle.date.day
url = URI("https://aoc-difficulty.ew.r.appspot.com/score?year=#{puzzle.date.year}&day=#{day}")

Rails.logger.info "\tUpdating difficulty for day #{day}..."
response = Net::HTTP.get_response(url)

if response.code != "200" || response.body.include?("error")
text = "⚠️ <@URZ0F4TEF> Error updating difficulty for day #{day} (#{response.code})"
message = client.chat_postMessage(channel:, text:)
client.chat_postMessage(channel:, thread_ts: message["message"]["ts"], text: "#{url}\n```#{response.body}```")
next
end

data = JSON.parse(response.body)

puzzle.update(
difficulty_part_1: data["1"]["quartile"],
difficulty_part_2: data["2"]["quartile"],
is_difficulty_final: data["1"]["final"] && data["2"]["final"]
)

next unless puzzle.is_difficulty_final

part_1 = Puzzle::DIFFICULTY_LEVELS[puzzle.difficulty_part_1]
part_2 = Puzzle::DIFFICULTY_LEVELS[puzzle.difficulty_part_2]

text = <<~TEXT
Estimated difficulty for day #{day} (experimental feature):
- part 1 is *`#{part_1[:difficulty]}`* #{part_1[:colour]}
- part 2 is *`#{part_2[:difficulty]}`* #{part_2[:colour]}
TEXT

client.chat_postMessage(channel: ENV.fetch("SLACK_CHANNEL", "#aoc-dev"), text:)
end

Rails.logger.info "✔ Puzzle difficulties updated"
end
end
4 changes: 2 additions & 2 deletions app/models/city.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ class City < ApplicationRecord
has_many :city_points, class_name: "Cache::CityPoint", dependent: :delete_all
has_many :city_scores, class_name: "Cache::CityScore", dependent: :delete_all

has_many :users, dependent: :nullify
has_many :original_users, class_name: "User", dependent: :nullify
has_many :users, dependent: :nullify, inverse_of: :city
has_many :original_users, class_name: "User", dependent: :nullify, inverse_of: :original_city
has_many :completions, through: :users

before_create :set_default_vanity_name
Expand Down
10 changes: 10 additions & 0 deletions app/models/puzzle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
class Puzzle < ApplicationRecord
validate :date_during_aoc

DIFFICULTY_LEVELS = {
1 => { difficulty: "EASY", colour: "🟢" },
2 => { difficulty: "HARD", colour: "🟡" },
3 => { difficulty: "VERY HARD", colour: "🟠" },
4 => { difficulty: "HARDCORE", colour: "🔴" },
5 => { difficulty: "IMPOSSIBLE", colour: "⚫" }
}.freeze

DEFAULT_DIFFICULTY = { difficulty: "UNKNOWN", colour: "⚪" }.freeze

def url
Aoc.url(date.day)
end
Expand Down
5 changes: 4 additions & 1 deletion app/models/snippet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ class Snippet < ApplicationRecord

belongs_to :user

has_many :reactions, dependent: :destroy
has_many :reactions, dependent: :destroy, inverse_of: :snippet
Reaction::TYPES.each do |reaction_type|
has_many :"#{reaction_type}_reactions", -> { where(reaction_type: reaction_type) }, class_name: "Reaction", dependent: :destroy, inverse_of: :snippet
end

validates :code, presence: true
validates :language, inclusion: { in: LANGUAGES.keys.map(&:to_s) }
Expand Down
6 changes: 3 additions & 3 deletions app/presenters/stats_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ def set_influencer_achievement

state = :locked
state = :unlocked if referrals_count >= 100
state = :unlocked_plus if user_referrals_count&.> 0
state = :unlocked_plus if referrals_count >= 100 && user_referrals_count.to_i > 0
title = "Influencer\n\nWe have reached 100 referrals 🤝 Actually #{referrals_count} and counting!"
title += " - and you have personally invited #{user_referrals_count} of them, thank you for spreading the love <3" if user_referrals_count&.> 0
title += " - and you have personally invited #{user_referrals_count} of them, thank you for spreading the love <3" if state == :unlocked_plus

{ nature: "influencer", state:, title: }
end
Expand All @@ -90,7 +90,7 @@ def set_belonging_achievement
end

def set_mobster_achievement
biggest_squad_id = Squad.joins(:users).group(:id).order("COUNT(users.id) DESC").first&.id
biggest_squad_id = Squad.joins(:users).group(:id).order("COUNT(users.id) DESC, created_at").first&.id
user_is_in_biggest_squad = @user&.squad_id == biggest_squad_id

state = :locked
Expand Down
5 changes: 5 additions & 0 deletions app/views/days/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
<div class="flex flex-wrap gap-x-6">
<h2 class="strong">Day <%= @day %></h2>

<span class="opacity-70"
title="<%= @difficulty_title %>">
<%= @part_1[:colour] %><%= @part_2[:colour] %>
</span>

<% if @day <= Aoc.latest_day %>
<span>·</span>
<%= link_to "puzzle", Aoc.url(@day), target: :_blank, rel: :noopener, class: "link-explicit link-external" %>
Expand Down
52 changes: 49 additions & 3 deletions config/brakeman.ignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,59 @@
{
"ignored_warnings": [
{
"warning_type": "Redirect",
"warning_code": 18,
"fingerprint": "516250b58f27249a9548ed00f0872fa69ca6e07770f3a972a2c3a475744d12db",
"check_name": "Redirect",
"message": "Possible unprotected redirect",
"file": "app/controllers/snippets_controller.rb",
"line": 64,
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
"code": "redirect_to(Snippet.find(params[:id]).slack_url)",
"render_path": null,
"location": {
"type": "method",
"class": "SnippetsController",
"method": "discuss"
},
"user_input": "Snippet.find(params[:id]).slack_url",
"confidence": "High",
"cwe_id": [
601
],
"note": "Redirecting to a safe Slack URL computed earlier."
},
{
"warning_type": "Redirect",
"warning_code": 18,
"fingerprint": "516250b58f27249a9548ed00f0872fa69ca6e07770f3a972a2c3a475744d12db",
"check_name": "Redirect",
"message": "Possible unprotected redirect",
"file": "app/controllers/snippets_controller.rb",
"line": 71,
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
"code": "redirect_to(Snippet.find(params[:id]).slack_url)",
"render_path": null,
"location": {
"type": "method",
"class": "SnippetsController",
"method": "discuss"
},
"user_input": "Snippet.find(params[:id]).slack_url",
"confidence": "High",
"cwe_id": [
601
],
"note": "Redirecting to a safe Slack URL computed earlier."
},
{
"warning_type": "SQL Injection",
"warning_code": 0,
"fingerprint": "c453ead0c5aa7e164c9fc8d4dfd2014bbeefabd31d4076e37d18d9a76b583668",
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/controllers/users_controller.rb",
"line": 60,
"line": 51,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "User.find_by(params[:attribute].to_sym => params[:identifier])",
"render_path": null,
Expand All @@ -24,6 +70,6 @@
"note": ""
}
],
"updated": "2023-11-12 00:39:59 +0100",
"brakeman_version": "6.0.1"
"updated": "2024-11-26 22:55:47 +0000",
"brakeman_version": "6.2.2"
}
17 changes: 13 additions & 4 deletions config/initializers/good_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,32 @@
shutdown_timeout: 30,
enable_cron: true,
cron: {
insert_new_completions: { # every 10 minutes between November 1st and December 30th
# every 10 minutes between November 1st and December 30th
insert_new_completions: {
cron: "*/10 * 1-30 11-12 *",
class: "InsertNewCompletionsJob"
},
cache_cleanup: { # every puzzle day, 5 minutes before a new puzzle is released
# every puzzle day, 5 minutes before a new puzzle is released
cache_cleanup: {
cron: "55 23 1-25 12 * America/New_York",
class: "Cache::CleanupJob"
},
generate_slack_thread: { # every puzzle day, 1 minutes after a new puzzle is released
# every puzzle day, 1 minutes after a new puzzle is released
generate_slack_thread: {
cron: "1 0 1-25 12 * America/New_York",
class: "GenerateSlackThread",
args: -> { [Time.current] }
},
buddies_generate_daily_pairs: { # every puzzle day, 5 minutes after a new puzzle is released
# every puzzle day, 5 minutes after a new puzzle is released
buddies_generate_daily_pairs: {
cron: "5 0 1-25 12 * America/New_York",
class: "Buddies::GenerateDailyPairsJob",
args: -> { [Aoc.latest_day] }
},
# every puzzle day, every 5 minutes for 3 hours after a new puzzle is released
update_puzzles_difficulty: {
cron: "*/5 0-2 1-25 12 * America/New_York",
class: "UpdatePuzzlesDifficultyJob"
}
}
}
Expand Down
Loading

0 comments on commit caf76c0

Please sign in to comment.