Skip to content

feat(js-cs-go-rs-ts-support): add more programming languages #7995

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

Merged
merged 1 commit into from
Jul 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ GIT

GIT
remote: https://github.com/Coursemology/polyglot
revision: 7cf1c55b530fd50950144d24524b0647f2d98f76
revision: bf38cf4d4e8a4ee732c604fc27db2263257a23ec
specs:
coursemology-polyglot (0.4.1)
coursemology-polyglot (0.4.2)
activesupport (>= 4.2)

GIT
Expand Down
4 changes: 4 additions & 0 deletions app/controllers/concerns/codaveri_language_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ def programming_language_map
Coursemology::Polyglot::Language::Java::Java21 => {
language: 'java',
version: '21.0'
},
Coursemology::Polyglot::Language::CSharp::CSharp5Point0 => {
language: 'csharp',
version: '5.0.201'
}
}
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
# frozen_string_literal: true
class Course::Assessment::Question::CodaveriProblemGenerationService
class Course::Assessment::Question::CodaveriProblemGenerationService # rubocop:disable Metrics/ClassLength
POLL_INTERVAL_SECONDS = 2
MAX_POLL_RETRIES = 1000

LANGUAGE_FILENAME_MAPPING = {
'python' => 'main.py',
'r' => 'main.R',
'javascript' => 'main.js',
'csharp' => 'main.cs',
'go' => 'main.go',
'rust' => 'main.rs',
'typescript' => 'main.ts'
}.freeze

LANGUAGE_TESTCASE_TYPE_MAPPING = {
'r' => 'IO',
'javascript' => 'IO',
'csharp' => 'IO',
'go' => 'IO',
'rust' => 'IO',
'typescript' => 'IO'
}.freeze

def codaveri_generate_problem
response_status, response_body, generation_id = send_problem_generation_request
poll_count = 0
Expand All @@ -23,7 +42,7 @@ def codaveri_generate_problem

private

def initialize(assessment, params, language, version)
def initialize(assessment, params, language, version) # rubocop:disable Metrics/AbcSize
custom_prompt = params[:custom_prompt].to_s || ''
@payload = {
userId: assessment.creator_id.to_s,
Expand Down Expand Up @@ -71,15 +90,15 @@ def initialize(assessment, params, language, version)
end

def generate_payload_file_name(codaveri_language, file_content)
return 'main.py' if codaveri_language == 'python'
return 'main.R' if codaveri_language == 'r'
return LANGUAGE_FILENAME_MAPPING[codaveri_language] if LANGUAGE_FILENAME_MAPPING.key?(codaveri_language)

match = file_content&.match(/\bclass\s+(\w+)\s*\{/)
match ? "#{match[1]}.java" : 'Main.java'
end

def generate_payload_testcases_type(codaveri_language)
codaveri_language == 'r' ? 'IO' : 'expression'
# New languages supported by Codaveri only allow IO test cases.
LANGUAGE_TESTCASE_TYPE_MAPPING.fetch(codaveri_language, 'expression')
end

def generate_payload_io_test_case(test_case, visibility, index)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
prepare:

compile: submission/template.cs tests/prepend.cs tests/append.cs
cat tests/prepend.cs submission/template.cs tests/append.cs > answer.cs

public:
echo "Not Implemented"

private:
echo "Not Implemented"

evaluation:
echo "Not Implemented"

solution: solution.cs
echo "Not Implemented"

solution.cs: solution/template.cs tests/prepend.cs tests/append.cs
cat tests/prepend.cs solution/template.cs tests/append.cs > solution.cs

clean:
rm -f answer.cs
rm -f report.xml
rm -f solution.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# frozen_string_literal: true
class Course::Assessment::Question::Programming::CSharp::CSharpPackageService < # rubocop:disable Metrics/ClassLength
Course::Assessment::Question::Programming::LanguagePackageService
def submission_templates
[
{
filename: 'template.cs',

Check warning on line 7 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L7

Added line #L7 was not covered by tests
content: @test_params[:submission] || ''
}
]
end

def generate_package(old_attachment)
return nil if @test_params.blank?

@tmp_dir = Dir.mktmpdir

Check warning on line 16 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L16

Added line #L16 was not covered by tests
@old_meta = old_attachment.present? ? extract_meta(old_attachment, nil) : nil
data_files_to_keep = old_attachment.present? ? find_data_files_to_keep(old_attachment) : []
@meta = generate_meta(data_files_to_keep)

Check warning on line 19 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L19

Added line #L19 was not covered by tests

return nil if @meta == @old_meta

@attachment = generate_zip_file(data_files_to_keep)

Check warning on line 23 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L23

Added line #L23 was not covered by tests
FileUtils.remove_entry @tmp_dir if @tmp_dir.present?
@attachment

Check warning on line 25 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L25

Added line #L25 was not covered by tests
end

def extract_meta(attachment, template_files)
return @meta if @meta.present? && attachment == @attachment

# attachment will be nil if the question is not autograded, in that case the meta data will be
# generated from the template files in the database.
return generate_non_autograded_meta(template_files) if attachment.nil?

extract_autograded_meta(attachment)

Check warning on line 35 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L35

Added line #L35 was not covered by tests
end

private

def extract_autograded_meta(attachment)
attachment.open(binmode: true) do |temporary_file|
package = Course::Assessment::ProgrammingPackage.new(temporary_file)
meta = package.meta_file

Check warning on line 43 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L41-L43

Added lines #L41 - L43 were not covered by tests
@old_meta = meta.present? ? JSON.parse(meta) : nil
ensure
next unless package

temporary_file.close

Check warning on line 48 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L48

Added line #L48 was not covered by tests
end
end

def generate_non_autograded_meta(template_files)
meta = default_meta

Check warning on line 53 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L53

Added line #L53 was not covered by tests

return meta if template_files.blank?

# For python editor, there is only a single submission template file.
meta[:submission] = template_files.first.content

Check warning on line 58 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L58

Added line #L58 was not covered by tests

meta.as_json

Check warning on line 60 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L60

Added line #L60 was not covered by tests
end

def extract_from_package(package, new_data_filenames, data_files_to_delete)
data_files_to_keep = []

Check warning on line 64 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L64

Added line #L64 was not covered by tests

if @old_meta.present?
package.unzip_file @tmp_dir

Check warning on line 67 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L67

Added line #L67 was not covered by tests

@old_meta['data_files']&.each do |file|
next if data_files_to_delete.try(:include?, file['filename'])
# new files overrides old ones
next if new_data_filenames.include?(file['filename'])

data_files_to_keep.append(File.new(File.join(@tmp_dir, file['filename'])))

Check warning on line 74 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L74

Added line #L74 was not covered by tests
end
end

data_files_to_keep

Check warning on line 78 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L78

Added line #L78 was not covered by tests
end

def find_data_files_to_keep(attachment)
new_filenames = (@test_params[:data_files] || []).reject(&:nil?).map(&:original_filename)

Check warning on line 82 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L82

Added line #L82 was not covered by tests

attachment.open(binmode: true) do |temporary_file|
package = Course::Assessment::ProgrammingPackage.new(temporary_file)
return extract_from_package(package, new_filenames, @test_params[:data_files_to_delete])

Check warning on line 86 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L84-L86

Added lines #L84 - L86 were not covered by tests
ensure
next unless package

temporary_file.close

Check warning on line 90 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L90

Added line #L90 was not covered by tests
end
end

# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def generate_zip_file(data_files_to_keep)
tmp = Tempfile.new(['package', '.zip'])
makefile_path = get_file_path('c_sharp_makefile')

Check warning on line 97 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L96-L97

Added lines #L96 - L97 were not covered by tests

Zip::OutputStream.open(tmp.path) do |zip|

Check warning on line 99 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L99

Added line #L99 was not covered by tests
# Create solution directory with template file
zip.put_next_entry 'solution/'
zip.put_next_entry 'solution/template.cs'
zip.print @test_params[:solution]
zip.print "\n"

Check warning on line 104 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L101-L104

Added lines #L101 - L104 were not covered by tests

# Create submission directory with template file
zip.put_next_entry 'submission/'
zip.put_next_entry 'submission/template.cs'
zip.print @test_params[:submission]
zip.print "\n"

Check warning on line 110 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L107-L110

Added lines #L107 - L110 were not covered by tests

# Create tests directory with prepend, append and autograde files
zip.put_next_entry 'tests/'
zip.put_next_entry 'tests/append.cs'
zip.print "\n"
zip.print @test_params[:append]
zip.print "\n"

Check warning on line 117 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L113-L117

Added lines #L113 - L117 were not covered by tests

zip.put_next_entry 'tests/prepend.cs'
zip.print @test_params[:prepend]
zip.print "\n"

Check warning on line 121 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L119-L121

Added lines #L119 - L121 were not covered by tests

[:public, :private, :evaluation].each do |test_type|
zip_test_files(test_type, zip)

Check warning on line 124 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L123-L124

Added lines #L123 - L124 were not covered by tests
end

# Creates Makefile
zip.put_next_entry 'Makefile'
zip.print File.read(makefile_path)

Check warning on line 129 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L128-L129

Added lines #L128 - L129 were not covered by tests

zip.put_next_entry '.meta'
zip.print @meta.to_json

Check warning on line 132 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L131-L132

Added lines #L131 - L132 were not covered by tests
end

Zip::File.open(tmp.path) do |zip|

Check warning on line 135 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L135

Added line #L135 was not covered by tests
@test_params[:data_files]&.each do |file|
next if file.nil?

zip.add(file.original_filename, file.tempfile.path)

Check warning on line 139 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L139

Added line #L139 was not covered by tests
end

data_files_to_keep.each do |file|
zip.add(File.basename(file.path), file.path)

Check warning on line 143 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L142-L143

Added lines #L142 - L143 were not covered by tests
end
end

tmp

Check warning on line 147 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L147

Added line #L147 was not covered by tests
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength

# Retrieves the absolute path of the file specified
#
# @param [String] filename The filename of the file to get the path of
def get_file_path(filename)
File.join(__dir__, filename).freeze

Check warning on line 155 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L155

Added line #L155 was not covered by tests
end

def zip_test_files(test_type, zip)
# Create a dummy report to pass test cases to DB/Codaveri
tests = @test_params[:test_cases]

Check warning on line 160 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L160

Added line #L160 was not covered by tests
return unless tests[test_type]&.count&.> 0

zip.put_next_entry "report-#{test_type}.xml"
zip.print build_dummy_report(test_type, tests[test_type])

Check warning on line 164 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L163-L164

Added lines #L163 - L164 were not covered by tests
end

def build_dummy_report(test_type, test_cases)
Course::Assessment::ProgrammingTestCaseReportBuilder.build_dummy_report(test_type, test_cases, '.cs')

Check warning on line 168 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L168

Added line #L168 was not covered by tests
end

def get_data_files_meta(data_files_to_keep, new_data_files)
data_files = []

Check warning on line 172 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L172

Added line #L172 was not covered by tests

new_data_files.each do |file|
sha256 = Digest::SHA256.file(file.tempfile).hexdigest
data_files.append(filename: file.original_filename, size: file.tempfile.size, hash: sha256)

Check warning on line 176 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L174-L176

Added lines #L174 - L176 were not covered by tests
end

data_files_to_keep.each do |file|
sha256 = Digest::SHA256.file(file).hexdigest
data_files.append(filename: File.basename(file.path), size: file.size, hash: sha256)

Check warning on line 181 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L179-L181

Added lines #L179 - L181 were not covered by tests
end

data_files.sort_by { |file| file[:filename].downcase }

Check warning on line 184 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L184

Added line #L184 was not covered by tests
end

def generate_meta(data_files_to_keep)
meta = default_meta

Check warning on line 188 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L188

Added line #L188 was not covered by tests

[:submission, :solution, :prepend, :append].each { |field| meta[field] = @test_params[field] }

Check warning on line 190 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L190

Added line #L190 was not covered by tests

new_data_files = (@test_params[:data_files] || []).reject(&:nil?)
meta[:data_files] = get_data_files_meta(data_files_to_keep, new_data_files)

Check warning on line 193 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L192-L193

Added lines #L192 - L193 were not covered by tests

[:public, :private, :evaluation].each do |test_type|
meta[:test_cases][test_type] = @test_params[:test_cases][test_type] || []

Check warning on line 196 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L195-L196

Added lines #L195 - L196 were not covered by tests
end

meta.as_json

Check warning on line 199 in app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb

View check run for this annotation

Codecov / codecov/patch

app/services/course/assessment/question/programming/c_sharp/c_sharp_package_service.rb#L199

Added line #L199 was not covered by tests
end
end
24 changes: 24 additions & 0 deletions app/services/course/assessment/question/programming/go/go_makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
prepare:

compile: submission/template.go tests/prepend.go tests/append.go
cat tests/prepend.go submission/template.go tests/append.go > answer.go

public:
echo "Not Implemented"

private:
echo "Not Implemented"

evaluation:
echo "Not Implemented"

solution: solution.go
echo "Not Implemented"

solution.go: solution/template.go tests/prepend.go tests/append.go
cat tests/prepend.go solution/template.go tests/append.go > solution.go

clean:
rm -f answer.go
rm -f report.xml
rm -f solution.go
Loading