Skip to content

Commit

Permalink
Use slugs for attachments urls, add badge for direct writeup downloads (
Browse files Browse the repository at this point in the history
#2144)

* Slug attachment names

* Regenerate slug on name change

* Add newline

* Don't regenerate slug when name is blank as that messes with the redirects upon update failure

* Simplify update error handling

* Better "Cancel" button

* Fix documentation

* Add badge for direct writeup download

* Disambiguate by using id

* Restore left margin for blue badges

* Fix slugs for new attachments

* Add padding-top to .date p
  • Loading branch information
damianhxy committed Apr 25, 2024
1 parent d1b97b0 commit 586e7e9
Show file tree
Hide file tree
Showing 14 changed files with 200 additions and 26 deletions.
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,6 @@ gem "jwt"
# Avoid "already initialized constant" errors (https://github.com/ruby/net-imap/issues/16)
gem "net-http"
gem 'uri', '0.10.3'

# To generate slugged urls
gem 'friendly_id', '~> 5.5.0'
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ GEM
sassc (>= 2.2)
sassc-rails (>= 2.1)
sprockets-rails (>= 2.1.3)
friendly_id (5.5.1)
activerecord (>= 4.0.0)
globalid (1.2.1)
activesupport (>= 6.1)
hashdiff (1.1.0)
Expand Down Expand Up @@ -491,6 +493,7 @@ DEPENDENCIES
exception_notification (>= 4.1.0)
factory_bot_rails
fomantic-ui-sass (= 2.8.8.1)
friendly_id (~> 5.5.0)
httparty
jbuilder (>= 2.0)
jquery-rails
Expand Down
1 change: 1 addition & 0 deletions app/assets/stylesheets/assessment_date.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
margin: 0;
font-size: 0.85rem;
color: $autolab-gray-text;
padding-top: 4px;
}
6 changes: 0 additions & 6 deletions app/assets/stylesheets/assessments.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
@import 'variables';

.badge {
&.blue {
margin-left: 0 !important;
}
}

.card-title {
margin-bottom: 0 !important;
}
Expand Down
16 changes: 7 additions & 9 deletions app/controllers/attachments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,9 @@ def update
flash[:success] = "Attachment updated"
redirect_to_index
else
error_msg = "Attachment update failed:"
if !@attachment.valid?
@attachment.errors.full_messages.each do |msg|
error_msg += "<br>#{msg}"
end
else
error_msg += "<br>Unknown error"
error_msg = "Attachment update failed."
@attachment.errors.full_messages.each do |msg|
error_msg += "<br>#{msg}"
end
flash[:error] = error_msg
flash[:html_safe] = true
Expand Down Expand Up @@ -131,9 +127,11 @@ def assessment?

def set_attachment
@attachment = if @is_assessment
@course.attachments.find_by(assessment_id: @assessment.id, id: params[:id])
@course.attachments.where(assessment_id: @assessment.id).friendly.find(
params[:id], allow_nil: true
)
else
@course.attachments.find_by(id: params[:id])
@course.attachments.friendly.find(params[:id], allow_nil: true)
end

return unless @attachment.nil?
Expand Down
21 changes: 21 additions & 0 deletions app/models/attachment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
# handout files to students through Autolab.
#
class Attachment < ApplicationRecord
include FriendlyId
friendly_id :slug_candidates, use: :slugged
after_create :initialize_slug
validates :name, presence: true
validates :category_name, presence: true
validates :filename, presence: true
Expand Down Expand Up @@ -44,6 +47,24 @@ def file=(upload)
self.mime_type = upload.content_type
end

# Regenerate slug whenever the name changes
def should_generate_new_friendly_id?
(name_changed? && name.present?) || slug.nil?
end

# https://github.com/norman/friendly_id/issues/1008
def initialize_slug
self.slug = nil
save!
end

def slug_candidates
[
:name,
[:name, :id]
]
end

def after_create
COURSE_LOGGER.log("Created Attachment #{id}:#{filename} (#{mime_type}) as \"#{name}\")")
end
Expand Down
3 changes: 3 additions & 0 deletions app/views/assessments/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@
<span class="new badge blue darken-4" data-badge-caption="unreleased"></span>
<% end %>
<% end %>
<% if asmt.has_writeup? %>
<span class="new badge orange darken-4" data-badge-caption="writeup" data-url="<%= writeup_course_assessment_path(@course, asmt) %>"></span>
<% end %>
<% hoverable = @cud.user.hover_assessment_date ? 'hover' : '' %>
<%= render partial: 'assessment_date', locals: { assessment: asmt, hover: hoverable } %>
<% end %>
Expand Down
6 changes: 5 additions & 1 deletion app/views/attachments/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@
<%= f.datetime_select :release_at, help_text: "Release attachment after this time" %>
</li>
<li>
<%= link_to "Cancel", :back, class: "btn-flat" %>
<% if @is_assessment %>
<%= link_to "Cancel", course_assessment_path(@course, @assessment), class: "btn-flat" %>
<% else %>
<%= link_to "Cancel", course_path(@course), class: "btn-flat" %>
<% end %>
<%= f.submit(@attachment.new_record? ? "Create Attachment" : "Save Changes") %>
</li>
<li style="margin-top: 20px">
Expand Down
107 changes: 107 additions & 0 deletions config/initializers/friendly_id.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# FriendlyId Global Configuration
#
# Use this to set up shared configuration options for your entire application.
# Any of the configuration options shown here can also be applied to single
# models by passing arguments to the `friendly_id` class method or defining
# methods in your model.
#
# To learn more, check out the guide:
#
# http://norman.github.io/friendly_id/file.Guide.html

FriendlyId.defaults do |config|
# ## Reserved Words
#
# Some words could conflict with Rails's routes when used as slugs, or are
# undesirable to allow as slugs. Edit this list as needed for your app.
config.use :reserved

config.reserved_words = %w[new edit index session login logout users admin
stylesheets assets javascripts images]

# This adds an option to treat reserved words as conflicts rather than exceptions.
# When there is no good candidate, a UUID will be appended, matching the existing
# conflict behavior.

# config.treat_reserved_as_conflict = true

# ## Friendly Finders
#
# Uncomment this to use friendly finders in all models. By default, if
# you wish to find a record by its friendly id, you must do:
#
# MyModel.friendly.find('foo')
#
# If you uncomment this, you can do:
#
# MyModel.find('foo')
#
# This is significantly more convenient but may not be appropriate for
# all applications, so you must explicitly opt-in to this behavior. You can
# always also configure it on a per-model basis if you prefer.
#
# Something else to consider is that using the :finders addon boosts
# performance because it will avoid Rails-internal code that makes runtime
# calls to `Module.extend`.
#
# config.use :finders
#
# ## Slugs
#
# Most applications will use the :slugged module everywhere. If you wish
# to do so, uncomment the following line.
#
# config.use :slugged
#
# By default, FriendlyId's :slugged addon expects the slug column to be named
# 'slug', but you can change it if you wish.
#
# config.slug_column = 'slug'
#
# By default, slug has no size limit, but you can change it if you wish.
#
# config.slug_limit = 255
#
# When FriendlyId can not generate a unique ID from your base method, it appends
# a UUID, separated by a single dash. You can configure the character used as the
# separator. If you're upgrading from FriendlyId 4, you may wish to replace this
# with two dashes.
#
# config.sequence_separator = '-'
#
# Note that you must use the :slugged addon **prior** to the line which
# configures the sequence separator, or else FriendlyId will raise an undefined
# method error.
#
# ## Tips and Tricks
#
# ### Controlling when slugs are generated
#
# As of FriendlyId 5.0, new slugs are generated only when the slug field is
# nil, but if you're using a column as your base method can change this
# behavior by overriding the `should_generate_new_friendly_id?` method that
# FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave
# more like 4.0.
# Note: Use(include) Slugged module in the config if using the anonymous module.
# If you have `friendly_id :name, use: slugged` in the model, Slugged module
# is included after the anonymous module defined in the initializer, so it
# overrides the `should_generate_new_friendly_id?` method from the anonymous module.
#
# config.use :slugged
# config.use Module.new {
# def should_generate_new_friendly_id?
# slug.blank? || <your_column_name_here>_changed?
# end
# }
#
# FriendlyId uses Rails's `parameterize` method to generate slugs, but for
# languages that don't use the Roman alphabet, that's not usually sufficient.
# Here we use the Babosa library to transliterate Russian Cyrillic slugs to
# ASCII. If you use this, don't forget to add "babosa" to your Gemfile.
#
# config.use Module.new {
# def normalize_friendly_id(text)
# text.to_slug.normalize! :transliterations => [:russian, :latin]
# end
# }
end
6 changes: 6 additions & 0 deletions db/migrate/20240406174037_add_slug_to_attachments.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddSlugToAttachments < ActiveRecord::Migration[6.1]
def change
add_column :attachments, :slug, :string
add_index :attachments, :slug, unique: true
end
end
21 changes: 21 additions & 0 deletions db/migrate/20240406174050_create_friendly_id_slugs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIGRATION_CLASS =
if ActiveRecord::VERSION::MAJOR >= 5
ActiveRecord::Migration["#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"]
else
ActiveRecord::Migration
end

class CreateFriendlyIdSlugs < MIGRATION_CLASS
def change
create_table :friendly_id_slugs do |t|
t.string :slug, null: false
t.integer :sluggable_id, null: false
t.string :sluggable_type, limit: 50
t.string :scope
t.datetime :created_at
end
add_index :friendly_id_slugs, [:sluggable_type, :sluggable_id]
add_index :friendly_id_slugs, [:slug, :sluggable_type], length: {slug: 140, sluggable_type: 50}
add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], length: {slug: 70, sluggable_type: 50, scope: 70}, unique: true
end
end
15 changes: 14 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_03_16_162826) do
ActiveRecord::Schema.define(version: 2024_04_06_174050) do

create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
Expand Down Expand Up @@ -131,7 +131,9 @@
t.integer "assessment_id"
t.string "category_name", default: "General"
t.datetime "release_at", default: -> { "CURRENT_TIMESTAMP" }
t.string "slug"
t.index ["assessment_id"], name: "index_attachments_on_assessment_id"
t.index ["slug"], name: "index_attachments_on_slug", unique: true
end

create_table "authentications", force: :cascade do |t|
Expand Down Expand Up @@ -192,6 +194,17 @@
t.boolean "infinite", default: false, null: false
end

create_table "friendly_id_slugs", charset: "utf8mb3", force: :cascade do |t|
t.string "slug", null: false
t.integer "sluggable_id", null: false
t.string "sluggable_type", limit: 50
t.string "scope"
t.datetime "created_at"
t.index ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true, length: { slug: 70, scope: 70 }
t.index ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type", length: { slug: 140 }
t.index ["sluggable_type", "sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_type_and_sluggable_id"
end

create_table "github_integrations", force: :cascade do |t|
t.string "oauth_state"
t.text "access_token_ciphertext"
Expand Down
17 changes: 8 additions & 9 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,14 @@ In the `hello` lab, students are asked to write a file called `hello.c`. The aut
**Try submitting to the autograded hello lab**

1. Create and submit a `hello.c` file.

//hello.c
#include <stdio.h>
int main()
{
printf("Hello, World!");
return 0;
}

```c
#include <stdio.h>
int main()
{
printf("Hello, World!");
return 0;
}
```
2. Refresh the submitted entries page to see the autograded score appear
3. Click on a sub score, in this case the `100.0` under the `Correctness` heading, to see the output from the autograder.
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ extra:
markdown_extensions:
- admonition
- pymdownx.superfences
- codehilite
strict: true
plugins:
- search

0 comments on commit 586e7e9

Please sign in to comment.