Skip to content

Commit

Permalink
Merge branch 'WikiEducationFoundation:master' into improve-sentry-sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
empty-codes authored Feb 7, 2025
2 parents 8bd7644 + 682bb35 commit a151598
Show file tree
Hide file tree
Showing 91 changed files with 1,075 additions and 114 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
/config/newrelic.yml
/config/secrets.yml
/dockerAuth.json
/fixtures
/fixtures/vcr_cassettes/*
!/fixtures/vcr_cassettes/cached/
!/fixtures/vcr_cassettes/cached/*
/node_modules
/js_coverage
/vendor/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const ArticleGraphs = ({ article }) => {
}

let radioInput;
if (articleData?.[0].wp10) {
if (articleData?.length > 0 && articleData?.[0]?.wp10) {
radioInput = (
<div>
<div className="input-row">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ export class ArticleScroll {
// Scrolling Logic, Scrolls to paragraph that contains revision by user
scrollTo(name, scrollBox) {
this.bump = false;
const paragraphs = this.scrollObject[name].filteredParagraphs;
const paragraphs = this.scrollObject[name]?.filteredParagraphs || [];
const length = paragraphs.length;
this.nextPosition = paragraphs[this.scrollObject[name].index].coordinates;
this.nextPosition = paragraphs[this.scrollObject[name]?.index]?.coordinates ?? null;

// Finds closest edit if user switchs to a different editor
if (name !== this.currentName && this.currentName !== null) {
Expand Down
4 changes: 1 addition & 3 deletions app/assets/javascripts/components/common/calendar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,7 @@ const Calendar = createReactClass({
}
}

course.day_exceptions = exceptions.join(',');
course.no_day_exceptions = (compact(exceptions).length === 0);
return this.props.updateCourse(course);
return this.props.updateCourse({ ...course, day_exceptions: exceptions.join(','), no_day_exceptions: (compact(exceptions).length === 0) });
},
selectWeekday(e, weekday) {
let weekdays;
Expand Down
8 changes: 5 additions & 3 deletions app/assets/javascripts/components/common/list.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ const List = ({
const persistAreaElement = persistAreaElements[i];
if (fixHeader) {
const floatingHeaderRow = persistAreaElements[i].getElementsByClassName('floatingHeader')[0];
const style = window.getComputedStyle(persistAreaElement);
const width = style.getPropertyValue('width');
floatingHeaderRow.style.width = width;
if (floatingHeaderRow) {
const style = window.getComputedStyle(persistAreaElement);
const width = style.getPropertyValue('width');
floatingHeaderRow.style.width = width;
}
}
const offset = persistAreaElement.offsetTop;
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/components/overview/tag_list.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const TagList = ({ tags, course }) => {
const comma = (index !== lastIndex) ? ', ' : '';
return <span key={`${tag.tag}${tag.id}`}>{tag.tag}{comma}</span>;
})
: I18n.t('courses.none'));
: <span>{I18n.t('courses.none')}</span>);

return (
<span key="tags_list" className="tags">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PropTypes from 'prop-types';

// Components
import ContentAdded from '@components/students/shared/StudentList/Student/ContentAdded.jsx';
import { setUploadFilters } from '~/app/assets/javascripts/actions/uploads_actions';

export const StudentRevisionRow = ({ course, isOpen, toggleDrawer, student, uploadsLink }) => {
return (
Expand All @@ -18,7 +19,7 @@ export const StudentRevisionRow = ({ course, isOpen, toggleDrawer, student, uplo
<td className="desktop-only-tc">
<Link
to={uploadsLink}
onClick={() => { this.setUploadFilters([{ value: student.username, label: student.username }]); }}
onClick={() => { setUploadFilters([{ value: student.username, label: student.username }]); }}
>
{student.total_uploads || 0}
</Link>
Expand Down
4 changes: 2 additions & 2 deletions app/assets/javascripts/components/timeline/BlockList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { Flipper } from 'react-flip-toolkit';

const BlockList = ({ blocks, moveBlock, week_id, ...props }) => {
const springBlocks = blocks.map((block, i) => {
block.order = i;
return <SpringBlock block={block} i={i} key={block.id} {...props} />;
const updatedBlock = { ...block, order: i };
return <SpringBlock block={updatedBlock} i={i} key={block.id} {...props} />;
});
return (
<Flipper flipKey={blocks.map(block => block.id).join('')} spring="stiff">
Expand Down
4 changes: 4 additions & 0 deletions app/assets/javascripts/reducers/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ const handleErrorNotification = function (data) {
console.log(data); // eslint-disable-line no-console
}

if (typeof notification.message !== 'string') {
notification.message = JSON.stringify(notification.message);
}

return notification;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ const TrainingSlideHandler = () => {
params={routeParams}
onClick={next}
/>
{isShown && <FastTrainingAlert/>}
{isShown && <div><FastTrainingAlert/></div>}
</>
);
} else {
Expand Down
6 changes: 5 additions & 1 deletion app/assets/javascripts/utils/article_viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ export const printArticleViewer = () => {
pageHeader.classList.add('header-print-article-viewer');

pageHeader.appendChild(document.querySelector('.article-viewer-title').cloneNode(true));
pageHeader.appendChild(document.querySelector('.user-legend-wrap').cloneNode(true));

const userLegendWrap = document.querySelector('.user-legend-wrap');
if (userLegendWrap) {
pageHeader.appendChild(userLegendWrap.cloneNode(true));
}

doc.write(pageHeader.outerHTML);
doc.write(document.querySelector('#article-scrollbox-id').innerHTML);
Expand Down
26 changes: 20 additions & 6 deletions app/assets/javascripts/utils/course.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ document.onreadystatechange = () => {
if (e.target.tagName === 'BUTTON') return;

const loc = e.currentTarget.dataset.link;
if (e.metaKey || (window.navigator.userAgentData.platform.toLowerCase().indexOf('win') !== -1 && e.ctrlKey)) {
if (e.metaKey || (window.navigator.userAgentData?.platform?.toLowerCase().includes('win') && e.ctrlKey)) {
window.open(loc, '_blank');
} else {
window.location = loc;
Expand All @@ -18,7 +18,7 @@ document.onreadystatechange = () => {
// Course sorting
// only sort if there are tables to sort
let courseList;
if (document.querySelectorAll('#courses table').length) {
if (isTableValid('#courses')) {
courseList = new List('courses', {
page: 500,
valueNames: [
Expand All @@ -31,7 +31,7 @@ document.onreadystatechange = () => {
// Course Results sorting
// only sort if there are tables to sort
let courseResultList;
if (document.querySelectorAll('#course_results table').length) {
if (isTableValid('#course_results')) {
courseResultList = new List('course_results', {
page: 500,
valueNames: [
Expand All @@ -44,7 +44,7 @@ document.onreadystatechange = () => {
// Campaign sorting
// only sort if there are tables to sort
let campaignList;
if (document.querySelectorAll('#campaigns table').length) {
if (isTableValid('#campaigns')) {
campaignList = new List('campaigns', {
page: 500,
valueNames: [
Expand All @@ -56,7 +56,7 @@ document.onreadystatechange = () => {
// Article sorting
// only sort if there are tables to sort
let articlesList;
if (document.querySelectorAll('#campaign-articles table').length) {
if (isTableValid('#campaign-articles')) {
articlesList = new List('campaign-articles', {
page: 10000,
valueNames: [
Expand All @@ -68,7 +68,7 @@ document.onreadystatechange = () => {
// Student sorting
// only sort if there are tables to sort
let studentsList;
if (document.querySelectorAll('#users table').length) {
if (isTableValid('#users')) {
studentsList = new List('users', {
page: 10000,
valueNames: [
Expand All @@ -77,6 +77,20 @@ document.onreadystatechange = () => {
});
}

function isTableValid(selector) {
const tables = document.querySelectorAll(`${selector} table`);
if (tables.length === 0) return false;

let isValid = false;
tables.forEach((table) => {
const tbody = table.querySelector('tbody');
if (tbody && tbody.children.length > 0) {
isValid = true;
}
});
return isValid;
}

// for use on campaign/programs page
const removeCourseBtn = document.querySelectorAll('.remove-course');
for (let i = 0; i < removeCourseBtn.length; i += 1) {
Expand Down
7 changes: 7 additions & 0 deletions app/assets/stylesheets/survey_modules/survey.styl
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,10 @@
color sprout
font-weight 600
padding 5px

.preview-content
display flex
align-items center
justify-content center
gap 20px

16 changes: 14 additions & 2 deletions app/controllers/personal_data_controller.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
# frozen_string_literal: true

# Allows users to download the personal data bout them stored on the Dashboard
require_dependency "#{Rails.root}/lib/personal_data/personal_data_csv_builder.rb"

# Allows users to download the personal data about them stored on the Dashboard
class PersonalDataController < ApplicationController
before_action :require_signed_in
respond_to :json
respond_to :json, :csv

def show
@user = current_user
end

def personal_data_csv
@user = current_user
csv_data = PersonalData::PersonalDataCsvBuilder.new(@user).generate_csv

send_data csv_data,
type: 'text/csv',
disposition: 'attachment',
filename: "#{@user.username}_personal_data_#{Time.zone.today}.csv"
end
end
10 changes: 10 additions & 0 deletions app/controllers/system_status_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

class SystemStatusController < ApplicationController
def index
system_metrics = SystemMetrics.new

@sidekiq_stats = system_metrics.fetch_sidekiq_stats
@queue_metrics = system_metrics.fetch_queue_management_metrics
end
end
2 changes: 1 addition & 1 deletion app/helpers/surveys_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def survey_notification_id(notification)
def survey_class_for_path(req, path)
current_path_segments = req.path.split('/').reject(&:blank?)
active_path = path.split('/').reject(&:blank?).last
current_path_segments.last == active_path ? 'active' : nil
current_path_segments.include?(active_path) ? 'active' : nil
end

######################
Expand Down
1 change: 1 addition & 0 deletions app/mailers/blocked_user_alert_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def email(alert)
@alert = alert
set_recipients
return if @recipients.empty?
return unless @alert.course # A user who isn't in a course may trigger an alert.
params = { to: @recipients,
subject: @alert.main_subject }
params[:reply_to] = @alert.reply_to unless @alert.reply_to.nil?
Expand Down
2 changes: 1 addition & 1 deletion app/models/training_module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def inflate_content_hash(content)
self.name = content['name'] || content[:name]
self.description = content['description'] || content[:description]
self.estimated_ttc = content['estimated_ttc']
self.translations = content['translations']
self.translations = TrainingBase.format_translation_keys content['translations']
self.settings = content['settings']
self.slide_slugs = content['slides'].pluck('slug')
end
Expand Down
97 changes: 97 additions & 0 deletions app/services/system_metrics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# frozen_string_literal: true

require 'sidekiq/api'

class SystemMetrics
def initialize
# 'very_long_update' queue is excluded as it is intentionally never processed
@queues = YAML.load_file('config/sidekiq.yml')[:queues]
.reject { |queue_name| queue_name == 'very_long_update' }
fetch_sidekiq_stats
end

def fetch_sidekiq_stats
stats = Sidekiq::Stats.new
{
enqueued_jobs: stats.enqueued,
active_jobs: stats.processes_size
}
end

def fetch_queue_management_metrics
queues = []
paused_queues = []
all_operational = true

@queues.each do |queue_name|
queue = Sidekiq::Queue.new(queue_name)
queues << get_queue_data(queue)

if queue.paused?
all_operational = false
paused_queues << queue_name
end
end

{
queues:,
paused_queues:,
all_operational:
}
end

def get_queue_data(queue)
{
name: queue.name,
size: queue.size,
status: get_queue_status(queue.name, queue.latency),
latency: convert_latency(queue.latency)
}
end

LATENCY_THRESHOLDS = {
'default' => 1,
'short_update' => 2.hours,
'medium_update' => 12.hours,
'long_update' => 1.day,
'daily_update' => 1.day,
'constant_update' => 15.minutes
}.freeze

def get_queue_status(queue_name, latency)
threshold = LATENCY_THRESHOLDS[queue_name]
latency < threshold ? 'Normal' : 'Backlogged'
end

def convert_latency(seconds)
case seconds
when 0...60
"#{seconds.to_i} second#{'s' unless seconds == 1}"
when 60...3600
format_time(seconds, 60, 'minute', 'second')
when 3600...86400
format_time(seconds, 3600, 'hour', 'minute')
else
format_time(seconds, 86400, 'day', 'hour')
end
end

def format_time(seconds, unit, main_unit_name, sub_unit_name)
main_unit = (seconds / unit).to_i
remaining_seconds = (seconds % unit).to_i
result = "#{main_unit} #{main_unit_name}#{'s' unless main_unit == 1}"
if remaining_seconds.positive?
sub_unit, sub_unit_name = case main_unit_name
when 'day'
[3600, 'hour']
when 'hour'
[60, 'minute']
else
[1, 'second']
end
sub_unit_value = (remaining_seconds / sub_unit).to_i
result += " #{sub_unit_value} #{sub_unit_name}#{'s' unless sub_unit_value == 1}"
end
result
end
end
Loading

0 comments on commit a151598

Please sign in to comment.