Add German translations for PyLint adapter with basic spec
This commit is contained in:
@ -29,7 +29,10 @@ module SubmissionScoring
|
|||||||
waiting_for_container_time: output[:waiting_for_container_time]
|
waiting_for_container_time: output[:waiting_for_container_time]
|
||||||
)
|
)
|
||||||
|
|
||||||
LinterCheckRun.create_from(testrun, assessment) if file.teacher_defined_linter?
|
if file.teacher_defined_linter?
|
||||||
|
LinterCheckRun.create_from(testrun, assessment)
|
||||||
|
assessment = assessor.translate_linter(assessment)
|
||||||
|
end
|
||||||
|
|
||||||
output.merge!(assessment)
|
output.merge!(assessment)
|
||||||
output.merge!(filename: file.name_with_extension, message: feedback_message(file, output), weight: file.weight)
|
output.merge!(filename: file.name_with_extension, message: feedback_message(file, output), weight: file.weight)
|
||||||
|
217
config/locales/de.linter.yml
Normal file
217
config/locales/de.linter.yml
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
de:
|
||||||
|
linter:
|
||||||
|
# This file is used to translate PyLint results from the original English output to German
|
||||||
|
# The following hierarchy has been implemented:
|
||||||
|
#
|
||||||
|
# 1. severity of the linter check result
|
||||||
|
# a. The `severity_name` translates the severity itself
|
||||||
|
# 2. code of the linter check result
|
||||||
|
# 3. A list of required values for the actual translation
|
||||||
|
# a. example: not used anywhere, just for reference when editing this yml file
|
||||||
|
# b. name: Title of the linter check
|
||||||
|
# c. regex: A regex used to translate dynamic parts with _named_ capture groups
|
||||||
|
# d. replacement: A fix replacement translation which is used instead of the
|
||||||
|
# original English output. It may refer to one of the named capture
|
||||||
|
# groups to include dynamic content from the English original
|
||||||
|
# 4. Optionally a named capture group from the regex
|
||||||
|
# 5. A list of fix translations for _values / matches_ of the named capture group
|
||||||
|
#
|
||||||
|
convention:
|
||||||
|
severity_name: Konvention
|
||||||
|
wrong-import-position:
|
||||||
|
example: Import "from turtle import *" should be placed at the top of the module
|
||||||
|
name: Falsche Import-Position
|
||||||
|
regex: .*"(?<import>.*)".*
|
||||||
|
replacement: Der Import von "%{import}" sollte am Anfang der Datei stehen
|
||||||
|
bad-whitespace:
|
||||||
|
example: No space allowed before bracket
|
||||||
|
name: Inkorrektes Leerzeichen
|
||||||
|
regex: (?<what>Exactly one space required|No space allowed) (?<where>after|before|around) (?<when>.*)
|
||||||
|
replacement: "%{where} %{when} %{what}"
|
||||||
|
what:
|
||||||
|
No space allowed: sollte kein Leerzeichen stehen
|
||||||
|
Exactly one space required: sollte genau ein Leerzeichen stehen
|
||||||
|
where:
|
||||||
|
before: Vor
|
||||||
|
after: Hinter
|
||||||
|
around: Vor und hinter
|
||||||
|
when:
|
||||||
|
':': einem Doppelpunkt
|
||||||
|
assignment: einer Zuweisung
|
||||||
|
comma: einem Komma
|
||||||
|
comparison: einem Vergleich
|
||||||
|
bracket: einer Klammer
|
||||||
|
keyword argument assignment: einer Zuweisung von Schlüsselargumenten
|
||||||
|
multiple-statements:
|
||||||
|
example: More than one statement on a single line
|
||||||
|
name: Mehrere Anweisungen
|
||||||
|
regex: .*
|
||||||
|
replacement: Mehr als eine Anweisung in einer Zeile
|
||||||
|
superfluous-parens:
|
||||||
|
example: Unnecessary parens after 'if' keyword
|
||||||
|
name: Überflüssige Klammer
|
||||||
|
regex: .*'(?<keyword>.*)'.*
|
||||||
|
replacement: Nach dem Schlüsselwort '%{keyword}' ist keine Klammer notwendig
|
||||||
|
error:
|
||||||
|
severity_name: Fehler
|
||||||
|
function-redefined:
|
||||||
|
example: function already defined line 15
|
||||||
|
name: Funktionsdefinition überschrieben
|
||||||
|
regex: .*line (?<line>\d*).*
|
||||||
|
replacement: Eine Funktion mit demselben Namen wurde bereits in Zeile %{line} definiert
|
||||||
|
import-error:
|
||||||
|
example: Unable to import 'turtel'
|
||||||
|
name: Import-Fehler
|
||||||
|
regex: .*'(?<import>.*)'.*
|
||||||
|
replacement: Der Import von '%{import} ist fehlgeschlagen
|
||||||
|
syntax-error:
|
||||||
|
example: EOL while scanning string literal (<unknown>, line 1)
|
||||||
|
name: Syntax-Fehler
|
||||||
|
regex: |
|
||||||
|
(?<what>invalid syntax|EOL while scanning string literal|EOF while scanning triple-quoted string literal|cannot assign to|expected an indented block|Missing parentheses in call to|closing parenthesis|expression cannot contain assignment, perhaps you meant|f-string expression part cannot include a backslash|f-string:|invalid character in identifier|invalid decimal literal|trailing comma not allowed without surrounding parentheses|unexpected EOF while parsing|unexpected character after line continuation character|unexpected indent|unindent does not match any outer indentation level|unmatched) ?(?<what_exactly>function call|literal|operator|set display|empty expression not allowed|single|unmatched)? ?(?:'(?<actual>[^'"]*)'\.*)? ?(?:(?<explanation>Did you mean|does not match opening parenthesis|is not allowed)(?: ')?)?(?:(?<suggestion>.*)(?:\?|'|"))? ?\((?<context>.*), line (?<line>\d*)\).*
|
||||||
|
replacement: "%{what}%{what_exactly}%{actual}%{explanation}%{suggestion}" # unused: context, line
|
||||||
|
what:
|
||||||
|
invalid syntax: Ungültige Syntax
|
||||||
|
EOL while scanning string literal: Ein String wurde nicht geschlossen
|
||||||
|
EOF while scanning triple-quoted string literal: Ein Kommentar mit drei Anführungszeichen wurde nicht geschlossen
|
||||||
|
cannot assign to: Die Zuweisung ist ungültig für
|
||||||
|
expected an indented block: Ein eingerückter Codeblock wurde erwartet
|
||||||
|
Missing parentheses in call to: Die Klammern beim Aufruf von "
|
||||||
|
closing parenthesis: 'Die schließende Klammer '
|
||||||
|
expression cannot contain assignment, perhaps you meant: 'Die Anweisung kann keine Zuweisung enthalten, vielleicht meintest du folgendes: '
|
||||||
|
f-string expression part cannot include a backslash: Ein Platzhalter in einem f-String kann keinen Backslash \ enthalten
|
||||||
|
'f-string:': 'f-String:'
|
||||||
|
invalid character in identifier: Ungültiges Zeichen in Bezeichner
|
||||||
|
invalid decimal literal: Ungültige Zahl # e.g. 100_years
|
||||||
|
trailing comma not allowed without surrounding parentheses: Ein Komma am Ende einer Aufzählung ist ohne umgebende Klammern nicht erlaubt
|
||||||
|
unexpected EOF while parsing: Es wurden weitere Zeichen in dem Quellcode erwartet, diese fehlten jedoch
|
||||||
|
unexpected character after line continuation character: Nach einem Backslash \ außerhalb eines Strings darf in der selben Zeile kein weiteres Zeichen folgen
|
||||||
|
unexpected indent: Ungültige Einrückung
|
||||||
|
unindent does not match any outer indentation level: Die Einrückung passt nicht zu einem vorherigen Teil
|
||||||
|
unmatched: 'Die folgende Klammer scheint zu viel zu sein: '
|
||||||
|
what_exactly:
|
||||||
|
# must start with a space character
|
||||||
|
function call: ' eine Funktion'
|
||||||
|
literal: ' eine Zahl'
|
||||||
|
operator: ' einen Operator'
|
||||||
|
set display: ' ein Set'
|
||||||
|
list display: ' eine Liste'
|
||||||
|
dict display: ' ein Dictionary'
|
||||||
|
f-string expression: ' einem F-String'
|
||||||
|
# the following are in the context of an f-string
|
||||||
|
empty expression not allowed: ' eine leere Anweisung ist nicht erlaubt'
|
||||||
|
single: ' eine einzelne "'
|
||||||
|
unmatched: ' unpassende '
|
||||||
|
explanation:
|
||||||
|
Did you mean: '" fehlen. Vielleicht meintest du folgendes:'
|
||||||
|
does not match opening parenthesis: ' passt nicht zu der öffnenden Klammer '
|
||||||
|
is not allowed: '" ist nicht erlaubt'
|
||||||
|
# additional capture groups that are used without translation:
|
||||||
|
# - actual
|
||||||
|
# - suggestion
|
||||||
|
# - context
|
||||||
|
# - line
|
||||||
|
undefined-variable:
|
||||||
|
example: Undefined variable 'beginn_fill'
|
||||||
|
name: Undefinierter Bezeichner
|
||||||
|
regex: .*'(?<name>.*)'.*
|
||||||
|
replacement: Der Name '%{name}' ist unbekannt
|
||||||
|
used-before-assignment:
|
||||||
|
example: Using variable 'kleidung' before assignment
|
||||||
|
name: Verwendung vor Zuweisung
|
||||||
|
regex: .*'(?<name>.*)'.*
|
||||||
|
replacement: Die Variable '%{name}' wird vor ihrer erstmaligen Zuweisung verwendet
|
||||||
|
return-outside-function:
|
||||||
|
example: Return outside function
|
||||||
|
name: Return außerhalb einer Funktion
|
||||||
|
regex: .*
|
||||||
|
replacement: Ein Return kann nur innerhalb einer Funktion verwendet werden
|
||||||
|
refactor:
|
||||||
|
severity_name: Überarbeitung empfohlen
|
||||||
|
comparison-with-itself:
|
||||||
|
example: Redundant comparison - hauptspeise == hauptspeise
|
||||||
|
name: Vergleich mit sich selbst
|
||||||
|
regex: .* - (?<comparison>.*)
|
||||||
|
replacement: Der Vergleich ist überflüssig - %{comparison}
|
||||||
|
inconsistent-return-statements:
|
||||||
|
example: Either all return statements in a function should return an expression, or none of them should.
|
||||||
|
name: Uneinheitliche Rückgabewerte
|
||||||
|
regex: .*
|
||||||
|
replacement: Entweder sollten alle return Anweisungen in einer Funktion ein Ergebnis zurückgeben oder keine Anweisung sollte einen Rückgabewert haben
|
||||||
|
redefined-argument-from-local:
|
||||||
|
example: Redefining argument with the local name 'Wort'
|
||||||
|
name: Überschreibung eines Arguments
|
||||||
|
regex: .*'(?<name>.*)'.*
|
||||||
|
replacement: Das Argument '%{name}' wird überschrieben
|
||||||
|
warning:
|
||||||
|
severity_name: Warnung
|
||||||
|
bad-indentation:
|
||||||
|
example: Bad indentation. Found 3 spaces, expected 4
|
||||||
|
name: Ungütlige Einrückung
|
||||||
|
regex: .*(?<actual>\d*).*(?<expected>\d*).*
|
||||||
|
replacement: Ungültige Einrückung. Statt '%{actual}' Leerzeichen wurden %{expected} Leerzeichen erwartet
|
||||||
|
duplicate-key:
|
||||||
|
example: Duplicate key 100 in dictionary
|
||||||
|
name: Doppelter Schlüssel
|
||||||
|
regex: Duplicate key (?<key>.*) in dictionary
|
||||||
|
replacement: Der Schlüssel '%{key}' ist im Dictionary doppelt vorhanden
|
||||||
|
duplicate-except:
|
||||||
|
example: Catching previously caught exception type ValueError
|
||||||
|
name: Doppeltes Except
|
||||||
|
regex: Catching previously caught exception type (?<exception>.*)
|
||||||
|
replacement: Die zuvor bereits aufgefangene Exception '%{exception}' wird erneut behandelt
|
||||||
|
mixed-indentation:
|
||||||
|
example: Found indentation with tabs instead of spaces
|
||||||
|
name: Gemischte Einrückung
|
||||||
|
regex: .*
|
||||||
|
replacement: Es wurde eine Einrückung mit Tabs anstelle von Leerzeichen entdeckt
|
||||||
|
pointless-statement:
|
||||||
|
example: Statement seems to have no effect
|
||||||
|
name: sinnlose Anweisung
|
||||||
|
regex: .*
|
||||||
|
replacement: Die Anweisung scheint keine Auswirkungen zu haben
|
||||||
|
pointless-string-statement:
|
||||||
|
example: String statement has no effect
|
||||||
|
name: sinnloser String
|
||||||
|
regex: .*
|
||||||
|
replacement: Ein einzelner String ohne Zuweisung hat keine Auswirkung
|
||||||
|
redefined-builtin:
|
||||||
|
example: Redefining built-in 'print'
|
||||||
|
name: Überschreibung
|
||||||
|
regex: .*'(?<builtin>.*)'.*
|
||||||
|
replacement: Der interne Bezeichner '%{builtin}' wird überschrieben
|
||||||
|
redefined-outer-name:
|
||||||
|
example: Redefining name 'name' from outer scope (line 1)
|
||||||
|
name: Überschreibung eines äußeren Bezeichners
|
||||||
|
regex: .*'(?<name>.*)'.*\(line (?<line>\d*)\).*
|
||||||
|
replacement: Der Bezeichner '%{name}', der bereits in Zeile %{line} definiert wurde, wird überschrieben
|
||||||
|
self-assigning-variable:
|
||||||
|
example: Assigning the same variable 'kleidung' to itself
|
||||||
|
name: Selbstzuweisung
|
||||||
|
regex: .*'(?<name>.*)'.*
|
||||||
|
replacement: Die Variable '%{name}' wird sich selbst zugewiesen
|
||||||
|
unreachable:
|
||||||
|
example: Unreachable code
|
||||||
|
name: Unerreichbar
|
||||||
|
regex: .*
|
||||||
|
replacement: Die Anweisung wird nie ausgeführt werden
|
||||||
|
undefined-loop-variable:
|
||||||
|
example: Using possibly undefined loop variable 'i'
|
||||||
|
name: Unbekannte Schleifenvariable
|
||||||
|
regex: .*'(?<name>.*)'.*
|
||||||
|
replacement: Die Schleifenvariable '%{name}' ist möglicherweise nicht definiert
|
||||||
|
unnecessary-semicolon:
|
||||||
|
example: Unnecessary semicolon
|
||||||
|
name: Unnötiges Semikolon
|
||||||
|
regex: .*
|
||||||
|
replacement: Das Semikolon ist unnötig
|
||||||
|
unused-argument:
|
||||||
|
example: Unused argument 'laenge'
|
||||||
|
name: Unbenutztes Argument
|
||||||
|
regex: .*'(?<name>.*)'.*
|
||||||
|
replacement: Das Argument '%{name}' wird nicht verwendet
|
||||||
|
unused-variable:
|
||||||
|
example: Unused variable 'i'
|
||||||
|
name: Unbenutzte Variable
|
||||||
|
regex: .*'(?<name>.*)'.*
|
||||||
|
replacement: Die Variable '%{name}' wird nicht verwendet
|
@ -27,5 +27,9 @@ class Assessor
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def translate_linter(result)
|
||||||
|
@testing_framework_adapter.translate_linter(result)
|
||||||
|
end
|
||||||
|
|
||||||
class Error < RuntimeError; end
|
class Error < RuntimeError; end
|
||||||
end
|
end
|
||||||
|
@ -39,4 +39,44 @@ class PyLintAdapter < TestingFrameworkAdapter
|
|||||||
concatenated_errors = assertion_error_matches.map { |result| "#{result[:name]}: #{result[:result]}" }.flatten
|
concatenated_errors = assertion_error_matches.map { |result| "#{result[:name]}: #{result[:result]}" }.flatten
|
||||||
{count: count, failed: failed, error_messages: concatenated_errors, detailed_linter_results: assertion_error_matches}
|
{count: count, failed: failed, error_messages: concatenated_errors, detailed_linter_results: assertion_error_matches}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.translate_linter(assessment)
|
||||||
|
# The message will be translated once the results were stored in the database
|
||||||
|
# See SubmissionScoring for actual function call
|
||||||
|
|
||||||
|
assessment[:detailed_linter_results].map! do |message|
|
||||||
|
severity = message[:severity]
|
||||||
|
name = message[:name]
|
||||||
|
|
||||||
|
message[:severity] = I18n.t("linter.#{severity}.severity_name", locale: :de, default: message[:severity])
|
||||||
|
message[:name] = I18n.t("linter.#{severity}.#{name}.name", locale: :de, default: message[:name])
|
||||||
|
|
||||||
|
regex = I18n.t("linter.#{severity}.#{name}.regex", locale: :de, default: nil)&.strip
|
||||||
|
|
||||||
|
if regex.present?
|
||||||
|
captures = message[:result].match(Regexp.new(regex)).named_captures.symbolize_keys
|
||||||
|
|
||||||
|
replacement = captures.each do |key, value|
|
||||||
|
value&.replace I18n.t("linter.#{severity}.#{name}.#{key}.#{value}", default: value, locale: :de)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
replacement = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
replacement.merge!(locale: :de, default: message[:result])
|
||||||
|
message[:result] = I18n.t("linter.#{severity}.#{name}.replacement", replacement)
|
||||||
|
message
|
||||||
|
end
|
||||||
|
|
||||||
|
assessment[:error_messages] = assessment[:detailed_linter_results].map do |message|
|
||||||
|
"#{message[:name]}: #{message[:result]}"
|
||||||
|
end
|
||||||
|
|
||||||
|
assessment
|
||||||
|
rescue StandardError => e
|
||||||
|
# A key was not defined or something really bad happened
|
||||||
|
Raven.extra_context(assessment)
|
||||||
|
Raven.capture_exception(e)
|
||||||
|
assessment
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -11,4 +11,8 @@ class PyUnitAndPyLintAdapter < TestingFrameworkAdapter
|
|||||||
PyUnitAdapter.new.parse_output(output)
|
PyUnitAdapter.new.parse_output(output)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def translate_linter(result)
|
||||||
|
PyLintAdapter.translate_linter(result)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
14
spec/helpers/yaml_spec.rb
Normal file
14
spec/helpers/yaml_spec.rb
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'find'
|
||||||
|
require 'yaml'
|
||||||
|
|
||||||
|
describe 'yaml config files' do
|
||||||
|
Find.find(__dir__, 'config') do |path|
|
||||||
|
next unless path =~ /.*.\.yml/
|
||||||
|
|
||||||
|
it "loads #{path} without syntax error" do
|
||||||
|
expect { YAML.load_file(path) }.not_to raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Reference in New Issue
Block a user