From fca85d4d7d1a455299989f8ffd8f1a3090554a2c Mon Sep 17 00:00:00 2001 From: asaldele1 <57564314+asaldele1@users.noreply.github.com> Date: Wed, 10 Jan 2024 18:11:23 +0300 Subject: [PATCH] Add support for stepik submit --- package.json | 40 ++++++++++++++++++++++++++ src/companion.ts | 48 ++++++++++++++++++++++++++++++++ src/extension.ts | 9 +++++- src/preferences.ts | 54 ++++++++++++++++++++++++++++++++++++ src/submit.ts | 42 +++++++++++++++++++++++++++- src/types.ts | 15 +++++++++- src/webview/JudgeView.ts | 6 +++- src/webview/frontend/App.tsx | 22 +++++++++++++++ 8 files changed, 232 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index ac9e59f..4546173 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,11 @@ ], "description": "The compiler chosen in the drop down during Codeforces submission for c" }, + "cph.language.c.SubmissionCompilerStepik": { + "type": "string", + "default": "c++11", + "description": "The compiler chosen during Stepik submission for c" + }, "cph.language.c.Command": { "type": "string", "default": "gcc", @@ -125,6 +130,11 @@ ], "description": "The compiler chosen in the drop down during Codeforces submission for cpp" }, + "cph.language.cpp.SubmissionCompilerStepik": { + "type": "string", + "default": "c++11", + "description": "The compiler chosen during Stepik submission for cpp" + }, "cph.language.cpp.Command": { "type": "string", "default": "g++", @@ -148,6 +158,11 @@ ], "description": "The compiler chosen in the drop down during Codeforces submission for python" }, + "cph.language.python.SubmissionCompilerStepik": { + "type": "string", + "default": "python3", + "description": "The compiler chosen during Stepik submission for python" + }, "cph.language.python.Command": { "type": "string", "default": "python3", @@ -167,6 +182,11 @@ ], "description": "The compiler chosen in the drop down during Codeforces submission for rust" }, + "cph.language.rust.SubmissionCompilerStepik": { + "type": "string", + "default": "rust", + "description": "The compiler chosen during Stepik submission for rust" + }, "cph.language.rust.Command": { "type": "string", "default": "rustc", @@ -186,6 +206,11 @@ ], "description": "The compiler chosen in the drop down during Codeforces submission for go" }, + "cph.language.go.SubmissionCompilerStepik": { + "type": "string", + "default": "go", + "description": "The compiler chosen during Stepik submission for go" + }, "cph.language.go.Command": { "type": "string", "default": "go", @@ -205,6 +230,11 @@ ], "description": "The compiler chosen in the drop down during Codeforces submission for haskell" }, + "cph.language.haskell.SubmissionCompilerStepik": { + "type": "string", + "default": "haskel 7.10", + "description": "The compiler chosen during Stepik submission for haskell" + }, "cph.language.haskell.Command": { "type": "string", "default": "ghc", @@ -225,6 +255,11 @@ ], "description": "The compiler chosen in the drop down during Codeforces submission for java" }, + "cph.language.java.SubmissionCompilerStepik": { + "type": "string", + "default": "java8", + "description": "The compiler chosen during Stepik submission for java" + }, "cph.language.java.Command": { "type": "string", "default": "javac", @@ -244,6 +279,11 @@ ], "description": "The compiler chosen in the drop down during Codeforces submission for js" }, + "cph.language.js.SubmissionCompilerStepik": { + "type": "string", + "default": "javascript", + "description": "The compiler chosen during Stepik submission for js" + }, "cph.language.js.Command": { "type": "string", "default": "node", diff --git a/src/companion.ts b/src/companion.ts index c500c16..46ea199 100644 --- a/src/companion.ts +++ b/src/companion.ts @@ -9,6 +9,7 @@ import { isCodeforcesUrl, randomId } from './utils'; import { getDefaultLangPref, getLanguageId, + getLanguageIdStepik, useShortCodeForcesName, getMenuChoices, getDefaultLanguageTemplateFileLocation, @@ -23,6 +24,7 @@ import os from 'os'; const emptyResponse: CphEmptyResponse = { empty: true }; let savedResponse: CphEmptyResponse | CphSubmitResponse = emptyResponse; const COMPANION_LOGGING = false; +export let stepikResult = ""; export const submitKattisProblem = (problem: Problem) => { globalThis.reporter.sendTelemetryEvent(telmetry.SUBMIT_TO_KATTIS); @@ -75,6 +77,52 @@ export const submitKattisProblem = (problem: Problem) => { }); }; +export const submitStepikProblem = (problem: Problem) => { + const srcPath = problem.srcPath; + const homedir = os.homedir(); + let submitPath = `${homedir}/.stepik/submitter.py`; + if (process.platform == 'win32') { + if ( + !existsSync(`${homedir}\\.stepik\\.client_file`) || + !existsSync(`${homedir}\\.stepik\\submitter.py`) + ) { + vscode.window.showErrorMessage( + `Please ensure .client_file and submitter.py are present in ${homedir}\\.stepik`, + ); + return; + } else { + submitPath = `${homedir}\\.stepik\\submitter.py`; + } + } else { + if ( + !existsSync(`${homedir}/.stepik/.client_file`) || + !existsSync(`${homedir}/.stepik/submitter.py`) + ) { + vscode.window.showErrorMessage( + `Please ensure .client_file and submitter.py are present in ${homedir}/.stepik`, + ); + return; + } else { + submitPath = `${homedir}/.stepik/submitter.py`; + } + } + const pyshell = spawn('python', [submitPath, 'submit', srcPath, '-l', getLanguageIdStepik(problem.srcPath), '--link', problem.url, "--silent"]); + + pyshell.stdout.on('data', function (data) { + console.log(data.toString()); + stepikResult += data.toString(); + getJudgeViewProvider().extensionToJudgeViewMessage({ + command: 'new-problem', + problem, + }); + ({ command: 'submit-finished' }); + }); + pyshell.stderr.on('data', function (data) { + console.log(data.tostring()); + vscode.window.showErrorMessage(data); + }); +}; + /** Stores a response to be submitted to CF page soon. */ export const storeSubmitProblem = (problem: Problem) => { const srcPath = problem.srcPath; diff --git a/src/extension.ts b/src/extension.ts index aa8fbb4..2d86527 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,7 +6,7 @@ import { editorClosed, checkLaunchWebview, } from './webview/editorChange'; -import { submitToCodeForces, submitToKattis } from './submit'; +import { submitToCodeForces, submitToKattis, submitToStepik } from './submit'; import JudgeViewProvider from './webview/JudgeView'; import { getRetainWebviewContextPref } from './preferences'; import TelemetryReporter from '@vscode/extension-telemetry'; @@ -47,6 +47,12 @@ const registerCommands = (context: vscode.ExtensionContext) => { submitToKattis(); }, ); + const disposable5 = vscode.commands.registerCommand( + 'cph.submitToStepik', + () => { + submitToStepik(); + }, + ); judgeViewProvider = new JudgeViewProvider(context.extensionUri); @@ -65,6 +71,7 @@ const registerCommands = (context: vscode.ExtensionContext) => { context.subscriptions.push(disposable2); context.subscriptions.push(disposable3); context.subscriptions.push(disposable4); + context.subscriptions.push(disposable5); globalThis.reporter = new TelemetryReporter(config.telemetryKey); context.subscriptions.push(globalThis.reporter); diff --git a/src/preferences.ts b/src/preferences.ts index 4a04e97..31a5f61 100644 --- a/src/preferences.ts +++ b/src/preferences.ts @@ -164,3 +164,57 @@ export const getLanguageId = (srcPath: string): number => { console.error("Couldn't find id for compiler " + compiler); return -1; }; + +export const getLanguageIdStepik = (srcPath: string): number => { + const extension = path.extname(srcPath); + let compiler = null; + switch (extension) { + case '.cpp': { + compiler = getPreference('language.cpp.SubmissionCompilerStepik'); + break; + } + + case '.java': { + compiler = getPreference('language.java.SubmissionCompilerStepik'); + break; + } + + case '.js': { + compiler = getPreference('language.js.SubmissionCompilerStepik'); + break; + } + + case '.c': { + compiler = getPreference('language.c.SubmissionCompilerStepik'); + break; + } + + case '.rs': { + compiler = getPreference('language.rust.SubmissionCompilerStepik'); + break; + } + + case '.py': { + compiler = getPreference('language.python.SubmissionCompilerStepik'); + break; + } + + case '.go': { + compiler = getPreference('language.go.SubmissionCompilerStepik'); + break; + } + + case '.hs': { + compiler = getPreference('language.haskell.SubmissionCompilerStepik'); + break; + } + } + if (compiler == null) return -1; + for (const [_compiler, id] of Object.entries(config.compilerToId)) { + if (_compiler === compiler) { + return id; + } + } + console.error("Couldn't find id for compiler " + compiler); + return -1; +}; \ No newline at end of file diff --git a/src/submit.ts b/src/submit.ts index 5527dc6..d372b53 100644 --- a/src/submit.ts +++ b/src/submit.ts @@ -1,6 +1,6 @@ import { getProblem } from './parser'; import * as vscode from 'vscode'; -import { storeSubmitProblem, submitKattisProblem } from './companion'; +import { storeSubmitProblem, submitKattisProblem, submitStepikProblem } from './companion'; import { getJudgeViewProvider } from './extension'; import telmetry from './telmetry'; @@ -45,6 +45,46 @@ export const submitToKattis = async () => { }); }; +export const submitToStepik = async () => { + const srcPath = vscode.window.activeTextEditor?.document.fileName; + if (!srcPath) { + vscode.window.showErrorMessage( + 'Active editor is not supported for submission', + ); + return; + } + + const textEditor = await vscode.workspace.openTextDocument(srcPath); + await vscode.window.showTextDocument(textEditor, vscode.ViewColumn.One); + await textEditor.save(); + + const problem = getProblem(srcPath); + + if (!problem) { + vscode.window.showErrorMessage('Failed to parse current code.'); + return; + } + + let url: URL; + try { + url = new URL(problem.url); + } catch (err) { + console.error(err); + vscode.window.showErrorMessage('Not a kattis problem.'); + return; + } + + if (url.hostname !== 'stepik.org') { + vscode.window.showErrorMessage('Not a kattis problem.'); + return; + } + + submitStepikProblem(problem); + getJudgeViewProvider().extensionToJudgeViewMessage({ + command: 'waiting-for-submit', + }); +}; + export const submitToCodeForces = async () => { const srcPath = vscode.window.activeTextEditor?.document.fileName; diff --git a/src/types.ts b/src/types.ts index 10f3bf4..27f15d9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,27 +14,35 @@ export type prefSection = | 'general.menuChoices' | 'language.c.Args' | 'language.c.SubmissionCompiler' + | 'language.c.SubmissionCompilerStepik' | 'language.c.Command' | 'language.cpp.Args' | 'language.cpp.SubmissionCompiler' + | 'language.cpp.SubmissionCompilerStepik' | 'language.cpp.Command' | 'language.go.Args' | 'language.go.SubmissionCompiler' + | 'language.go.SubmissionCompilerStepik' | 'language.go.Command' | 'language.rust.Args' | 'language.rust.SubmissionCompiler' + | 'language.rust.SubmissionCompilerStepik' | 'language.rust.Command' | 'language.java.Args' | 'language.java.SubmissionCompiler' + | 'language.java.SubmissionCompilerStepik' | 'language.java.Command' | 'language.js.Args' | 'language.js.SubmissionCompiler' + | 'language.js.SubmissionCompilerStepik' | 'language.js.Command' | 'language.python.Args' | 'language.python.SubmissionCompiler' + | 'language.python.SubmissionCompilerStepik' | 'language.python.Command' | 'language.haskell.Args' | 'language.haskell.SubmissionCompiler' + | 'language.haskell.SubmissionCompilerStepik' | 'language.haskell.Command' | 'general.retainWebviewContext' | 'general.autoShowJudge' @@ -133,6 +141,10 @@ export type SubmitKattis = { command: 'submitKattis'; } & WebviewMessageCommon; +export type SubmitStepik = { + command: 'submitStepik'; +} & WebviewMessageCommon; + export type GetInitialProblem = { command: 'get-initial-problem'; }; @@ -151,7 +163,8 @@ export type WebviewToVSEvent = | DeleteTcsCommand | SubmitCf | OnlineJudgeEnv - | SubmitKattis; + | SubmitKattis + | SubmitStepik; export type RunningCommand = { command: 'running'; diff --git a/src/webview/JudgeView.ts b/src/webview/JudgeView.ts index 602c66e..c6d402e 100644 --- a/src/webview/JudgeView.ts +++ b/src/webview/JudgeView.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { storeSubmitProblem, submitKattisProblem } from '../companion'; +import { storeSubmitProblem, submitKattisProblem, submitStepikProblem } from '../companion'; import { killRunning } from '../executions'; import { saveProblem } from '../parser'; import { VSToWebViewMessage, WebviewToVSEvent } from '../types'; @@ -84,6 +84,10 @@ class JudgeViewProvider implements vscode.WebviewViewProvider { submitKattisProblem(message.problem); break; } + case 'submitStepik': { + submitStepikProblem(message.problem); + break; + } case 'online-judge-env': { setOnlineJudgeEnv(message.value); diff --git a/src/webview/frontend/App.tsx b/src/webview/frontend/App.tsx index 389d50a..c6b850a 100644 --- a/src/webview/frontend/App.tsx +++ b/src/webview/frontend/App.tsx @@ -10,6 +10,7 @@ import { RunningCommand, WebViewpersistenceState, } from '../../types'; +import { stepikResult } from '../../companion' import CaseView from './CaseView'; declare const vscodeApi: { postMessage: (message: WebviewToVSEvent) => void; @@ -202,6 +203,15 @@ function Judge(props: { setWaitingForSubmit(true); }; + const submitStepik = () => { + vscodeApi.postMessage({ + command: 'submitStepik', + problem, + }); + + setWaitingForSubmit(true); + }; + const debounceFocusLast = () => { setTimeout(() => { setFocusLast(false); @@ -347,6 +357,18 @@ function Judge(props: { )} ); + }else if (url.hostname == 'stepik.org') { + return ( +