-
-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add dynamic counts to search filters #5471
Conversation
This reverts commit 84872dc.
(There are still some test and linting issues here.) |
@coderabbitai review |
1 similar comment
@coderabbitai review |
rebase and request review |
WalkthroughThe pull request introduces significant changes across multiple components and models, primarily focusing on enhancing filtering capabilities. Key modifications include the introduction of new filter management properties and methods, the restructuring of existing filters, and updates to localization files. Various components, such as Changes
Possibly related PRs
Suggested labels
Suggested reviewers
Warning Rate limit exceeded@jorg-vr has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 2 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 43
🧹 Outside diff range and nitpick comments (68)
app/views/annotations/question_index.js.erb (1)
2-2
: Consider adding error handling for filter updatesThe filter update operation might fail if the JSON is malformed or if there are network issues. Consider wrapping the update in a try-catch block and showing appropriate error messages to users.
Here's a suggested implementation:
-dodona.search.updateFilters(<%= @filters.to_json.html_safe %>); +try { + dodona.search.updateFilters(<%= @filters.to_json.html_safe %>); +} catch (error) { + dodona.showError('Failed to update search filters. Please refresh the page.'); + console.error('Filter update failed:', error); +}app/views/course_members/index.js.erb (1)
3-3
: Add error handling for filter updates.The filter update operation might fail if the JSON is malformed or if there are network issues. Consider wrapping it in a try-catch block and showing an appropriate error message to users.
-dodona.search.updateFilters(<%= raw @filters.to_json %>); +try { + dodona.search.updateFilters(<%= raw @filters.to_json %>); +} catch (error) { + console.error('Failed to update filters:', error); + dodona.showError('Failed to update search filters. Please refresh the page.'); +}app/views/activities/available.js.erb (1)
3-3
: Add error handling for undefined objectsThe code looks good, but consider adding error handling to prevent potential runtime errors.
-dodona.search.updateFilters(<%= raw @filters.to_json %>); +if (dodona.search && typeof dodona.search.updateFilters === 'function') { + dodona.search.updateFilters(<%= raw @filters.to_json %>); +} else { + console.error('Failed to update filters: dodona.search.updateFilters is not available'); +}app/assets/stylesheets/components/dropdown-filter.css.scss (1)
14-19
: Use Bootstrap SCSS variables for consistent themingThe disabled state styling uses hardcoded color values. Consider using Bootstrap's SCSS variables for better maintainability and consistent theming.
&.disabled { .dropdown-toggle { - background-color: #f8f9fa; - color: #6c757d; + background-color: $gray-100; + color: $gray-600; } }app/views/evaluations/add_users.js.erb (1)
4-4
: Add error handling for nil filters.The code assumes
@filters
is always present and valid. Consider adding a nil check to prevent potential JavaScript errors.Apply this diff to add error handling:
-dodona.search.updateFilters(<%= raw @filters.to_json %>); +<% if @filters %> + dodona.search.updateFilters(<%= json_escape(@filters.to_json) %>); +<% else %> + console.warn('No filters data available'); + dodona.search.updateFilters({}); +<% end %>app/controllers/events_controller.rb (1)
1-11
: Overall implementation feedbackThe changes align well with the PR objectives of adding dynamic counts to search filters. The implementation follows a consistent pattern seen across other controllers.
However, there are some key points to consider:
- Test coverage is missing for the new filtering functionality
- Performance implications need to be monitored, especially given the concerns raised in PR discussions
- The implementation correctly maintains authorization checks with policy_scope
Consider extracting filter-related test helpers into a shared context to make it easier to test similar implementations across controllers.
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 2-2: app/controllers/events_controller.rb#L2
Added line #L2 was not covered by tests
[warning] 4-4: app/controllers/events_controller.rb#L4
Added line #L4 was not covered by tests
[warning] 8-8: app/controllers/events_controller.rb#L8
Added line #L8 was not covered by testsapp/views/saved_annotations/new.html.erb (1)
Line range hint
8-12
: Consider documenting the dynamic filter behaviorThe searchbar now handles filters internally as part of the broader refactor. Consider adding a comment explaining how the dynamic counts feature works in this context, particularly for future maintainers.
Add a comment above the searchbar render:
+<%# Searchbar handles filters internally and updates counts dynamically %> <%= render partial: 'layouts/searchbar', locals: { refresh_element: "#annotation-container" } %>
app/views/activities/index.html.erb (1)
9-9
: Consider performance optimization strategies.Based on the PR objectives, this page could see increased query times up to 300ms due to the dynamic count queries. Consider implementing:
- Caching strategies for filter counts
- Lazy loading of counts
- Batch loading of counts
Would you like assistance in implementing any of these optimization strategies?
app/assets/javascripts/components/search/filter_collection.ts (2)
6-10
: Add JSDoc comments to document the class and its propertiesWhile the code is well-structured, adding documentation would improve maintainability and help other developers understand the purpose of these properties.
Consider adding documentation like this:
+/** + * Manages a collection of filters for search components + */ export class FilterCollection extends DodonaElement { @property({ type: Array }) + /** Array of filter options available for this collection */ filters: FilterOptions[] = []; @property({ type: Array }) + /** Array of filter parameters to hide from display */ hide: string[] = [];
6-20
: Consider adding loading state handlingGiven the performance concerns mentioned in the PR objectives regarding count queries, it would be beneficial to add loading state handling to provide better user feedback during slower operations.
Consider adding:
- A loading property to track the loading state of filter counts
- Methods to update the loading state
- Integration with any existing loading indicators in the UI
Example addition:
@property({ type: Boolean }) loading = false; updateFilters(newFilters: FilterOptions[]): void { this.loading = true; this.filters = newFilters; // After counts are updated this.loading = false; }app/views/saved_annotations/index.html.erb (1)
14-14
: Document the autoSearch parameterConsider adding a comment explaining the purpose of
autoSearch: false
to improve maintainability, as this parameter's behavior isn't immediately obvious.+ <%# Disable automatic search on page load to prevent unnecessary API calls %> <%= render partial: 'layouts/searchbar', locals: { autoSearch: false } %>
app/views/annotations/question_index.html.erb (1)
29-29
: Consider adding ARIA attributes for dynamic filter countsSince the filters now include dynamic counts, ensure they're accessible to screen readers.
Add appropriate ARIA attributes to the searchbar partial to announce filter count updates:
-<%= render partial: 'layouts/searchbar', locals: { options: options, refresh_element: "#question-container"} %> +<%= render partial: 'layouts/searchbar', locals: { + options: options, + refresh_element: "#question-container", + aria: { + live: "polite", + atomic: true + } +} %>app/policies/annotation_policy.rb (1)
8-8
: Consider adding tests for association queries.To prevent similar association name mismatches in the future, consider adding tests that verify the policy's scope resolution with various user scenarios (regular user, course admin, zeus).
Example test cases to consider:
- User viewing their own submission annotations
- Course admin viewing annotations in their course
- User attempting to view annotations from other courses
test/models/saved_annotation_test.rb (2)
29-31
: LGTM! Consider adding edge case tests.The test coverage for course_id filtering looks good, verifying nil values, matching courses, and non-matching courses. However, consider adding tests for edge cases:
- Invalid course IDs
- Deleted courses
- Multiple courses with the same filter
41-43
: LGTM! Consider reducing test duplication.The test structure is identical to the course_id filtering test. Consider using a shared example or test helper to reduce duplication while maintaining the same test coverage.
Example refactor:
def assert_filtered_by(scope, field, record) filtered = SavedAnnotation.public_send(:"by_#{field}_id", record.id) [ create(:saved_annotation, field => nil), create(:saved_annotation, field => record) ].each { |s| assert_includes filtered, s } assert_not_includes filtered, create(:saved_annotation, field => create(scope)) end test 'filtering by various fields should contain nil values' do user = create :user assert_filtered_by(:course, :course, create(:course)) assert_filtered_by(:exercise, :exercise, create(:exercise)) endapp/assets/javascripts/components/search/standalone-dropdown-filter.ts (2)
Line range hint
6-16
: Update component documentation to reflect new inheritanceThe documentation comment still mentions inheriting from
FilterCollectionElement
, but the class now extendsFilterElement
.Apply this diff to fix the documentation:
/** - * This component inherits from FilterCollectionElement. + * This component inherits from FilterElement. * It represents a dropdown which allows to select one label, with the currently selected label shown as a button * * @element d-standalone-dropdown-filter
26-26
: LGTM! Improved label selection logicThe new selection logic using direct ID comparison is cleaner and more type-safe than the previous string conversion approach using
paramVal
.Consider adding type safety by explicitly typing the
default
property:@property({ type: String }) - default; + default: string | undefined;app/assets/javascripts/components/search/filter_tabs.ts (4)
17-17
: Update JSDoc to reflect new inheritanceThe class now extends
FilterElement
, but the JSDoc comment still mentionsFilterCollectionElement
. Please update the documentation to avoid confusion.-* This component inherits from FilterCollectionElement. +* This component inherits from FilterElement.
Line range hint
47-49
: Enhance accessibility for count badgesWhile the count badges are visually clear, screen readers might benefit from more context.
-<span class="badge rounded-pill colored-secondary" id="${label.id}-count">${label.count}</span> +<span class="badge rounded-pill colored-secondary" + id="${label.id}-count" + aria-label="${label.count} items in ${label.name}"> + ${label.count} +</span>
Line range hint
1-54
: Overall implementation looks solidThe component successfully implements dynamic counts while maintaining good performance and accessibility. The code is clean, well-structured, and follows TypeScript best practices.
Consider implementing a loading state for the count badges if the counts are fetched asynchronously, to prevent layout shifts during updates.
Bootstrap tooltips are not initialized in filter_tabs.ts
Ensure that
initTooltips
is imported and called infilter_tabs.ts
to properly initialize Bootstrap tooltips.
- File: app/assets/javascripts/components/search/filter_tabs.ts
🔗 Analysis chain
Line range hint
43-44
: Verify Bootstrap tooltip initializationThe code uses Bootstrap tooltips with
data-bs-toggle="tooltip"
. Ensure that tooltips are properly initialized, as they typically require manual initialization.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for tooltip initialization code rg -l "tooltip.*initialize|bootstrap.*tooltip" --type tsLength of output: 58
Script:
#!/bin/bash # Search for any Bootstrap tooltip initialization in TypeScript files rg -i "bootstrap\.tooltip\(|\$\(.*\).tooltip\(" --type tsLength of output: 269
Script:
#!/bin/bash # Search for tooltip-related data attributes and associated initialization rg -i "data-bs-toggle\s*=\s*['\"]tooltip['\"]" --type tsLength of output: 1815
Script:
#!/bin/bash # Search for initializing tooltips within DOMContentLoaded or similar event listeners rg -i "addEventListener\(['\"]DOMContentLoaded['\"].*bootstrap\.Tooltip" --type tsLength of output: 88
Script:
#!/bin/bash # Search for any tooltip method invocations in TypeScript files rg -i "\.tooltip\s*\(" --type tsLength of output: 245
Script:
#!/bin/bash # Search for global tooltip initialization calls rg -i "initializeTooltip\(" --type tsLength of output: 39
Script:
#!/bin/bash # Search for tooltip initialization within common initialization functions rg -i "init.*tooltip" --type tsLength of output: 2334
Script:
#!/bin/bash # Check if initTooltips is imported and called in filter_tabs.ts rg -i "initTooltips\s*\(" app/assets/javascripts/components/search/filter_tabs.ts --type tsLength of output: 93
Script:
#!/bin/bash # Check for import statement of initTooltips in filter_tabs.ts rg -i "import\s+{?\s*initTooltips\s*}?\s+from\s+['\"]utilities['\"]" app/assets/javascripts/components/search/filter_tabs.ts --type tsLength of output: 291
Script:
#!/bin/bash # Check for import statement of initTooltips in filter_tabs.ts rg -i "import\s+{\s*initTooltips\s*}\s+from\s+['\"]utilities['\"]" app/assets/javascripts/components/search/filter_tabs.ts --type tsLength of output: 287
Script:
#!/bin/bash # Check if initTooltips is called in filter_tabs.ts rg -i "initTooltips\s*\(" app/assets/javascripts/components/search/filter_tabs.ts --type tsLength of output: 93
app/models/course_membership.rb (2)
15-15
: Good architectural choice!Including the Filterable module aligns well with the goal of centralizing and simplifying filter logic. This promotes better maintainability and reduces code duplication across the application.
Consider documenting the available filter options in the model's documentation to help other developers understand the filtering capabilities.
34-35
: Consider performance optimization strategiesGiven the PR's concerns about count query performance:
- Consider adding database indexes to optimize the join paths:
- course_labels.name
- institutions.id
- Evaluate caching strategies for the count queries, especially for frequently accessed filters
- Monitor query execution plans in production to identify potential bottlenecks
Would you like help designing the appropriate database indexes or implementing a caching strategy?
app/models/question.rb (1)
Line range hint
37-44
: Enhance error handling in reset_in_progress method.While the method includes a basic existence check, consider adding error logging and handling for edge cases:
def self.reset_in_progress(id) - return unless exists?(id) + begin + return unless exists?(id) - question = find(id) - return unless question.question_state == 'in_progress' + question = find(id) + return unless question.question_state == 'in_progress' - question.update(question_state: 'unanswered') + question.update!(question_state: 'unanswered') + rescue ActiveRecord::RecordNotFound => e + Rails.logger.info("Question #{id} was deleted before reset_in_progress could run") + rescue => e + Rails.logger.error("Failed to reset question #{id}: #{e.message}") + raise + end endapp/assets/stylesheets/components/token.css.scss (1)
40-44
: LGTM! Consider adding ARIA attributes for better accessibility.The disabled state implementation follows best practices with appropriate opacity, cursor, and pointer-events. The opacity value of 0.38 aligns well with Material Design standards for disabled states.
Consider adding a note in the code comments to remind developers to include appropriate ARIA attributes (e.g.,
aria-disabled="true"
) in the HTML markup when using this class. This would enhance accessibility for screen readers. Example:// Remember to add aria-disabled="true" when using this class &.disabled { opacity: 0.38; cursor: initial; pointer-events: none; }app/models/activity_read_state.rb (1)
Line range hint
14-45
: Document filtering behavior and performance characteristicsConsider adding documentation to clarify:
- The interaction between FilterableByCourseLabels and existing scopes
- Performance implications of dynamic counts
- Cache invalidation impact on filtered results
Add documentation above the module includes:
+ # Enables dynamic filtering and counting of activities by course labels + # Performance note: Counting results may impact response times for large datasetsapp/controllers/annotations_controller.rb (2)
2-2
: Consider consolidating filtering approaches.The controller is using both
has_scope
andhas_filter
declarations. While this works, having two different filtering approaches might make the code harder to maintain.Consider migrating the remaining
has_scope
declarations tohas_filter
for consistency:- has_scope :by_submission, as: :submission_id - has_scope :by_user, as: :user_id + has_filter :submission_id + has_filter :user_idAlso applies to: 8-9
Line range hint
26-34
: Simplify complex conditional logic.The conditional logic for handling unfiltered questions for course admins could be extracted into a private method for better readability.
Consider refactoring to:
+ def filter_by_admin_courses?(unfiltered) + unfiltered && + current_user&.a_course_admin? && + !ActiveRecord::Type::Boolean.new.deserialize(params[:everything]) + end # In question_index - if @unfiltered && current_user&.a_course_admin? && !ActiveRecord::Type::Boolean.new.deserialize(params[:everything]) + if filter_by_admin_courses?(@unfiltered) @questions = @questions.where( course_id: current_user.administrating_courses.map(&:id) ) endapp/views/evaluations/show.html.erb (1)
41-41
: Document the new search filter behavior.Since this change is part of a larger refactor to add dynamic counts to search filters, consider adding a comment explaining how the search functionality works in this view.
Add a comment above the searchbar render:
+<%# Search functionality with dynamic filter counts - see app/views/layouts/_searchbar for implementation %> <%= render partial: 'layouts/searchbar' %>
app/controllers/feedbacks_controller.rb (1)
3-3
: LGTM! Consider adding documentation.The inclusion of
HasFilter
aligns with the PR objectives to simplify filter definitions. Consider adding a comment explaining the module's purpose and available filter methods for better maintainability.+ # Include HasFilter to enable simplified filter definitions and dynamic counts include HasFilter
app/assets/javascripts/components/search/dropdown_filter.ts (1)
57-63
: Consider i18n and performance optimizationsA few suggestions for improvement:
- The count display could benefit from i18n formatting (e.g., thousands separator)
- For large lists, consider memoizing the sorted results to avoid unnecessary re-sorting on each render
- ${this.filteredLabels.sort((a, b) => b.count - a.count).map(s => html` + ${this.memoizedSortedLabels().map(s => html` <li><span class="dropdown-item-text "> <div class="form-check"> <input class="form-check-input" type="${this.multi?"checkbox":"radio"}" .checked=${this.isSelected(s)} @click="${() => this.toggle(s)}" id="check-${this.param}-${s.id}"> <label class="form-check-label" for="check-${this.param}-${s.id}"> ${s.name} - ${s.count ? html`<span class="text-muted float-end ms-4">${s.count}</span>` : ""} + ${s.count ? html`<span class="text-muted float-end ms-4">${i18n.t('js.numbers.format', { number: s.count })}</span>` : ""} </label> </div> </span></li>Add this method to the class:
private memoizedSortedLabels = memoize(() => { return this.filteredLabels.sort((a, b) => b.count - a.count); });app/views/repositories/show.html.erb (1)
69-69
: Consider adding a loading indicator for filter counts.Since the PR objectives mention a potential ~300ms increase in query time for the activities page due to count queries, consider adding a loading indicator while filter counts are being fetched.
-<%= render(partial: 'layouts/searchbar', locals: {baseUrl: activities_path(repository_id: @repository.id), eager: true, no_filter_for: ['repository_id']}) %> +<%= render(partial: 'layouts/searchbar', locals: { + baseUrl: activities_path(repository_id: @repository.id), + eager: true, + no_filter_for: ['repository_id'], + show_loading_indicator: true +}) %>app/controllers/activity_read_states_controller.rb (1)
96-102
: Consider enhancing filter initialization with error handling and documentation.While the filter initialization logic is well-structured, consider these improvements:
- The filter options query might be expensive based on PR comments about count performance. Consider adding a comment documenting the performance characteristics.
- The method
course_labels_filter_options
could fail if the association is invalid.Add error handling and documentation:
if @course.present? && @user.blank? && current_user&.course_admin?(@course) + # Fetch course label filter options with counts. Note: This may impact performance + # on courses with many activities due to COUNT queries. + filter_options = begin + @read_states.course_labels_filter_options(@course.id) + rescue ActiveRecord::StatementInvalid => e + Rails.logger.error("Failed to fetch course label filter options: #{e.message}") + [] + end + @filters = [{ param: 'course_labels', multi: true, - data: @read_states.course_labels_filter_options(@course.id) + data: filter_options }] endapp/views/series/edit.html.erb (1)
90-90
: Add documentation about the new filter behavior.Consider adding a comment above the searchbar partial to document that filters are now dynamically populated. This will help other developers understand the new behavior.
Add this comment above the searchbar:
+<%# Filters are dynamically populated based on the current filtering state %> <%= render partial: 'layouts/searchbar', locals: {baseUrl: available_activities_series_path(@series), eager: true, local_storage_key: 'seriesActivitySearch'} %>
app/models/annotation.rb (1)
Line range hint
25-61
: Consider caching strategies for expensive filter counts.Given the PR objectives and the concerns about COUNT query performance, consider implementing caching strategies for filter counts, especially for:
- Frequently accessed combinations of filters
- Filters on the submissions association, which was noted as particularly slow
Some suggestions:
- Use counter caches for common filter combinations
- Consider implementing Russian Doll caching for nested filter counts
- Add monitoring for filter query performance in production
- Document any performance limitations or recommendations in the model
Would you like help implementing any of these suggestions?
app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts (1)
60-60
: Consider performance optimization for filter updates.The call to
search.updateFilters
on every render might impact performance, especially given the concerns about count queries mentioned in the PR discussion. Consider:
- Adding a check to prevent unnecessary updates
- Implementing debouncing for the filter updates
Here's a suggested improvement:
render(): TemplateResult { - search.updateFilters(this.filters); + // Only update filters if they've changed + if (!this.arraysEqual(this.lastFilters, this.filters)) { + search.updateFilters(this.filters); + } return this.savedAnnotations.length > 0 ? html`Add this helper method to the class:
private arraysEqual(a: FilterOptions[], b: FilterOptions[]): boolean { return JSON.stringify(a) === JSON.stringify(b); }🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 60-60: app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts#L60
Added line #L60 was not covered by testsapp/controllers/saved_annotations_controller.rb (1)
Line range hint
46-52
: Consider optimizing database queries.The implementation looks good but consider these optimizations:
- The separate queries for submissions, courses, and exercises could be combined
- Consider using a single query with joins instead of multiple pluck operations
Example optimization:
submissions_with_associations = Submission .joins(:course, :exercise) .where(id: @annotations.pluck(:submission_id).uniq) .select('submissions.*, courses.*, activities.*') @courses = submissions_with_associations.map(&:course).uniq @exercises = submissions_with_associations.map(&:exercise).uniqapp/assets/javascripts/search.ts (2)
17-17
: Add documentation for the filterCollections propertyConsider adding JSDoc comments to explain the purpose and usage of this property, especially since it's part of a significant refactor of the filtering system.
+ /** Collection of filter instances used to manage search filter state and UI */ filterCollections: FilterCollection[] = [];
131-138
: Consider implementing filter update optimizationsGiven the performance concerns mentioned in the PR objectives regarding count queries, consider implementing:
- Debouncing for filter updates to prevent excessive re-renders
- Caching of filter states to reduce unnecessary updates
- Batch updates when multiple filters change simultaneously
Example implementation:
private filterUpdateDelayer = createDelayer(); private cachedFilters: Map<string, FilterOptions[]> = new Map(); public updateFilters(filterOptions: FilterOptions[] | undefined): void { const cacheKey = JSON.stringify(filterOptions); if (this.cachedFilters.has(cacheKey)) { return; } this.filterUpdateDelayer(() => { // ... existing implementation ... this.cachedFilters.set(cacheKey, coloredFilterOptions); }, 150); }🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 131-131: app/assets/javascripts/search.ts#L131
Added line #L131 was not covered by tests
[warning] 133-133: app/assets/javascripts/search.ts#L133
Added line #L133 was not covered by tests
[warning] 135-136: app/assets/javascripts/search.ts#L135-L136
Added lines #L135 - L136 were not covered by testsapp/assets/javascripts/state/SavedAnnotations.ts (1)
37-37
: Consider implementing response caching for filter dataGiven the performance concerns mentioned in the PR discussion about count queries, consider these optimizations:
- Implement client-side caching with a TTL for filter data
- Add a mechanism to invalidate filter cache when relevant data changes
- Consider batch loading filters for common parameter combinations
This would help mitigate the performance impact while maintaining the dynamic nature of the filters.
Also applies to: 96-104
test/javascript/components/search/search_field.test.ts (2)
67-88
: Consider enhancing test coverage with diverse filter configurationsThe new filter structure is cleaner and more maintainable. However, consider adding test cases for:
- Filters with
multi: true
- Different color values
- Empty data arrays
Example enhancement:
const filters: FilterOptions[] = [ { param: "foo", data: [ { name: "fool", id: "1" }, { name: "bar", id: "2" }, { name: "baz", id: "3" }, ], multi: false, color: "red", }, { param: "bar", data: [ { name: "food", id: "1" }, { name: "barn", id: "2" }, { name: "baz", id: "3" }, ], multi: false, color: "red", + }, + { + param: "tags", + data: [ + { name: "important", id: "1" }, + { name: "urgent", id: "2" } + ], + multi: true, + color: "blue", + }, + { + param: "empty", + data: [], + multi: false, + color: "gray", } ];
Add tests for dynamic counts and performance
Current test suite lacks coverage for dynamic count updates and performance metrics. Please implement:
- Tests for dynamic count updates as outlined in PR objectives.
- Performance tests to assess:
- Multiple concurrent filter updates
- Handling large data sets
- Adherence to response time thresholds
🔗 Analysis chain
Line range hint
1-143
: Consider adding tests for dynamic counts and performanceWhile the existing tests cover the basic functionality well, consider adding:
- Tests for the dynamic count updates mentioned in PR objectives
- Performance tests to validate the concerns about count queries, especially for:
- Multiple concurrent filter updates
- Large data sets
- Response time thresholds
Let's check if there are any existing performance tests:
Consider implementing performance monitoring in the test suite using browser performance APIs or similar tools to catch potential performance regressions early.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for performance-related tests rg -i "performance|benchmark|timing" --type tsLength of output: 286
app/controllers/evaluations_controller.rb (3)
4-4
: Consider migrating all scopes to the new filter system.While the
has_filter :institution_id
change aligns with the PR objectives, mixinghas_filter
andhas_scope
could lead to maintenance challenges. Consider migrating the remaining scopes to use the new filter system for consistency.The complex conditional in
by_course_labels
scope might be better handled within the filter system, which is designed for such scenarios.Also applies to: 9-17
Line range hint
88-98
: Extract duplicated filter and query logic.The filter initialization and query chain are identical to the edit action. Consider extracting this common logic into a private method to follow the DRY principle:
+ private + + def set_course_membership_filters + @filters = filters(@course.course_memberships) + @filters << { + param: 'course_labels', + multi: true, + data: apply_scopes(@course.course_memberships).course_labels_filter_options + } + end + + def load_course_memberships + @course_memberships = apply_scopes(@course.course_memberships) + .includes(:course_labels, user: [:institution]) + .order(status: :asc) + .order(Arel.sql('users.permission ASC')) + .order(Arel.sql('users.last_name ASC'), Arel.sql('users.first_name ASC')) + .where(status: %i[course_admin student]) + .paginate(page: parse_pagination_param(params[:page]), per_page: 15) + endThen use these methods in both actions:
def edit @should_confirm = params[:confirm].present? @course = @evaluation.series.course - @filters = filters(@course.course_memberships) - @filters << { - param: 'course_labels', - multi: true, - data: apply_scopes(@course.course_memberships).course_labels_filter_options - } - @course_memberships = apply_scopes(@course.course_memberships)... + set_course_membership_filters + load_course_memberships # rest of the method end
Potential N+1 Queries Detected.
The
@course_memberships.each
loop inapp/views/course_members/index.csv.erb
accesses methods likecorrect_exercises
andattempted_exercises
oncm.user
, which may trigger additional database queries. Review and optimize these method calls to prevent N+1 query issues.🔗 Analysis chain
Line range hint
58-67
: Consider optimizing the query chain.While the filter initialization looks good, the subsequent query chain is complex and might benefit from optimization:
- Consider extracting the query chain into a scope or class method for reusability
- Verify if additional eager loading is needed to prevent N+1 queries
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for potential N+1 queries in course memberships rg -A 2 "course_memberships\." app/models/Length of output: 925
Script:
#!/bin/bash # Search for iterations over @course_memberships in controllers and views rg "@course_memberships\.each" app/controllers/ app/views/Length of output: 138
Script:
#!/bin/bash # Search for method calls on cm in the course_memberships view rg "cm\.[a-zA-Z_]+" app/views/course_members/index.csv.erbLength of output: 289
app/controllers/submissions_controller.rb (2)
Line range hint
172-209
: Consider extracting filter logic for better maintainability.The
set_submissions
method handles multiple concerns (filtering, scoping, ordering). Consider extracting the filter initialization and course labels logic into separate methods:def set_submissions @submissions = policy_scope(Submission) if params[:user_id] @user = User.find(params[:user_id]) @submissions = @submissions.of_user(@user) end set_filter_context apply_context_filters initialize_filters @course_membership = CourseMembership.find_by(user: @user, course: @course) if @user.present? && @course.present? apply_most_recent_filter if params[:most_recent_per_user] end +private + +def set_filter_context + @course = Course.find(params[:course_id]) if params[:course_id] + @series = Series.find(params[:series_id]) if params[:series_id] + activity_id = params[:activity_id] || params[:exercise_id] + @activity = Exercise.find(activity_id) if activity_id + @judge = Judge.find(params[:judge_id]) if params[:judge_id] +end + +def apply_context_filters + if @activity + @submissions = @submissions.of_exercise(@activity) + if @course + @submissions = @submissions.in_course(@course) + elsif @series + @submissions = @submissions.in_course(@series.course) + end + elsif @series + @submissions = @submissions.in_series(@series) + elsif @course + @submissions = @submissions.in_course(@course) + elsif @judge + @submissions = @submissions.of_judge(@judge) + end +end + +def initialize_filters + @filters = filters(@submissions) + @submissions = apply_scopes(@submissions) + add_course_labels_filter if @course.present? && @user.blank? && current_user&.course_admin?(@course) +end + +def add_course_labels_filter + @filters << { + param: 'course_labels', + multi: true, + data: @submissions.course_labels_filter_options(@course.id) + } +end + +def apply_most_recent_filter + @submissions = @submissions.most_recent_per_user + + if params[:order_by].present? + @submissions = Submission.where(id: @submissions) + @submissions = apply_scopes(@submissions, { order_by: params[:order_by] }) + else + @submissions = @submissions.reorder(id: :desc) + end +end
200-201
: Document performance considerations for filter initialization.Based on the PR objectives, there were performance concerns with count queries on the submissions table. Consider adding a comment explaining that counts were intentionally removed from the submissions page due to performance impact.
+ # Note: Count queries for filters were removed from submissions page due to + # significant performance impact (tripled response time in testing). @filters = filters(@submissions) @submissions = apply_scopes(@submissions)config/locales/js/en.yml (1)
210-212
: Consider standardizing remaining filter key names.Some keys don't follow the established naming pattern:
course_labels
could be renamed tocourse_label
for consistencystatus
could be renamed tostatus_id
if it references a foreign keyapp/controllers/activities_controller.rb (3)
74-74
: Monitor performance impact of filter count queries.The initialization of
@filters
before pagination is correctly placed. However, based on the PR discussion, these count queries might impact performance, especially on pages with multiple filters.Consider:
- Adding performance monitoring for these filter count queries
- Implementing caching for filter counts if they become a bottleneck
- Adding database indexes to optimize the count queries
Would you like help setting up monitoring or implementing caching for these queries?
Also applies to: 78-81
106-108
: Ensure consistent performance optimization across actions.The filter initialization in the
available
action mirrors theindex
action. Any performance optimizations implemented for the index action should also be applied here for consistency.Consider extracting the filter initialization and performance optimization logic into a shared method to ensure consistent behavior across both actions.
+ private + + def initialize_filters(scope) + @filters = filters(scope) + scope = apply_scopes(scope) + scope + end
Missing
filterable_by
declarations inActivity
model.
- Add
filterable_by :popularity
toapp/models/activity.rb
.- Add
filterable_by :description_languages
toapp/models/activity.rb
.🔗 Analysis chain
Line range hint
5-29
: LGTM! Verify filter behavior with multiple selections.The migration from
has_scope
tohas_filter
looks good and aligns with the PR objectives. The multi-selection capability is properly configured forlabels
anddescription_languages
.Let's verify the filter declarations match their model counterparts:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that the model supports these filterable fields # Expected: Find corresponding filterable_by declarations in the Activity model ast-grep --pattern 'class Activity < ApplicationRecord $$$ filterable_by :programming_language $$$ filterable_by :type $$$ filterable_by :judge_id $$$ filterable_by :labels $$$ filterable_by :repository_id $$$ filterable_by :draft $$$ filterable_by :popularity $$$ filterable_by :description_languages $$$ 'Length of output: 343
Script:
#!/bin/bash # Description: Search for filterable_by declarations in the Activity model # Expected: List all instances of filterable_by in app/models/activity.rb rg 'filterable_by\s+:.*' app/models/activity.rbLength of output: 707
config/locales/js/nl.yml (2)
212-212
: Resolve duplicate translations for typeLines 212 and 220 both translate to "type" which could cause confusion. Consider making these more specific:
type
: "Type"event_type
: "Event type" or "Gebeurtenistype"Also applies to: 220-220
206-218
: Consider standardizing translation verbositySome translations use abbreviated forms while others use full words:
description_languages
: "Talen" (abbreviated)programming_language
: "Programmeertaal" (full word)institution_id
: "Onderwijsinstelling" (full word)Consider standardizing the verbosity level across all translations for better consistency.
app/models/submission.rb (2)
72-75
: Consider caching filter options for better performanceWhile the implementation correctly maps status enums to filter options, the comment suggests performance concerns. Since the status options are static (defined by the enum), consider caching the result of this mapping to avoid regenerating it on each request.
- filter_options_for :status do - # Filter options without count as this is a very expensive operation - statuses.map { |key, _| { id: key.to_s, name: human_enum_name(:status, key) } } - end + filter_options_for :status do + @status_filter_options ||= statuses.map { |key, _| + { id: key.to_s, name: human_enum_name(:status, key) } + end + end
Add necessary indexes to the submissions table to improve query performance.
Ensure that frequently filtered columns in the submissions table are properly indexed to address the performance concerns.
🔗 Analysis chain
Line range hint
72-89
: Consider database optimizations for filter performanceBased on the PR objectives mentioning performance impacts (up to 300ms increase in response time), consider the following optimizations:
- Add composite indexes for frequently filtered columns
- Implement result set pagination
- Consider materialized views for complex aggregations
- Implement request-level caching for filter results
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check existing indexes on the submissions table rg -A 5 "create_table.*submissions" db/schema.rb rg "add_index.*submissions" db/schema.rb # Check if there are other models implementing similar filtering ast-grep --pattern 'include Filterable'Length of output: 937
app/controllers/courses_controller.rb (1)
54-54
: Consider caching filter results for performanceThe PR comments mention performance concerns with count queries. Since
@filters = filters(@courses)
is called before pagination and could be expensive for large datasets, consider caching the results, especially for institution-specific views.Consider implementing caching:
def index # ... @filters = Rails.cache.fetch("course_filters_#{cache_key}", expires_in: 1.hour) do filters(@courses) end # ... endAlso applies to: 68-68
app/assets/javascripts/i18n/translations.json (1)
333-354
: Maintain consistent capitalization in filter labels.Some filter labels have inconsistent capitalization:
- "Type", "Programming Language", "Status" start with capitals
- While "event_type" uses lowercase
Consider standardizing the capitalization across all filter labels for better consistency.
"filter": { "course_id": "Course", "course_labels": "Member labels", "description_languages": "Languages", "draft": "Draft", - "event_type": "Type", + "event_type": "Event Type", "exercise_id": "Exercise", "institution_id": "Institution", "judge_id": "Judge", "labels": "Labels", "popularity": "Popularity", "programming_language": "Programming Language", "question_state": "Status", "repository_id": "Repository", "status": "Status", "type": "Type" }app/controllers/concerns/has_filter.rb (2)
21-21
: Remove unnecessary RuboCop directiveThe
# rubocop:disable Naming/PredicateName
comment is unnecessary here, as the method namehas_filter
does not violate any naming conventions. Removing this directive will clean up the code.Apply this diff to remove the unnecessary directive:
-def has_filter(name, multi: false) # rubocop:disable Naming/PredicateName +def has_filter(name, multi: false)🧰 Tools
🪛 rubocop
[warning] 21-21: Unnecessary disabling of
Naming/PredicateName
.(Lint/RedundantCopDisableDirective)
13-13
: Usepublic_send
instead ofsend
for safer method invocationUsing
public_send
instead ofsend
ensures that only public methods are called, preventing unintended access to private methods. This enhances the security and robustness of the code.Apply this diff to replace
send
withpublic_send
:- data: apply_scopes(target, scope_params).send("#{name}_filter_options"), + data: apply_scopes(target, scope_params).public_send("#{name}_filter_options"),app/models/concerns/filterable_by_course_labels.rb (2)
16-20
: Simplify the scope by removing redundantunscoped.where(id: select(:id))
.The use of
unscoped.where(id: select(:id))
is redundant, as it effectively selects the same records. You can simplify the scope without altering its functionality by removing this portion.Apply this diff to simplify the scope:
-def filterable_by_course_labels(through_user: false) +def filterable_by_course_labels(through_user: false) if through_user has_many :course_memberships, through: :user has_many :course_labels, through: :course_memberships end scope :by_course_labels, lambda { |labels, course_id| - unscoped.where(id: select(:id)) - .joins(:course_memberships) - .where(course_memberships: { id: CourseMembership.where(course_id: course_id).by_course_labels(labels) }) + joins(:course_memberships) + .where(course_memberships: { id: CourseMembership.where(course_id: course_id).by_course_labels(labels) }) }
29-30
: Usereject
instead offilter
for clarity.Since you're excluding options where
option[:name]
is not present, usingreject
withblank?
can improve readability.Apply this diff:
count.map { |key, value| { id: key.to_s, name: key.to_s, count: value } } - .filter { |option| option[:name].present? } + .reject { |option| option[:name].blank? }app/views/layouts/_searchbar.html.erb (1)
15-18
: Verify boolean attributes are correctly interpretedWhen setting attributes like
eager
, which are expected to be boolean, ensure that passingeager="<%= local_assigns.fetch :eager, false %>"
results in the correct behavior. Since HTML attributes are strings, the component should correctly parse the string values"true"
or"false"
to boolean.app/assets/javascripts/components/search/search_token.ts (2)
53-56
: Ensure JSDoc comments forSearchTokens
are up to date with recent changes.The documentation should reflect that
SearchTokens
now extendsFilterCollection
and accurately describe its properties. Keeping comments synchronized with the code improves clarity and helps in future maintenance.Apply this diff to update the comments:
/** * This component represents a list of dismissible tokens. * * @element d-search-tokens * * @prop {FilterOptions[]} filters - the list of filter options for which tokens could be created * @prop {string[]} hide - the list of filter params that should never be shown */
58-68
: Optimize theactiveFilters
getter for improved readability and performance.Currently, the method iterates over
this.visibleFilters
and performs multiple lookups insearchQueryState
. Consider refactoring to reduce redundancy and enhance performance. For example:
- Accumulate the count using a reducer function.
- Cache frequently accessed values if possible.
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 58-60: app/assets/javascripts/components/search/search_token.ts#L58-L60
Added lines #L58 - L60 were not covered by tests
[warning] 63-63: app/assets/javascripts/components/search/search_token.ts#L63
Added line #L63 was not covered by tests
[warning] 67-67: app/assets/javascripts/components/search/search_token.ts#L67
Added line #L67 was not covered by testsapp/models/concerns/filterable.rb (3)
20-20
: Optimize theby_filter
scope for performanceThe
by_filter
scope splits thefilter
string and iteratively builds the query usinginject
. This could lead to performance issues with large datasets. Consider refactoring to use full-text search capabilities or indexing on thesearch
column to improve query performance.
7-10
: Enhance documentation for better maintainabilityThe class and its methods are complex. Adding more detailed documentation and examples for methods like
search_by
andfilterable_by
would improve code readability and maintainability.
51-60
: Avoid potential issues with dynamic pluralizationIn line 53, you're using
column.to_s.pluralize.to_s
, which relies on Rails' inflection to pluralize column names. This might not work as expected for irregular nouns. Consider specifying the exact enumeration name or handling irregular cases explicitly.app/models/activity.rb (1)
92-96
: Useselect
instead offilter
for broader compatibilityWhile
.filter
is an alias for.select
in Ruby 2.6 and later, using.select
improves compatibility with earlier Ruby versions and enhances code readability.Apply this diff to make the change:
filter_options_for :description_languages do [{ id: 'nl', name: I18n.t('js.nl'), count: where(description_nl_present: true).count }, { id: 'en', name: I18n.t('js.en'), count: where(description_en_present: true).count }] - .filter { |lang| lang[:count].positive? } + .select { |lang| lang[:count].positive? } end
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (82)
app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts
(2 hunks)app/assets/javascripts/components/search/dropdown_filter.ts
(4 hunks)app/assets/javascripts/components/search/filter_collection.ts
(1 hunks)app/assets/javascripts/components/search/filter_element.ts
(3 hunks)app/assets/javascripts/components/search/filter_tabs.ts
(2 hunks)app/assets/javascripts/components/search/search_field.ts
(6 hunks)app/assets/javascripts/components/search/search_token.ts
(3 hunks)app/assets/javascripts/components/search/standalone-dropdown-filter.ts
(2 hunks)app/assets/javascripts/i18n/translations.json
(4 hunks)app/assets/javascripts/search.ts
(3 hunks)app/assets/javascripts/state/SavedAnnotations.ts
(4 hunks)app/assets/stylesheets/components/dropdown-filter.css.scss
(1 hunks)app/assets/stylesheets/components/token.css.scss
(1 hunks)app/controllers/activities_controller.rb
(4 hunks)app/controllers/activity_read_states_controller.rb
(2 hunks)app/controllers/annotations_controller.rb
(3 hunks)app/controllers/concerns/has_filter.rb
(1 hunks)app/controllers/course_members_controller.rb
(2 hunks)app/controllers/courses_controller.rb
(5 hunks)app/controllers/evaluations_controller.rb
(4 hunks)app/controllers/events_controller.rb
(1 hunks)app/controllers/feedbacks_controller.rb
(2 hunks)app/controllers/saved_annotations_controller.rb
(3 hunks)app/controllers/series_controller.rb
(2 hunks)app/controllers/submissions_controller.rb
(4 hunks)app/controllers/users_controller.rb
(2 hunks)app/models/activity.rb
(2 hunks)app/models/activity_read_state.rb
(2 hunks)app/models/annotation.rb
(2 hunks)app/models/concerns/filterable.rb
(1 hunks)app/models/concerns/filterable_by_course_labels.rb
(1 hunks)app/models/course.rb
(2 hunks)app/models/course_membership.rb
(2 hunks)app/models/event.rb
(1 hunks)app/models/question.rb
(1 hunks)app/models/saved_annotation.rb
(2 hunks)app/models/submission.rb
(3 hunks)app/models/user.rb
(3 hunks)app/policies/annotation_policy.rb
(1 hunks)app/views/activities/available.js.erb
(1 hunks)app/views/activities/index.html.erb
(1 hunks)app/views/activities/index.js.erb
(1 hunks)app/views/activity_read_states/index.html.erb
(1 hunks)app/views/annotations/question_index.html.erb
(1 hunks)app/views/annotations/question_index.js.erb
(1 hunks)app/views/course_members/index.html.erb
(0 hunks)app/views/course_members/index.js.erb
(1 hunks)app/views/courses/index.html.erb
(1 hunks)app/views/courses/index.js.erb
(1 hunks)app/views/courses/new.html.erb
(1 hunks)app/views/courses/scoresheet.html.erb
(0 hunks)app/views/courses/scoresheet.js.erb
(1 hunks)app/views/evaluations/_add_users.html.erb
(0 hunks)app/views/evaluations/add_users.js.erb
(1 hunks)app/views/evaluations/edit.html.erb
(0 hunks)app/views/evaluations/edit.js.erb
(1 hunks)app/views/evaluations/show.html.erb
(1 hunks)app/views/evaluations/show.js.erb
(1 hunks)app/views/events/index.html.erb
(1 hunks)app/views/events/index.js.erb
(1 hunks)app/views/feedbacks/edit.html.erb
(1 hunks)app/views/feedbacks/edit.js.erb
(1 hunks)app/views/judges/show.html.erb
(1 hunks)app/views/layouts/_searchbar.html.erb
(2 hunks)app/views/repositories/admins.html.erb
(1 hunks)app/views/repositories/show.html.erb
(1 hunks)app/views/saved_annotations/index.html.erb
(1 hunks)app/views/saved_annotations/new.html.erb
(1 hunks)app/views/saved_annotations/new.js.erb
(1 hunks)app/views/series/edit.html.erb
(1 hunks)app/views/series/scoresheet.html.erb
(0 hunks)app/views/submissions/index.html.erb
(1 hunks)app/views/submissions/index.js.erb
(1 hunks)app/views/users/index.html.erb
(1 hunks)app/views/users/index.js.erb
(1 hunks)config/locales/js/en.yml
(1 hunks)config/locales/js/nl.yml
(1 hunks)test/javascript/components/search/dropdown_filter.test.ts
(1 hunks)test/javascript/components/search/search_field.test.ts
(2 hunks)test/javascript/components/search/search_token.test.ts
(1 hunks)test/models/activity_test.rb
(0 hunks)test/models/saved_annotation_test.rb
(2 hunks)
💤 Files with no reviewable changes (6)
- app/views/course_members/index.html.erb
- app/views/courses/scoresheet.html.erb
- app/views/evaluations/_add_users.html.erb
- app/views/evaluations/edit.html.erb
- app/views/series/scoresheet.html.erb
- test/models/activity_test.rb
🧰 Additional context used
🪛 GitHub Check: codecov/patch
app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts
[warning] 9-9: app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts#L9
Added line #L9 was not covered by tests
[warning] 48-50: app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts#L48-L50
Added lines #L48 - L50 were not covered by tests
[warning] 53-53: app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts#L53
Added line #L53 was not covered by tests
[warning] 55-56: app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts#L55-L56
Added lines #L55 - L56 were not covered by tests
[warning] 60-60: app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts#L60
Added line #L60 was not covered by tests
app/assets/javascripts/components/search/dropdown_filter.ts
[warning] 90-90: app/assets/javascripts/components/search/dropdown_filter.ts#L90
Added line #L90 was not covered by tests
app/assets/javascripts/components/search/filter_element.ts
[warning] 55-55: app/assets/javascripts/components/search/filter_element.ts#L55
Added line #L55 was not covered by tests
app/assets/javascripts/components/search/search_token.ts
[warning] 58-60: app/assets/javascripts/components/search/search_token.ts#L58-L60
Added lines #L58 - L60 were not covered by tests
[warning] 63-63: app/assets/javascripts/components/search/search_token.ts#L63
Added line #L63 was not covered by tests
[warning] 67-67: app/assets/javascripts/components/search/search_token.ts#L67
Added line #L67 was not covered by tests
[warning] 70-71: app/assets/javascripts/components/search/search_token.ts#L70-L71
Added lines #L70 - L71 were not covered by tests
[warning] 73-75: app/assets/javascripts/components/search/search_token.ts#L73-L75
Added lines #L73 - L75 were not covered by tests
[warning] 86-86: app/assets/javascripts/components/search/search_token.ts#L86
Added line #L86 was not covered by tests
[warning] 98-98: app/assets/javascripts/components/search/search_token.ts#L98
Added line #L98 was not covered by tests
[warning] 102-102: app/assets/javascripts/components/search/search_token.ts#L102
Added line #L102 was not covered by tests
app/assets/javascripts/search.ts
[warning] 131-131: app/assets/javascripts/search.ts#L131
Added line #L131 was not covered by tests
[warning] 133-133: app/assets/javascripts/search.ts#L133
Added line #L133 was not covered by tests
[warning] 135-136: app/assets/javascripts/search.ts#L135-L136
Added lines #L135 - L136 were not covered by tests
app/assets/javascripts/state/SavedAnnotations.ts
[warning] 48-48: app/assets/javascripts/state/SavedAnnotations.ts#L48
Added line #L48 was not covered by tests
[warning] 96-98: app/assets/javascripts/state/SavedAnnotations.ts#L96-L98
Added lines #L96 - L98 were not covered by tests
[warning] 100-100: app/assets/javascripts/state/SavedAnnotations.ts#L100
Added line #L100 was not covered by tests
[warning] 103-103: app/assets/javascripts/state/SavedAnnotations.ts#L103
Added line #L103 was not covered by tests
app/controllers/events_controller.rb
[warning] 2-2: app/controllers/events_controller.rb#L2
Added line #L2 was not covered by tests
[warning] 4-4: app/controllers/events_controller.rb#L4
Added line #L4 was not covered by tests
[warning] 8-8: app/controllers/events_controller.rb#L8
Added line #L8 was not covered by tests
app/controllers/feedbacks_controller.rb
[warning] 47-47: app/controllers/feedbacks_controller.rb#L47
Added line #L47 was not covered by tests
[warning] 49-50: app/controllers/feedbacks_controller.rb#L49-L50
Added lines #L49 - L50 were not covered by tests
app/models/concerns/filterable.rb
[warning] 72-72: app/models/concerns/filterable.rb#L72
Added line #L72 was not covered by tests
🪛 rubocop
app/controllers/concerns/has_filter.rb
[warning] 21-21: Unnecessary disabling of Naming/PredicateName
.
(Lint/RedundantCopDisableDirective)
🔇 Additional comments (105)
app/views/activities/index.js.erb (1)
1-2
: Verify performance impact of filter updates
Based on the PR objectives, the activities page could experience increased load times of up to 300ms due to additional count queries. Let's verify the performance impact.
app/views/feedbacks/edit.js.erb (1)
2-2
: Verify performance impact of filter updates
Based on the PR objectives, there were concerns about the performance impact of count queries. Let's verify the implementation in this controller.
✅ Verification successful
Filter update implementation is properly optimized
The implementation uses efficient caching mechanisms and optimized queries:
- Filter updates only refresh UI state with JSON data
- Underlying queries leverage counter caches and Rails.cache
- Scoped queries avoid expensive count operations
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for potential performance bottlenecks in the feedback filters implementation
# Check if FeedbacksController implements count optimization or caching
echo "Checking FeedbacksController implementation..."
rg -A 10 "class FeedbacksController" app/controllers/
# Look for any filter-related queries in the controller
echo "Checking filter-related queries..."
rg -l "has_filter|filterable_by" app/controllers/feedbacks_controller.rb
# Check if there are any performance-related comments or TODOs
echo "Checking for performance-related comments..."
rg -i "todo|performance|slow|optimize" app/controllers/feedbacks_controller.rb
Length of output: 1301
Script:
#!/bin/bash
# Description: Further investigate performance optimizations related to filter updates
# Search for caching implementations in the codebase
echo "Searching for caching implementations..."
rg "Rails.cache|cache\.fetch|cache_store" .
# Search for count queries in FeedbacksController and related models
echo "Searching for count queries in FeedbacksController..."
rg "count" app/controllers/feedbacks_controller.rb
echo "Searching for count queries in models..."
rg "count" app/models/
Length of output: 17440
Script:
#!/bin/bash
# Description: Trace the usage of count queries and verify if they are cached
# Find where count queries in FeedbacksController are used
echo "Tracing count queries usage in FeedbacksController..."
rg "count" app/controllers/feedbacks_controller.rb --context 5
# Find where count queries in models are used and if they are cached
echo "Tracing count queries usage in models with caching..."
rg "count" app/models/ | while read -r file; do
echo "In file: $file"
rg "count" "$file" --context 3 | rg -B 2 "Rails.cache"
done
# Search for performance benchmarks or logs related to count queries
echo "Searching for performance benchmarks or logs related to count queries..."
rg -i "benchmark|log|performance" .
Length of output: 120339
Script:
#!/bin/bash
# Description: Identify count queries and verify if they are optimized with caching
# List all files in app/controllers/ and app/models/ that contain the word "count"
echo "Listing files with 'count' queries in app/controllers/ and app/models/..."
files=$(rg -l "count" app/controllers/ app/models/)
# Iterate over each file and search for 'count' queries and associated Rails.cache usage
for file in $files; do
echo "Analyzing file: $file"
# Search for 'count' in the file
count_lines=$(rg "count" "$file")
# Check if 'Rails.cache' is used near 'count' queries
echo "$count_lines" | while IFS= read -r line; do
# Get the line number of the 'count' query
line_number=$(echo "$line" | grep -oP '^\S+:\K\d+')
# Define a range of lines to search around the 'count' query for 'Rails.cache'
start_line=$((line_number - 2))
end_line=$((line_number + 2))
# Search for 'Rails.cache' within the specified range
if rg -N $start_line -N $end_line "Rails.cache" "$file" > /dev/null; then
echo " [OPTIMIZED] 'count' query at line $line_number is optimized with Rails.cache."
else
echo " [NOT OPTIMIZED] 'count' query at line $line_number is NOT optimized with Rails.cache."
fi
done
done
# Search for performance benchmarks or logs related to 'count' queries
echo "Searching for performance benchmarks or logs related to 'count' queries..."
rg -i "benchmark|log|performance" .
Length of output: 221383
app/views/users/index.js.erb (2)
2-2
: Verify performance impact of filter updates
Based on the PR objectives, there were concerns about performance impact from count queries. While the users page wasn't specifically mentioned as problematic (unlike submissions), we should verify the performance implications.
Consider implementing the following optimizations if performance becomes an issue:
- Implement caching for filter counts
- Consider lazy loading of counts
- Add appropriate database indexes for commonly filtered columns
Would you like assistance in implementing any of these optimizations?
2-2
: Verify the security of using raw
with @filters
Using raw
with to_json
could potentially expose the application to XSS attacks if @filters
contains user-provided input. Please ensure that the data is properly sanitized before reaching this point.
app/views/saved_annotations/new.js.erb (1)
1-2
: Verify JavaScript integration and error handling.
The code assumes the existence of dodona.search.updateFilters
method. Let's verify its implementation and error handling.
app/views/activities/available.js.erb (1)
3-3
: Verify the structure of @filters object
Let's verify the structure and content of the @filters
object to ensure it matches the expected format.
app/assets/stylesheets/components/dropdown-filter.css.scss (1)
6-6
: Verify layout impact of min-width setting
The min-width: max-content
ensures filter labels don't wrap, which improves readability. However, this could potentially cause layout issues with very long filter options.
Let's check for potential layout issues with long filter labels:
✅ Verification successful
Verified no layout issues found with the min-width: max-content
setting
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Search for long text content in filter options that might cause layout issues
# Look for long text strings in translation files that might be used as filter labels
rg -g '*.yml' -A 1 'filter.*:' config/locales/
# Look for filter definitions in models and controllers to understand what content might appear
rg -g '*.rb' '(filterable_by|has_filter)' app/
Length of output: 6669
app/controllers/events_controller.rb (3)
2-2
: Add test coverage for HasFilter integration
The HasFilter module integration is a critical change that affects the controller's filtering behavior. However, this change lacks test coverage.
Let's verify existing test coverage:
#!/bin/bash
# Check for existing controller tests
rg -l "describe EventsController" "spec/"
# Check for filter-related tests in other controllers
rg "has_filter" "spec/controllers/"
Would you like me to help generate test cases for the HasFilter integration?
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 2-2: app/controllers/events_controller.rb#L2
Added line #L2 was not covered by tests
8-8
: Review performance impact of filter counts
Based on the PR objectives, filter counts have shown performance impact on some pages. Consider the following:
- The
filters
method will generate count queries for each filter option - These counts are executed on the policy-scoped collection, which is correct for permission handling
Consider these optimizations:
- Cache filter counts if they don't need real-time accuracy
- Add database indexes for commonly filtered columns
- Monitor query performance in production for this endpoint
Let's check for similar implementations and existing optimizations:
#!/bin/bash
# Look for other controllers using filters method
rg "filters\(.*policy_scope" "app/controllers/"
# Check for existing caching implementations
rg "Rails.cache.*filters" "app/controllers/"
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 8-8: app/controllers/events_controller.rb#L8
Added line #L8 was not covered by tests
4-4
: Verify event_type filter implementation and performance
The transition from has_scope
to has_filter
for event_type needs verification:
- Ensure the Event model supports event_type filtering
- Consider performance implications of dynamic counts mentioned in PR objectives
Let's verify the implementation:
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 4-4: app/controllers/events_controller.rb#L4
Added line #L4 was not covered by tests
app/views/evaluations/edit.js.erb (2)
4-4
: Verify proper JSON sanitization
The use of raw
with @filters.to_json
could potentially lead to XSS vulnerabilities if the filter data contains malicious content. Ensure that all user-provided data within @filters
is properly sanitized before JSON conversion.
4-4
: Verify performance impact of filter updates
Based on the PR objectives, some pages experienced significant slowdowns due to the dynamic count queries. We should verify if the evaluations page is affected.
app/views/courses/scoresheet.js.erb (1)
2-2
: Verify the performance impact of filter updates.
Given the PR's context about performance concerns with count queries, we should verify the size and complexity of the filters data being transmitted.
app/views/evaluations/show.js.erb (1)
Line range hint 1-5
: LGTM!
The existing HTML update logic is well-structured and includes proper conditional rendering.
app/views/feedbacks/edit.html.erb (2)
Line range hint 9-13
: Confirm performance optimization for submissions table.
Since this view includes a submissions table and the PR mentioned performance issues with submission counts, we should verify that the necessary optimizations are in place.
9-9
: Verify that the searchbar component can still access submission statuses.
The removal of the statuses
local variable suggests that the searchbar component now handles submission statuses internally. Let's verify this implementation.
app/views/saved_annotations/new.html.erb (1)
8-8
: Verify searchbar functionality after variable removal
The removal of courses
and exercises
variables from the searchbar partial suggests these are now handled internally. Let's verify this change doesn't break existing functionality.
✅ Verification successful
Searchbar functionality verified after variable removal
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check if the searchbar partial has been updated to handle filters internally
# Check for filter-related changes in the searchbar partial
echo "Checking searchbar partial implementation:"
rg -A 10 "def.*searchbar" app/views/layouts/
Length of output: 141
Script:
#!/bin/bash
# Description: Verify if 'courses' and 'exercises' variables are used within the searchbar partial
echo "Checking for usage of 'courses' and 'exercises' in the searchbar partial:"
rg "courses|exercises" app/views/layouts/_searchbar.html.erb
Length of output: 231
Script:
#!/bin/bash
# Description: Search for 'courses' and 'exercises' in controller files
echo "Checking for definitions and usages of 'courses' and 'exercises' in controller files:"
rg "courses|exercises" app/controllers/
Length of output: 13445
app/views/activities/index.html.erb (1)
9-9
: Verify the simplified searchbar implementation.
The searchbar partial now receives fewer parameters, relying on @labels
instead of individual variables. Let's verify this refactoring doesn't miss any required functionality.
✅ Verification successful
Searchbar refactoring verified successfully.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for any remaining references to old parameter names in the searchbar partial
# and verify the structure of @labels in the controller
# Check searchbar partial for any references to old parameters
rg -A 5 "programming_languages|repositories|activity_types|description_languages|judges|popularity|draft" app/views/layouts/_searchbar*
# Check controller for @labels structure
ast-grep --pattern 'def index
$$$
@labels = $_
$$$
end' app/controllers/activities_controller.rb
Length of output: 3923
app/models/event.rb (1)
21-21
: Verify query performance impact of filterable_by.
The configuration looks correct, but given the PR's concerns about COUNT queries performance:
- Ensure that filtering by enum doesn't introduce N+1 queries
- Verify if the counts are cached appropriately
#!/bin/bash
# Description: Look for performance optimizations in the Filterable implementation
# Expected: Find evidence of query optimization or caching
# Search for caching mechanisms
rg -t ruby "cache|index|counter" --glob "app/models/concerns/filterable.rb"
# Look for eager loading or join optimizations
ast-grep --pattern 'joins($_) { $$ }'
# Check if there are other models using filterable_by with enums
rg -t ruby "filterable_by.*is_enum: true"
Consider implementing counter caching if the Event counts are frequently accessed in filters. This could help mitigate the performance impact of COUNT queries.
app/views/users/index.html.erb (1)
13-13
: LGTM! Verify query performance impact.
The removal of institutions: Institution.all
aligns with the PR's goal of dynamic filter counts and simplified filter definitions. However, let's verify that this change doesn't introduce N+1 queries.
Let's check how institutions are loaded in the searchbar partial:
app/assets/javascripts/components/search/filter_collection.ts (2)
1-4
: LGTM: Imports are well-organized and properly scoped
The imports are clean and each imported entity is utilized within the code.
17-19
: LGTM: Clean and efficient implementation of visible filters
The getter provides a clear and efficient way to access filtered results. The implementation correctly uses array methods and maintains good readability.
app/views/saved_annotations/index.html.erb (1)
14-14
: Verify the removal of courses and exercises variables
The removal of courses
and exercises
local variables from the searchbar partial aligns with the PR's objective to refactor search filters. However, we should verify that these variables are now handled by the new dynamic filtering system.
✅ Verification successful
Verified removal of courses
and exercises
variables
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify that courses and exercises are handled by the new filtering system
# and that this change is consistent across other views
# Check if other views still pass these variables to the searchbar
echo "Checking other views using the searchbar partial:"
rg -l "render.*layouts/searchbar.*courses.*exercises" "app/views"
# Check if the new filtering system handles these in the controller
echo "\nChecking for filter definitions in the controller:"
rg "has_filter.*:course|has_filter.*:exercise" "app/controllers/saved_annotations_controller.rb"
# Verify the model has the filterable declarations
echo "\nChecking for filterable declarations in the model:"
rg "filterable_by.*:course|filterable_by.*:exercise" "app/models/saved_annotation.rb"
Length of output: 773
app/views/repositories/admins.html.erb (2)
20-20
: LGTM! Removal of institutions aligns with dynamic filtering approach.
The removal of the institutions
local variable from the searchbar partial is a positive change that aligns with the PR's goal of implementing dynamic filters. This should improve initial page load time by avoiding eager loading of all institutions.
20-20
: Verify compatibility with initAdminsEdit()
Please ensure that the dodona.initAdminsEdit()
function is compatible with the new dynamic filtering system, particularly since the searchbar is initialized with eager: true
.
Also applies to: 28-32
app/models/saved_annotation.rb (2)
28-29
: Review performance implications of new filter declarations
The new filterable_by
declarations with always_match_nil: true
replace the previous scopes while maintaining similar functionality. However, there are a few points to consider:
- Including nil values in filters could impact performance on the submissions page, which was mentioned as a concern in the PR objectives.
- The relationship with Exercise and Course models needs verification.
Let's verify the implementation and its impact:
#!/bin/bash
# Description: Verify model relationships and potential performance impacts
# Check for existing indexes on exercise_id and course_id
rg "add_index.*saved_annotations.*(exercise_id|course_id)" "db/migrate/"
# Verify Exercise and Course model relationships
rg "has_many.*saved_annotations" "app/models/{exercise,course}.rb"
# Look for existing usage patterns that might be affected
rg "by_course|by_exercise" "app/controllers/" "app/views/" "spec/"
Consider adding database indexes if not present:
add_index :saved_annotations, :exercise_id
add_index :saved_annotations, :course_id
16-16
: Verify test coverage for Filterable module integration
The inclusion of the Filterable module aligns well with the PR objectives to enhance filtering capabilities.
Let's verify the test coverage:
app/views/courses/index.html.erb (2)
28-28
: Review the usage of eager parameter
The eager: true
parameter is still present while other eager loading was removed. This might need adjustment to align with the new dynamic filtering system.
28-28
: Verify institutions data loading mechanism
The removal of institutions: Institution.all
suggests that institutions are now loaded dynamically. Let's verify the new data loading mechanism to ensure it's properly implemented.
app/policies/annotation_policy.rb (1)
8-8
: LGTM! Association name fixed.
The change from submission
to submissions
in the query conditions correctly aligns with Rails' convention for has_many/belongs_to associations.
Let's verify the association name in the Annotation model:
app/views/activity_read_states/index.html.erb (1)
37-37
: Ensure alignment with the new dynamic counts feature.
Since this PR introduces dynamic counts for search filters, verify that the searchbar partial is properly integrated with the new filtering system.
#!/bin/bash
# Description: Verify integration with the new filtering system
# Look for filter-related JavaScript and partial implementations
# Check for filter update handling in JavaScript
echo "Checking for filter update handling..."
rg 'updateFilters|initializeFilters' 'app/javascript'
# Look for searchbar partial implementation
echo "Checking searchbar partial implementation..."
fd searchbar.html.erb -t f --exec cat {}
app/assets/javascripts/components/search/filter_tabs.ts (1)
3-3
: LGTM: Import changes align with new architecture
The switch to importing FilterElement
aligns with the PR's objective to simplify the filtering system.
app/models/course_membership.rb (1)
34-35
: Verify query performance and access control
The filter declarations look functionally correct, but there are some considerations:
- Performance: The nested associations (especially for institution_id) could impact query performance when combined with count queries mentioned in the PR objectives.
- Security: Ensure proper access control is in place when filtering by institution_id to prevent unauthorized access across institutions.
Let's verify the query performance and access control:
app/models/question.rb (1)
31-32
: Consider adding an index for question_state to optimize filtering.
The implementation correctly defines the question states and enables filtering. However, given the concerns about performance mentioned in the PR objectives, especially regarding COUNT queries, consider adding a database index for the question_state
column to optimize filtering operations.
Let's verify the current schema and existing indexes:
#!/bin/bash
# Description: Check schema and indexes for question_state column
# Check for existing indexes on question_state
rg -A 10 "create_table.*annotations" db/schema.rb
rg "add_index.*annotations.*question_state" db/migrate/
# Check if question_state is used in complex queries that might benefit from an index
rg -A 5 "\.where.*question_state" app/
test/javascript/components/search/search_token.test.ts (2)
7-7
: LGTM! Import path update aligns with the codebase restructuring.
The change from filter_collection_element
to filter_element
matches the PR's objective of simplifying the filter-related components.
7-7
: Verify consistent import path updates across the codebase.
Let's ensure all imports of the Label
class have been updated consistently.
✅ Verification successful
Consistent import path updates verified across the codebase.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for any remaining imports from the old path and verify new imports
# are used consistently across the codebase
echo "Checking for any remaining old imports..."
rg "from ['\"]components/search/filter_collection_element['\"]"
echo "Verifying new imports..."
rg "from ['\"]components/search/filter_element['\"]"
Length of output: 1620
app/models/activity_read_state.rb (2)
14-15
: Verify integration with existing filtering scopes
The inclusion of FilterableByCourseLabels
and Filterable
modules enhances filtering capabilities. However, we should verify that these don't conflict with existing scopes, particularly by_filter
and other filtering methods.
✅ Verification successful
Module inclusions do not conflict with existing filtering scopes
The inclusion of FilterableByCourseLabels
and Filterable
modules does not introduce any conflicts with existing scopes. Filtering behavior remains consistent and unaffected.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for potential method conflicts and usage patterns
# Check for any method conflicts between modules
ast-grep --pattern 'module FilterableByCourseLabels
$$$
def $method($$$) {
$$$
}
$$$
}'
# Look for existing usages of filtering methods
rg -A 3 'by_filter|by_course_labels' --type ruby
Length of output: 428
Script:
#!/bin/bash
# Description: Check for method definitions within FilterableByCourseLabels and Filterable modules to identify potential conflicts
# Search for method definitions in FilterableByCourseLabels
ast-grep --pattern 'module FilterableByCourseLabels {
$$$
def $_($_)
$$$
end
}'
# Search for method definitions in Filterable
ast-grep --pattern 'module Filterable {
$$$
def $_($_)
$$$
end
}'
# Look for existing usages of filtering methods to assess integration
rg 'by_filter|by_course_labels' --type ruby
Length of output: 5328
45-45
: Consider performance implications of dynamic counts
The filterable_by_course_labels through_user: true
declaration adds dynamic counting functionality. Given the PR's concerns about COUNT query performance:
- Consider adding database indexes to optimize the course labels filtering
- Monitor query execution time, especially when filtering across multiple dimensions
- Consider implementing result caching for frequently accessed counts
app/views/submissions/index.html.erb (2)
60-60
: LGTM! Performance optimization implemented as discussed.
The removal of course_labels
and statuses
from the searchbar partial aligns with the decision to exclude count queries on the submissions page, addressing the performance concerns raised in the PR discussion where these queries were causing significant slowdowns.
60-60
: Verify that course and status filters remain functional.
While the course_labels
and statuses
parameters have been removed for performance reasons, we should ensure these filters still work through the new dynamic filtering system.
app/assets/javascripts/components/search/filter_element.ts (4)
5-5
: LGTM: Label type enhancement supports dynamic counts
The addition of the optional count property aligns well with the PR objective of displaying dynamic counts in filters.
6-7
: Optimize color type definition
The current implementation can be simplified by leveraging TypeScript's type inference.
Apply this diff to prevent repeating the color values:
-export const ACCENT_COLORS = ["pink", "deep-purple", "teal", "orange", "brown", "blue-gray", "red", "purple", "indigo"];
-export type AccentColor = typeof ACCENT_COLORS[number];
+export const ACCENT_COLORS = ["pink", "deep-purple", "teal", "orange", "brown", "blue-gray", "red", "purple", "indigo"] as const;
+export type AccentColor = typeof ACCENT_COLORS[number];
Line range hint 26-94
: LGTM: Simplified filter implementation
The refactoring improves code maintainability by:
- Removing the unnecessary paramVal indirection
- Using direct label.id access
- Maintaining clear separation of single/multi select logic
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 55-55: app/assets/javascripts/components/search/filter_element.ts#L55
Added line #L55 was not covered by tests
55-55
: Add test coverage for multiUnSelect method
The multiUnSelect method lacks test coverage, which is crucial for maintaining filter functionality.
Let's verify the current test coverage:
Would you like me to help generate test cases for this method? The tests should cover:
- Removing a selected label
- Handling non-existent labels
- Preserving other selected labels
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 55-55: app/assets/javascripts/components/search/filter_element.ts#L55
Added line #L55 was not covered by tests
app/controllers/annotations_controller.rb (2)
41-41
: Verify necessity of preloaded associations.
While eager loading is good for performance, let's verify if all these associations are necessary for the current view.
✅ Verification successful
All preloaded associations are utilized appropriately in the view templates.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Look for view templates that use these associations
rg -A 5 "question\.(user|last_updated_by|submission)" app/views/
# Check if these associations are used in any partials
fd -e erb . app/views/questions/ -x rg -l "question\.(user|last_updated_by|submission)"
Length of output: 3417
Script:
#!/bin/bash
# Search for usage of the associations in all view templates
rg -A 5 "question\.(user|last_updated_by|submission)" app/views/
# Find all partials that include questions and check for association usage
rg -l "render.*question" app/views/ | xargs -I{} rg "question\.(user|last_updated_by|submission)" {}
Length of output: 3326
36-38
: 🛠️ Refactor suggestion
Review filter ordering and performance impact.
There are a few concerns with the current implementation:
- The
@filters
assignment occurs before applying all filters, which might lead to inaccurate counts. - The question_state filter is applied separately after other scopes, which could impact query performance.
Let's verify the impact on query performance:
Consider refactoring to:
- @filters = filters(@questions)
- @questions = @questions.merge(apply_scopes(Question).all)
- @questions = @questions.where(question_state: params[:question_state]) if params[:question_state]
+ # Apply all filters first
+ @questions = @questions.merge(apply_scopes(Question).all)
+ @questions = @questions.where(question_state: params[:question_state]) if params[:question_state]
+ # Calculate filters after all conditions are applied
+ @filters = filters(@questions)
app/controllers/users_controller.rb (1)
126-127
: Consider performance optimizations for the query chain
The query includes multiple order clauses and pagination which could impact performance, especially with the new filter counts. Consider:
- Verifying that appropriate indexes exist for the ordering columns (permission, last_name, first_name)
- Evaluating if all order clauses are necessary
- Monitoring the query performance with the new filter counts
✅ Verification successful
Query performance is optimized with appropriate indexing
The users
table includes necessary indexes on email
, username
, and token
. No similar complex order chains found in other controllers.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for existing indexes on User model and look for similar patterns
# in other controllers that might benefit from optimization
echo "Checking User model for index definitions:"
rg -A 5 "add_index.*users" "db/migrate/"
echo -e "\nChecking for similar complex order chains in other controllers:"
ast-grep --pattern 'order\(.*:.*:.*\)\.paginate'
Length of output: 1117
Script:
#!/bin/bash
# Description: Check for existing indexes on User model and look for similar complex order chains in other controllers.
echo "Checking User model for index definitions:"
rg -A 5 "add_index.*users" "db/migrate/"
echo -e "\nChecking for similar complex order chains in other controllers:"
ast-grep --pattern 'order\(.*:.*:.*\)\.paginate'
Length of output: 1261
app/controllers/feedbacks_controller.rb (2)
47-48
: LGTM! Query chain looks correct.
The scoping of submissions to the specific series and user is properly implemented.
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 47-47: app/controllers/feedbacks_controller.rb#L47
Added line #L47 was not covered by tests
49-50
: 🛠️ Refactor suggestion
Add test coverage and consider performance implications.
- These lines lack test coverage according to static analysis.
- Given the PR objectives mentioning performance concerns with count queries, we should ensure this implementation is optimized.
#!/bin/bash
# Description: Check for existing filter tests and related performance specs
# Look for existing filter tests
rg -l "describe.*filters" spec/controllers/
# Check for performance-related specs
rg -l "performance|benchmark" spec/
# Look for similar filter implementations in other controllers
ast-grep --pattern 'def edit
$$$
@filters = filters($_)
$$$
end'
Consider adding the following:
- Controller specs to cover the filter implementation
- Performance specs to ensure acceptable query times
- Documentation for the
filters
method usage
Would you like me to help generate the test cases?
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 49-50: app/controllers/feedbacks_controller.rb#L49-L50
Added lines #L49 - L50 were not covered by tests
app/assets/javascripts/components/search/dropdown_filter.ts (3)
3-7
: LGTM! Clean inheritance refactor
The change from FilterCollectionElement
to FilterElement
aligns well with the codebase simplification goals mentioned in the PR objectives.
Also applies to: 23-25
83-98
: Add test coverage for DropdownFilters rendering
The static analysis indicates that the template rendering logic lacks test coverage. Please add tests to verify:
- Rendering with empty filters
- Rendering with visible filters
- Rendering with hidden filters
#!/bin/bash
# Check existing test coverage for DropdownFilters
rg -A 5 "describe.*DropdownFilters" --type ts
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 90-90: app/assets/javascripts/components/search/dropdown_filter.ts#L90
Added line #L90 was not covered by tests
38-40
: Verify disabled state edge cases
The disabled state logic looks good for basic cases, but let's verify it handles all scenarios correctly:
- When labels are empty (
length === 0
) - When there's only one option in single-select mode (
!multi && length === 1
)
app/views/courses/new.html.erb (1)
72-72
: Improved performance by removing eager loading of all institutions.
The removal of institutions: Institution.all
from the searchbar partial is a good optimization, preventing unnecessary loading of all institution records.
app/controllers/activity_read_states_controller.rb (2)
78-78
: LGTM! Code simplification improves readability.
The simplified course assignment removes unnecessary conditional complexity while maintaining the same functionality.
96-102
: Verify query performance impact of filter options.
Based on PR discussions, the COUNT queries for filter options can impact performance. The activities page showed increases of up to 300ms in response time.
Let's analyze the query complexity:
app/views/series/edit.html.erb (1)
90-90
: LGTM! Verify dynamic filter updates.
The simplified searchbar partial call aligns with the PR's objective of implementing dynamic filter counts. The removal of static filter parameters (labels, programming_languages, etc.) suggests these will now be populated dynamically.
Let's verify the dynamic filter updates are working as expected:
test/javascript/components/search/dropdown_filter.test.ts (2)
17-17
: LGTM! Property assignment simplified.
The change from a function-based color property to a direct string value simplifies the code while maintaining the same functionality.
17-17
: Verify the TypeScript interface for color property.
Let's ensure this change is consistent with the component's TypeScript interface definition.
app/models/annotation.rb (2)
60-61
: Verify index coverage for filter queries.
The new filter declarations look good, but since they involve joins and foreign key relationships, ensure optimal performance by verifying index coverage:
course_id
filter should use the index oncourse_id
exercise_id
filter joins throughsubmissions
table and usessubmissions.exercise_id
#!/bin/bash
# Check for proper index definitions
echo "Checking schema for required indexes..."
rg -A 5 "create_table.*annotations" "db/schema.rb"
rg -A 5 "add_index.*annotations" "db/schema.rb"
echo "Checking for existing migrations that add indexes..."
fd -e rb . "db/migrate" --exec rg -l "add_index.*(course_id|exercise_id)"
Consider adding the following to optimize query performance:
- Ensure
course_id
has an index if not already present - Consider adding a composite index if frequently filtering by both course and exercise
25-25
: Verify existing filter functionality and add documentation.
The inclusion of Filterable
is part of the filtering system refactor. Since this model has complex relationships and multiple scopes, ensure that:
- All existing filtering scenarios are preserved
- The performance impact is acceptable, especially for queries involving multiple associations
Consider adding documentation about the new filtering capabilities and any performance considerations, especially for queries involving multiple associations.
app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts (2)
8-9
: LGTM: Imports are appropriate for the new filtering functionality.
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 9-9: app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts#L9
Added line #L9 was not covered by tests
48-57
: 🛠️ Refactor suggestion
Add test coverage for the new filter management code.
The new filter management code follows the established pattern but lacks test coverage. Consider adding tests for:
- Initial state of lastFilters
- Behavior when filters are undefined
- Filter update scenarios
Consider adding type assertions to make the code more robust:
get filters(): FilterOptions[] {
- const filters = savedAnnotationState.getFilters(this.queryParams, this.arrayQueryParams);
+ const filters: FilterOptions[] | undefined = savedAnnotationState.getFilters(
+ this.queryParams,
+ this.arrayQueryParams
+ );
if (filters === undefined) {
return this.lastFilters;
}
this.lastFilters = filters;
return filters;
}
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 48-50: app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts#L48-L50
Added lines #L48 - L50 were not covered by tests
[warning] 53-53: app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts#L53
Added line #L53 was not covered by tests
[warning] 55-56: app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts#L55-L56
Added lines #L55 - L56 were not covered by tests
app/controllers/saved_annotations_controller.rb (3)
3-5
: LGTM: Module inclusion and filter headers setup looks good!
The inclusion of HasFilter
and configuration of filter headers aligns well with the PR's objective of adding dynamic counts to search filters.
8-11
: Consider converting remaining scopes to filters for consistency.
While the conversion of course and exercise filtering to has_filter
looks good, consider converting by_user
and by_filter
scopes as well for consistency across the codebase.
19-21
: Verify performance impact of filter counts.
The implementation looks correct, but given the performance concerns raised in the PR discussion about count queries:
- Consider adding database indexes if not present
- Monitor query execution time in production
- Consider implementing caching for filter counts if needed
app/controllers/course_members_controller.rb (1)
2-2
: LGTM! Verify filter module integration.
The migration from scopes to filters aligns well with the PR objectives. The multi-selection support for course labels is a good enhancement.
Let's verify the HasFilter module integration and its impact:
Also applies to: 8-9
✅ Verification successful
Filter module integration verified successfully.
All relevant changes are properly implemented in app/controllers/course_members_controller.rb
.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify HasFilter module integration and usage patterns
# Check if other controllers are consistently using HasFilter
echo "Checking HasFilter usage patterns across controllers:"
rg -l "include HasFilter" "app/controllers"
# Verify if any old scope-based filtering remains
echo "Checking for remaining scope-based filtering:"
rg "by_institution|by_course_labels" "app/controllers"
Length of output: 2242
app/assets/javascripts/search.ts (1)
5-6
: LGTM: New imports support filter enhancements
The added imports align well with the PR's objective of enhancing search filter functionality.
app/assets/javascripts/state/SavedAnnotations.ts (3)
8-8
: LGTM: Clean integration of filter management
The new import and state property follow the established patterns in the codebase, maintaining type safety and consistency with existing state management approach.
Also applies to: 37-37
48-48
: Add test coverage for filter header parsing
The new filter parsing from response headers lacks test coverage. Since this is crucial for the dynamic filter counts feature, we should ensure it handles various scenarios correctly (valid JSON, missing headers, malformed data).
Would you like help creating test cases for:
- Successful filter parsing
- Missing X-Filters header
- Malformed JSON in X-Filters header
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 48-48: app/assets/javascripts/state/SavedAnnotations.ts#L48
Added line #L48 was not covered by tests
96-104
:
Fix incorrect condition in getFilters method
There's a critical bug in the implementation. The method checks paginationByURL
instead of filtersByURL
in the condition, which could lead to unnecessary API calls.
Apply this fix:
getFilters(params?: Map<string, string>, arrayParams?: Map<string, string[]>): FilterOptions[] {
const url = addParametersToUrl(`${this.url}.json`, params, arrayParams);
delayerByURL.get(url)(() => {
- if (!this.paginationByURL.has(url)) {
+ if (!this.filtersByURL.has(url)) {
this.fetchList(url);
}
}, 200);
return this.filtersByURL.get(url);
}
Additionally, this method needs test coverage to ensure proper functionality.
Run the following to check for similar patterns that might have the same issue:
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 96-98: app/assets/javascripts/state/SavedAnnotations.ts#L96-L98
Added lines #L96 - L98 were not covered by tests
[warning] 100-100: app/assets/javascripts/state/SavedAnnotations.ts#L100
Added line #L100 was not covered by tests
[warning] 103-103: app/assets/javascripts/state/SavedAnnotations.ts#L103
Added line #L103 was not covered by tests
test/javascript/components/search/search_field.test.ts (2)
6-6
: LGTM: Import change aligns with refactor objectives
The change from FilterCollection
to FilterOptions
reflects the codebase simplification mentioned in the PR objectives.
92-92
: LGTM: Template binding correctly updated
The binding syntax for the new filter structure is correct and consistent with Lit's property binding syntax.
app/controllers/evaluations_controller.rb (1)
26-34
: LGTM! Filter initialization and structure look good.
The filter initialization and course labels filter addition are well-structured and maintain proper separation of concerns.
config/locales/js/en.yml (2)
200-203
: LGTM! Well-structured i18n implementation.
The new tokens section follows i18n best practices with proper pluralization support using smart_count
.
206-209
: LGTM! Consistent naming convention for filter keys.
The renaming of filter keys follows a clear pattern:
- Singular form for all keys
- Added
_id
suffix for foreign key references (e.g.,judge_id
,repository_id
)
Also applies to: 214-217
app/controllers/series_controller.rb (2)
195-199
: Verify performance impact of course_labels_filter_options
The filter configuration looks correct, but given the performance concerns mentioned in the PR discussion:
- The
course_labels_filter_options
call might introduce additional queries - This could impact the scoresheet page load time, especially with large datasets
Consider these optimizations if not already implemented:
- Eager loading of course labels
- Caching the filter options if they don't change frequently
- Limiting the number of options if the dataset is large
4-4
: Verify HasFilter module integration with existing scopes
The HasFilter module inclusion aligns with the filtering system refactor. However, let's verify the interaction between HasFilter and existing has_scope declarations.
✅ Verification successful
HasFilter module integration confirmed with existing scopes
The HasFilter module is properly integrated with the has_scope declarations in SeriesController, aligning with the filtering system refactor.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for potential conflicts between HasFilter and has_scope
# Look for other controllers using both HasFilter and has_scope
# Search for controllers using both HasFilter and has_scope
echo "Controllers using both HasFilter and has_scope:"
rg "include HasFilter" -l | xargs rg "has_scope" -l
# Check HasFilter module implementation
echo "\nHasFilter module implementation:"
fd -e rb -E node_modules | xargs rg "module HasFilter" -A 10
Length of output: 339
Script:
#!/bin/bash
# Description: Retrieve the implementation of the HasFilter module
echo "Searching for the HasFilter module implementation..."
fd -e rb -E node_modules | xargs rg "module HasFilter" --context 10
echo "\nChecking for has_scope usage within SeriesController..."
rg "has_scope" app/controllers/series_controller.rb --context 5
echo "\nSearching for any controllers that include both HasFilter and has_scope..."
rg "include HasFilter" -l | xargs -I {} sh -c 'echo "Controller: {}"; rg "has_scope" {} || echo "No has_scope found in {}"'
Length of output: 4884
config/locales/js/nl.yml (2)
200-203
: LGTM: New translations for dynamic filter counts
The translations for active filters count and clear action are well-structured and correctly handle plural forms using the smart_count
variable.
200-220
: Overall translation changes look good
The translation changes successfully support the new dynamic filter counts feature while maintaining good localization practices. The structure is clean and the translations are clear, with only minor consistency improvements suggested above.
app/models/course.rb (2)
29-29
: Verify Filterable module implementation
The inclusion of the Filterable module aligns with the PR's objective to simplify filtering. However, we should verify its implementation.
✅ Verification successful
Further verify Filterable module existence
Filterable module correctly implemented
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the Filterable module implementation
# Expected: Find the module definition and its core filtering methods
# Look for the Filterable module definition
echo "Searching for Filterable module definition..."
rg -p "module(\s+)Filterable" --type ruby
# Look for core filtering methods
echo "Searching for core filtering methods..."
ast-grep --pattern 'module Filterable
$$$
def self.included($_) {
$$$
}
$$$
}'
Length of output: 666
Script:
#!/bin/bash
# Description: Enhanced search for the Filterable module implementation
# Search for the Filterable module definition with a more flexible pattern
echo "Searching for module Filterable definition..."
rg 'module\s+Filterable' --type ruby
# Search for any occurrences of Filterable to identify its usage and potential definitions
echo "Searching for any references to Filterable..."
rg 'Filterable' --type ruby
Length of output: 1259
164-167
: Verify database indexes for search fields
The search and filter functionality relies heavily on database queries. Let's verify the existence of appropriate indexes.
app/models/submission.rb (1)
23-23
: LGTM: Filterable module inclusion aligns with standardization goals
The inclusion of the Filterable module aligns with the PR's objective of standardizing filter implementations across models.
app/controllers/courses_controller.rb (1)
233-237
: Verify course_labels_filter_options implementation
The implementation looks clean, but let's verify the existence and implementation of the course_labels_filter_options
method to ensure it provides the correct data structure for the filter.
✅ Verification successful
course_labels_filter_options implemented correctly
The course_labels_filter_options
method is properly defined in a shared concern and utilized across relevant controllers, ensuring the filter functions as intended.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify course_labels_filter_options implementation
# Test: Look for the method definition and its usage
# Check for method definition
echo "Checking for course_labels_filter_options method definition..."
ast-grep --pattern 'def course_labels_filter_options'
# Check for other usages of the method
echo "Checking for other usages of course_labels_filter_options..."
rg "course_labels_filter_options"
Length of output: 1605
Script:
#!/bin/bash
# Description: Re-verify course_labels_filter_options implementation
# Test: Look for the method definition and its usages comprehensively
# Search for the exact method definition
echo "Searching for exact 'course_labels_filter_options' method definition..."
ast-grep --pattern 'def course_labels_filter_options'
# Search for any method definitions containing 'course_labels_filter'
echo "Searching for method definitions containing 'course_labels_filter'..."
ast-grep --pattern 'def course_labels_filter_'
# Search for usages of methods that include 'course_labels_filter' in their name
echo "Searching for usages of methods related to 'course_labels_filter'..."
rg 'course_labels_filter'
# Check for any potential typos or similar method names
echo "Searching for similar method names that might be misspelled..."
rg 'course_label_filter_options'
Length of output: 1781
app/models/user.rb (4)
121-121
: LGTM: Association follows Rails conventions.
The has_many :course_labels, through: :course_memberships
association is properly defined and aligns with the filtering enhancements described in the PR objectives.
139-139
: Verify the impact of search changes.
The new search_by
implementation with multiple fields replaces the previous set_search
method. While this change simplifies the search functionality, we should verify that all existing search features continue to work as expected.
#!/bin/bash
# Check for existing search usage that might be affected
echo "Checking for previous search method usage..."
rg "set_search"
echo "Checking for current search usage..."
rg "search_by.*:username.*:first_name.*:last_name"
echo "Checking for search-related tests..."
fd ".*_test\.rb" | xargs rg "search"
142-142
: Consider performance implications of dynamic counts.
The new filter implementation with filterable_by :institution_id
and filterable_by_course_labels
aligns with the PR objectives. However, based on the PR comments about performance impact:
- The submissions page experienced significant slowdowns (3x response time)
- The activities page showed increased query time (up to 300ms)
Consider implementing the following optimizations:
- Add database indexes for frequently filtered columns
- Implement caching for filter counts
- Consider batch loading or lazy loading of counts
#!/bin/bash
# Check for existing indexes on filtered columns
echo "Checking for existing indexes in schema..."
rg "add_index.*institution_id"
rg "add_index.*course_.*labels"
# Check for other filter implementations that might provide optimization patterns
echo "Checking for other filterable implementations..."
rg "filterable_by"
Also applies to: 145-145
31-32
: Verify the module dependencies and their order.
The inclusion of both FilterableByCourseLabels
and Filterable
modules might lead to method conflicts. Ensure that FilterableByCourseLabels
is intended to complement rather than replace Filterable
.
✅ Verification successful
No Method Conflicts Detected Between FilterableByCourseLabels
and Filterable
Modules
The modules FilterableByCourseLabels
and Filterable
define distinct methods without overlapping names, ensuring they complement each other without causing any method conflicts.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for potential method conflicts between the modules
echo "Checking FilterableByCourseLabels module..."
rg -A 10 "module FilterableByCourseLabels"
echo "Checking Filterable module..."
rg -A 10 "module Filterable"
Length of output: 3265
app/assets/javascripts/i18n/translations.json (1)
196-196
: LGTM: Search dropdown translations are consistent.
The English ("Search") and Dutch ("Zoeken") translations for the dropdown search functionality are properly implemented.
Also applies to: 691-691
app/views/layouts/_searchbar.html.erb (1)
4-8
: Variables are correctly initialized and combined
The variables no_filter_for
and no_dropdown_for
are appropriately fetched from local_assigns
and combined as intended.
app/assets/javascripts/components/search/search_token.ts (2)
70-77
: Verify that clearAll()
correctly updates the query parameters and triggers updates.
Ensure that directly setting searchQueryState.arrayQueryParams
and searchQueryState.queryParams
correctly updates the application state and UI. If there are specific methods or observers that need to be notified, consider invoking those to maintain consistency.
Would you like to confirm that the clearAll()
method functions as intended across the application?
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 70-71: app/assets/javascripts/components/search/search_token.ts#L70-L71
Added lines #L70 - L71 were not covered by tests
[warning] 73-75: app/assets/javascripts/components/search/search_token.ts#L73-L75
Added lines #L73 - L75 were not covered by tests
18-24
: 🛠️ Refactor suggestion
Update the JSDoc comments to reflect the current class inheritance and properties.
The JSDoc comments still mention that SearchToken
inherits from FilterCollectionElement
, but it now extends FilterElement
. Additionally, properties like paramVal
may no longer be applicable due to the recent changes. Updating the documentation will enhance maintainability and aid other developers in understanding the current code structure.
Apply this diff to update the comments:
/**
- * This component inherits from FilterCollectionElement.
+ * This component inherits from FilterElement.
* It represents a list of dismissible tokens, one for each active label
*
* @element d-search-token
*
* @prop {AccentColor} color - the color associated with the filter
* @prop {string} param - the searchQuery param to be used for this filter
* @prop {boolean} multi - whether one or more labels can be selected at the same time
- * @prop {(l: Label) => string} paramVal - a function that extracts the value that should be used in a searchQuery for a selected label
* @prop {[Label]} labels - all labels that could potentially be selected
*/
Likely invalid or redundant comment.
app/models/concerns/filterable.rb (3)
15-17
: Ensure set_search
handles nil values appropriately
In the set_search
method, you're concatenating column values, which may be nil
. Ensure that send(column) || ''
correctly handles all possible nil
scenarios to prevent unexpected results in the search
attribute.
85-88
: Handle potential issues with nil
counts and option names
In the construction of result
, ensure that names[key]
and option[:name]
handle cases where the name might be nil
or empty. This prevents potential errors when displaying filter options.
69-69
:
Use of unscoped
may have unintended side effects
Using unscoped
removes all default scopes, which might lead to records being included or excluded unexpectedly, especially if default scopes are used for soft deletes or multi-tenancy. Review the necessity of unscoped
here and consider alternatives.
Run the following script to identify models using default scopes that could be affected:
✅ Verification successful
Unintended Side Effects of unscoped
Not Detected
The use of unscoped
in app/models/concerns/filterable.rb
does not interfere with default scopes related to data inclusion or exclusion.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for models with default scopes that might be impacted by `unscoped`.
# Find models with default scopes
grep -r "default_scope" app/models/*.rb
Length of output: 1964
app/assets/javascripts/components/search/search_field.ts (8)
6-6
: Approved: Updated import statement for FilterElement and Label
The import statement correctly includes FilterElement
and Label
from "components/search/filter_element"
, which are necessary for the updated class definitions.
10-10
: Approved: Importing FilterCollection
Importing FilterCollection
from "components/search/filter_collection"
is appropriate, as it is required for the SearchField
class that now extends FilterCollection
.
54-54
: Approved: Dynamic dropdown header using i18n
The dropdown header now utilizes internationalization by incorporating i18n.t
with the param
property. This enhances the localization support for the dropdown.
59-59
: Approved: Conditional display of label counts
Adding the conditional rendering of label.count
improves the user interface by showing the count of items associated with each label when available.
74-75
: Approved: Addition of filters and hide properties
The new @property {FilterOptions[]} filters
and @property {string[]} hide
properties allow better control over which filters are displayed or hidden, enhancing the configurability of the SearchField
component.
150-150
: Approved: Checking for undefined visibleFilters
The conditional check if (!this.visibleFilters)
ensures that the component handles cases where visibleFilters
might be undefined or null.
166-166
: Approved: Rendering suggestions based on visible filters
Mapping over this.visibleFilters
to render the suggestion fields ensures that only the intended filters are displayed to the user.
79-79
: Verify change of base class to FilterCollection
The SearchField
class now extends FilterCollection
instead of DodonaElement
. This may impact inherited functionality.
Please ensure that all necessary functionalities from DodonaElement
are either not needed or have been appropriately migrated. Run the following script to check for any missing methods or properties:
#!/bin/bash
# Description: Verify that extending FilterCollection provides necessary methods previously inherited from DodonaElement
# Extract methods used in SearchField
rg --no-heading -o 'this\.\w+' app/assets/javascripts/components/search/search_field.ts | sort | uniq > methods_used.txt
# Extract methods defined in FilterCollection
rg --no-heading -o '^\s*\w+\(' app/assets/javascripts/components/search/filter_collection.ts | sed 's/^\s*\(\w\+\)(.*/this.\1/' | sort | uniq > methods_in_filtercollection.txt
# Extract methods defined in DodonaElement
rg --no-heading -o '^\s*\w+\(' app/assets/javascripts/components/dodona_element.ts' | sed 's/^\s*\(\w\+\)(.*/this.\1/' | sort | uniq > methods_in_dodonaelement.txt
# Identify methods that were available in DodonaElement but not in FilterCollection
comm -23 methods_in_dodonaelement.txt methods_in_filtercollection.txt > missing_methods.txt
# Check if any of these methods are used in SearchField
grep -F -f missing_methods.txt methods_used.txt > methods_potentially_missing.txt
echo "Methods potentially missing due to the change of base class:"
cat methods_potentially_missing.txt
app/models/activity.rb (3)
36-36
: Inclusion of the Filterable
module enhances filtering capabilities
Including the Filterable
module allows the Activity
model to leverage shared filtering logic, improving code maintainability and consistency across models.
77-85
: Verify the filterable_by
options for correctness
Please ensure that the options passed to the filterable_by
methods are accurate:
- Confirm that the
associations
option correctly references existing associations in theActivity
model. - Check that the
column
names correspond to the correct database columns. - For
filterable_by :type
, verify that thename_hash
returns the intended human-readable names.
103-112
: Avoid using raw SQL in query construction
Using raw SQL strings can lead to potential SQL injection risks and reduces code maintainability. Consider refactoring the query using ActiveRecord or Arel methods to construct the SQL in a safer and more readable manner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (2)
config/locales/js/nl.yml (1)
204-217
: LGTM with minor casing inconsistency.The filter translations are well-structured and properly renamed to singular form. However, there's an inconsistency in casing that should be fixed.
Fix the casing inconsistency:
- type: Type + type: typeapp/assets/javascripts/i18n/translations.json (1)
834-834
: Maintain consistent capitalization in translations.The Dutch translation for
event_type
uses lowercase "type" while the English version uses uppercase "Type". Consider maintaining consistent capitalization:- "event_type": "type", + "event_type": "Type",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
app/assets/javascripts/i18n/translations.json
(4 hunks)config/locales/js/nl.yml
(1 hunks)
🔇 Additional comments (5)
config/locales/js/nl.yml (2)
200-203
: LGTM! Well-structured translations for filter tokens.
The new translations for active filters count and clear action are properly implemented with correct plural forms and Dutch grammar.
212-212
: Verify potential confusion between similar keys.
The presence of both type
and event_type
with similar translations could lead to confusion. Consider either:
- Using more specific names to differentiate their purposes
- Consolidating them if they serve the same purpose
Let's check if these keys are used distinctly in the codebase:
Also applies to: 219-219
app/assets/javascripts/i18n/translations.json (3)
196-196
: LGTM!
The addition of the search text in the dropdown section is appropriate and aligns with the PR objectives.
333-354
: LGTM! Well-structured search translations.
The search section is well-organized with comprehensive filter types and proper pluralization handling.
333-354
: LGTM! Translations are complete and consistent.
All new translation keys are properly implemented in both English and Dutch with consistent structure and proper pluralization handling.
Also applies to: 828-850
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (1)
app/controllers/courses_controller.rb (1)
232-236
: Consider adding error handling for filter options computationThe course labels filter implementation looks good. However, consider adding error handling for the
course_labels_filter_options
call to gracefully handle any potential issues.Consider adding error handling:
@filters = [{ param: 'course_labels', multi: true, - data: @users.course_labels_filter_options(@course.id) + data: @users.course_labels_filter_options(@course.id) rescue [] }]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (1)
app/controllers/auth/authentication_controller.rb (1)
Line range hint
8-47
: Consider optimizing provider initialization.The current implementation makes multiple individual
find_by
queries. Consider using a single query to fetch all providers and then group them in memory:def sign_in + # Fetch all providers in a single query with required attributes + all_providers = Provider.where( + entity_id: [ + 'https://identity.ugent.be/simplesaml/saml2/idp/metadata.php', + 'urn:mace:kuleuven.be:kulassoc:kuleuven.be', + 'https://idp.uhasselt.be:443/idp/shibboleth' + ].compact + ).or( + Provider.where( + identifier: [ + '792e08fb-2d54-4a8e-af72-202548136ef6', + '695b7ca8-2da8-4545-a2da-42d03784e585', + # ... other identifiers ... + ].compact + ) + ).index_by { |p| [p.entity_id, p.identifier].compact.first } + + # Use the indexed providers - @universities = [ - Provider.find_by(entity_id: 'https://identity.ugent.be/simplesaml/saml2/idp/metadata.php'), - # ... other providers ... - ].compact + @universities = [ + all_providers['https://identity.ugent.be/simplesaml/saml2/idp/metadata.php'], + # ... other providers ... + ].compactThis optimization:
- Reduces the number of database queries
- Improves page load performance
- Maintains the same functionality
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
app/controllers/auth/authentication_controller.rb
(1 hunks)app/models/provider.rb
(1 hunks)
🔇 Additional comments (4)
app/controllers/auth/authentication_controller.rb (2)
Line range hint 49-56
: LGTM! Query structure follows best practices.
The OAuth providers query:
- Uses proper eager loading to avoid N+1 queries
- Has clear, maintainable filtering conditions
- Integrates well with the new dynamic filtering system
4-4
: LGTM! Scope name change improves consistency.
The rename from by_institution
to by_institution_id
better reflects the underlying database column while maintaining backward compatibility through the as:
parameter.
Let's verify the related model defines this scope correctly:
✅ Verification successful
Scope name change is correctly implemented and consistent
The verification confirms:
- Provider model correctly defines the
by_institution_id
scope with proper implementation - Both the model and controller are aligned in using the new scope name
- No traces of the old
by_institution
scope remain
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check if the Provider model defines the new scope name
# Expected: Find the scope definition in the Provider model
# Search for the old and new scope definitions
echo "Checking for scope definitions..."
rg "scope\s+:by_institution" --type ruby
rg "scope\s+:by_institution_id" --type ruby
# Search for related specs to ensure they're updated
echo "Checking for related specs..."
rg "by_institution(_id)?" spec/ --type ruby
Length of output: 744
app/models/provider.rb (2)
38-38
: LGTM! Improved scope naming.
The rename from by_institution
to by_institution_id
better reflects the parameter type and matches the database column name, improving code clarity.
38-38
: Verify callers of the renamed scope.
The scope rename requires updates to all existing callers. Let's verify the impact across the codebase.
This pull request contains a refactor of search filters. It adds dynamic counts of the expected results to all search filters. When already partially filtered, it also limits the other filters to only show relevant options.
The initial goal was to get rid of some old code that used js functions as part of 'filterCollection' objects to extract the color and paramVal from a filter option. This made most related code quite ugly and there was no longer any need for it.
So with cc610d6 and 810fa47 I made filterCollection a pure map of data objects.
With these changes it became a lot simpler to dynamically update filters.
I tried to write generic concerns that automatically define a dynamic option count when defining a scope. This way the end result should be less specific code.
In the most basic case writing a filter for a given field of a model is as simple as writing
filterable_by :field
in the model and writinghas_filter :field
in the controller. In the controller, the@filters
variable can then be set by callingfilters(elements)
on the unfiltered list of elements.@filters
is then used to update the shown filter options and counts.Common cases, such as enums and foreign keys can be filtered using
filterable_by :field, is_enum: true
andfilterable_by :field, model: Model
respectively.Naturally there are always some more complex cases, in which case a custom name_hash function can be written or even a full custom count function. See
activity.rb
for some examples of this.The most complex case were
course_labels
as these required the course as extra argument. For now these are generalized in their own specific reusable function.