Add training certificate endpoint with completion validation - #665
Merged
marceloarocha merged 2 commits intoSep 1, 2026
Conversation
Adds GET /training/<id>/certificate returning the data the frontend needs to render a completion certificate: user name, module title, completion date and lesson count. The endpoint refuses unknown/inactive modules and modules with pending lessons. The completion date comes from the treinamento_usuario record, falling back to the last lesson-finish date for completions recorded before that table was populated. is_training_finished now derives from the shared lesson-completion stats query, so the certificate and the finish flow share one source of truth. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011woQiKv9LnkUj5E6JeahY8
There was a problem hiding this comment.
Review Summary
This PR adds a certificate endpoint with comprehensive test coverage and clean refactoring. However, one critical issue must be fixed before merge:
Critical Issue:
- Missing null check on user query (line 174) can cause
AttributeErrorcrashes
Approved Changes:
- Repository refactoring extracts
get_lesson_completion_statsfor reuse - New
get_trainingandget_training_userfunctions provide clear abstractions - Certificate validation properly enforces completion requirements
- Comprehensive test coverage (5 test cases) covering success, validation, permissions, and legacy compatibility
- Clean separation of concerns between route, service, and repository layers
Once the null check is added, this PR is ready to merge.
You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.
| ) | ||
|
|
||
| # user_context is a JWT stub without the name column | ||
| user = db.session.query(User).filter(User.id == user_context.id).first() |
There was a problem hiding this comment.
🛑 Crash Risk: Query returns None when user doesn't exist, causing AttributeError on line 184 when accessing user.name.
Suggested change
| user = db.session.query(User).filter(User.id == user_context.id).first() | |
| user = db.session.query(User).filter(User.id == user_context.id).first() | |
| if user is None: | |
| raise ValidationError( | |
| "Usuário não encontrado", | |
| "errors.invalidUser", | |
| status.HTTP_400_BAD_REQUEST, | |
| ) |
Publishing new lessons in a module reopens it, but must not revoke certificates already earned. The treinamento_usuario record is now the single proof of completion: the certificate endpoint no longer matches current active-lesson counts (and no longer requires the module to still be active), and the lesson count printed on the certificate is what the user actually finished back then. /training/list gains a certificateAvailable flag derived from the same record so the frontend can keep offering the certificate on reopened modules. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011woQiKv9LnkUj5E6JeahY8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new GET
/training/<id>/certificateendpoint that allows users to retrieve certificate data for completed training modules. The endpoint validates that all active lessons are finished before issuing a certificate and includes fallback logic for legacy completion records.Key Changes
New Repository Functions:
get_training(): Retrieves active training records by IDget_lesson_completion_stats(): Returns total lessons, completed lessons, and last completion timestamp for a userget_training_user(): Retrieves the training completion record for a useris_training_finished()to use the newget_lesson_completion_stats()functionNew Service Function:
get_training_certificate(): Validates training completion and returns certificate data including user name, training title, total lessons, and completion dateREAD_BASIC_FEATUREStreinamento_usuarioentriesNew API Endpoint:
GET /training/<id>/certificate: Returns certificate data for completed trainingsREAD_BASIC_FEATURESpermissionComprehensive Test Coverage:
Implementation Details
The certificate endpoint enforces strict validation: it refuses to issue certificates while any active lesson remains unfinished. This ensures certificates are only issued for truly completed modules. The implementation also handles backward compatibility by falling back to the last lesson completion timestamp for records created before the
treinamento_usuariotable was introduced.https://claude.ai/code/session_011woQiKv9LnkUj5E6JeahY8