From 8078124ac125063348388921fa94874fdbc9414f Mon Sep 17 00:00:00 2001 From: LookAsLukas Date: Sat, 25 Apr 2026 22:23:37 +0700 Subject: [PATCH 1/4] ETO PROSTO KAPES A NE LABA --- Task_2_4_1/.gitignore | 43 ++++ Task_2_4_1/.idea/.gitignore | 4 + Task_2_4_1/.idea/artifacts/Task_2_4_1_jar.xml | 19 ++ Task_2_4_1/.idea/gradle.xml | 17 ++ Task_2_4_1/.idea/misc.xml | 7 + Task_2_4_1/.idea/vcs.xml | 6 + Task_2_4_1/build.gradle | 53 ++++ Task_2_4_1/config/groups.groovy | 10 + Task_2_4_1/config/tasks.groovy | 10 + Task_2_4_1/config/test_check.groovy | 21 ++ .../gradle/wrapper/gradle-wrapper.properties | 6 + Task_2_4_1/gradlew | 234 ++++++++++++++++++ Task_2_4_1/gradlew.bat | 89 +++++++ Task_2_4_1/index.html | Bin 0 -> 2658 bytes Task_2_4_1/settings.gradle | 1 + .../nmashkin/task241/dsl/BonusBlock.groovy | 15 ++ .../nmashkin/task241/dsl/CheckBlock.groovy | 16 ++ .../task241/dsl/CheckpointsBlock.groovy | 20 ++ .../nmashkin/task241/dsl/ConfigBuilder.groovy | 76 ++++++ .../nmashkin/task241/dsl/CriteriaBlock.groovy | 13 + .../nmashkin/task241/dsl/GroupBlock.groovy | 20 ++ .../nmashkin/task241/dsl/SettingsBlock.groovy | 23 ++ .../nmashkin/task241/dsl/TasksBlock.groovy | 27 ++ .../java/ru/nsu/nmashkin/task241/Main.java | 156 ++++++++++++ .../nmashkin/task241/cli/ConfigLoader.java | 43 ++++ .../ru/nsu/nmashkin/task241/core/Check.java | 9 + .../nsu/nmashkin/task241/core/Checkpoint.java | 11 + .../ru/nsu/nmashkin/task241/core/Config.java | 201 +++++++++++++++ .../ru/nsu/nmashkin/task241/core/Group.java | 30 +++ .../ru/nsu/nmashkin/task241/core/Student.java | 10 + .../ru/nsu/nmashkin/task241/core/Task.java | 15 ++ .../task241/service/BuildService.java | 24 ++ .../task241/service/DocGenerator.java | 24 ++ .../nmashkin/task241/service/GitService.java | 102 ++++++++ .../task241/service/GradleExecutor.java | 78 ++++++ .../task241/service/HtmlReportGenerator.java | 94 +++++++ .../task241/service/ScoreCalculator.java | 66 +++++ .../task241/service/StyleChecker.java | 121 +++++++++ .../service/TaskVerificationResult.java | 14 ++ .../nmashkin/task241/service/TestService.java | 74 ++++++ .../src/main/resources/META-INF/MANIFEST.MF | 3 + .../src/main/resources/google_style.xml | 50 ++++ 42 files changed, 1855 insertions(+) create mode 100644 Task_2_4_1/.gitignore create mode 100644 Task_2_4_1/.idea/.gitignore create mode 100644 Task_2_4_1/.idea/artifacts/Task_2_4_1_jar.xml create mode 100644 Task_2_4_1/.idea/gradle.xml create mode 100644 Task_2_4_1/.idea/misc.xml create mode 100644 Task_2_4_1/.idea/vcs.xml create mode 100644 Task_2_4_1/build.gradle create mode 100644 Task_2_4_1/config/groups.groovy create mode 100644 Task_2_4_1/config/tasks.groovy create mode 100644 Task_2_4_1/config/test_check.groovy create mode 100644 Task_2_4_1/gradle/wrapper/gradle-wrapper.properties create mode 100644 Task_2_4_1/gradlew create mode 100644 Task_2_4_1/gradlew.bat create mode 100644 Task_2_4_1/index.html create mode 100644 Task_2_4_1/settings.gradle create mode 100644 Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/BonusBlock.groovy create mode 100644 Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/CheckBlock.groovy create mode 100644 Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/CheckpointsBlock.groovy create mode 100644 Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/ConfigBuilder.groovy create mode 100644 Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/CriteriaBlock.groovy create mode 100644 Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/GroupBlock.groovy create mode 100644 Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/SettingsBlock.groovy create mode 100644 Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/TasksBlock.groovy create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/Main.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/cli/ConfigLoader.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Check.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Checkpoint.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Config.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Group.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Student.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Task.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/BuildService.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/DocGenerator.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GitService.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GradleExecutor.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/ScoreCalculator.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/StyleChecker.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/TaskVerificationResult.java create mode 100644 Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/TestService.java create mode 100644 Task_2_4_1/src/main/resources/META-INF/MANIFEST.MF create mode 100644 Task_2_4_1/src/main/resources/google_style.xml diff --git a/Task_2_4_1/.gitignore b/Task_2_4_1/.gitignore new file mode 100644 index 0000000..4fe79f8 --- /dev/null +++ b/Task_2_4_1/.gitignore @@ -0,0 +1,43 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +*.jar +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/Task_2_4_1/.idea/.gitignore b/Task_2_4_1/.idea/.gitignore new file mode 100644 index 0000000..4287606 --- /dev/null +++ b/Task_2_4_1/.idea/.gitignore @@ -0,0 +1,4 @@ +# Default ignored files +/shelf/ +/workspace.xml +*.jar diff --git a/Task_2_4_1/.idea/artifacts/Task_2_4_1_jar.xml b/Task_2_4_1/.idea/artifacts/Task_2_4_1_jar.xml new file mode 100644 index 0000000..a24e261 --- /dev/null +++ b/Task_2_4_1/.idea/artifacts/Task_2_4_1_jar.xml @@ -0,0 +1,19 @@ + + + $PROJECT_DIR$/out/artifacts/Task_2_4_1_jar + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Task_2_4_1/.idea/gradle.xml b/Task_2_4_1/.idea/gradle.xml new file mode 100644 index 0000000..2a65317 --- /dev/null +++ b/Task_2_4_1/.idea/gradle.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/Task_2_4_1/.idea/misc.xml b/Task_2_4_1/.idea/misc.xml new file mode 100644 index 0000000..b35a12a --- /dev/null +++ b/Task_2_4_1/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Task_2_4_1/.idea/vcs.xml b/Task_2_4_1/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/Task_2_4_1/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Task_2_4_1/build.gradle b/Task_2_4_1/build.gradle new file mode 100644 index 0000000..cfba2f5 --- /dev/null +++ b/Task_2_4_1/build.gradle @@ -0,0 +1,53 @@ +plugins { + id 'java' + id 'groovy' + id 'application' + id 'com.gradleup.shadow' version '8.3.0' +} + +group = 'ru.nsu.nmashkin.task241' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.codehaus.groovy:groovy:3.0.19' + implementation 'org.codehaus.groovy:groovy-templates:3.0.19' + + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +test { + useJUnitPlatform() +} + +application { + mainClass = 'ru.nsu.nmashkin.task241.Main' +} + +sourceSets { + main { + // Отдаём все исходники компилятору Groovy. Он умеет компилировать и Java. + groovy { + srcDirs = ['src/main/groovy', 'src/main/java'] + } + // Отключаем отдельный javac, чтобы не было конфликта и циклов + java { + srcDirs = [] + } + } +} + +tasks.named('test') { + useJUnitPlatform() +} \ No newline at end of file diff --git a/Task_2_4_1/config/groups.groovy b/Task_2_4_1/config/groups.groovy new file mode 100644 index 0000000..b91db5b --- /dev/null +++ b/Task_2_4_1/config/groups.groovy @@ -0,0 +1,10 @@ +group("24213") { + IlyaStub("Ilya Stubarev", "https://github.com/IlyaStub/OOP") + "7AD0VNIK"("Klim Sadov", "https://github.com/7AD0VNIK/OOP") + aelsi2("Andrey Eliseev", "https://github.com/aelsi2/OOP") + Matvey("Matvey Solovev", "https://github.com/fresh-ops/OOP") +} + +group("24214") { + LookAsLukas("Mashkin Nikolay", "https://github.com/LookAsLukas/OOP") +} diff --git a/Task_2_4_1/config/tasks.groovy b/Task_2_4_1/config/tasks.groovy new file mode 100644 index 0000000..ee4eca6 --- /dev/null +++ b/Task_2_4_1/config/tasks.groovy @@ -0,0 +1,10 @@ +tasks { + Task_1_2_1("Graph bs", 3, "01.11.2025", "08.11.2025") + Task_1_1_1("IDKlol", 1, "01.09.2025", "08.09.2025") +} + +criteria { + excellent 4 + goida 2 + loh 1 +} diff --git a/Task_2_4_1/config/test_check.groovy b/Task_2_4_1/config/test_check.groovy new file mode 100644 index 0000000..73fcf83 --- /dev/null +++ b/Task_2_4_1/config/test_check.groovy @@ -0,0 +1,21 @@ +importConfig 'config/tasks.groovy' +importConfig 'config/groups.groovy' + +checkpoints { + oct "01.10.2025" + lol "01.01.2026" +} + +points { + IlyaStub (-67) + "7AD0VNIK" (67) + aelsi2 (0) + Matvey (1) + LookAsLukas 999999 +} + +check { + LookAsLukas "Task_1_1_1" + LookAsLukas "Task_1_2_1" + aelsi2 "Task_1_1_1" +} diff --git a/Task_2_4_1/gradle/wrapper/gradle-wrapper.properties b/Task_2_4_1/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..6e512f7 --- /dev/null +++ b/Task_2_4_1/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Apr 23 09:41:48 NOVT 2026 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Task_2_4_1/gradlew b/Task_2_4_1/gradlew new file mode 100644 index 0000000..1b6c787 --- /dev/null +++ b/Task_2_4_1/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Task_2_4_1/gradlew.bat b/Task_2_4_1/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/Task_2_4_1/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Task_2_4_1/index.html b/Task_2_4_1/index.html new file mode 100644 index 0000000000000000000000000000000000000000..b52413e23b94188e028bbc7f3d81f718597ae981 GIT binary patch literal 2658 zcmeH}-D?w35XI+N@PCLD@x|DDS!)slYFnTD0PBNKk|t|5BsC#PZA0n5uKv#4+-`Og zXr)jPWLbMNGxyBgGiUDl`&Zi@*@1nuw{~n_?UNl^$x>U`sg12;?ZP^-uJzr1Y6($Y zqI<-2ZDw<~ys{_u1*{`0+dgYy%!yorm+B(!R6$fv3!>OTHlyOKY-{eZsCO z_8z-hzUs4Yh-W}d%_`U~@QmG-fRYk9#H;F?R<&o=uq&_e#B*PI?inZv)s{h?U>}oX z>a{mFEG=r$$p)T7n9n_HiR>*q$Jz%!^=uVvkFjdU1?YKi18=BU6{Yr+fBSGCHeyA> z*O^LGqsoL{vK^|@v&q5Uih%{^xd5+BX6+ilQIk~yuM1}&^-SV&gm(t|88LmLc8FfD^?lAPM9Qz~1Hh!^10S>2pXNYaLsQAFz5 z9vJ)n-UcZpGS7F}*0A@oc* ziq8DU-o24$$a~(fPu=Km@g2ZpZ;M^o!t`&;lsF&rE&Gna)0xIOpK1+f301G3vB+MG z)U0*vwQGLHsh9A02=0*F3D3_}hH$fm=*Yo0xQ=hAKa0}cr~5-n*S!~2uUl`ztlf8f z$6nIQioeC&>h1}3Rk?dAoPU?M>J`em9vvADs?x8F*ASQK>?V~q9QnUWZ?Dq#uBR0B z=v!B^5BwdWCh2UM>(jri)4f~hRA0>a{GDd=bALBRYd-z!^C8~vd-CH~P&Z3l7i;RQ eCSNzerY}r?Wo`Py7v%2KzgxH;vj08mcK8F-0=?@1 literal 0 HcmV?d00001 diff --git a/Task_2_4_1/settings.gradle b/Task_2_4_1/settings.gradle new file mode 100644 index 0000000..4fadcb5 --- /dev/null +++ b/Task_2_4_1/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'Task_2_4_1' \ No newline at end of file diff --git a/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/BonusBlock.groovy b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/BonusBlock.groovy new file mode 100644 index 0000000..ea40423 --- /dev/null +++ b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/BonusBlock.groovy @@ -0,0 +1,15 @@ +package ru.nsu.nmashkin.task241.dsl + +import ru.nsu.nmashkin.task241.core.Config + +class BonusBlock { + private final Config config + + BonusBlock(Config config) { this.config = config } + + def methodMissing(String name, args) { + if (args.length == 1 && args[0] instanceof Number) { + config.addBonus(name, args[0] as int) + } + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/CheckBlock.groovy b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/CheckBlock.groovy new file mode 100644 index 0000000..28074d3 --- /dev/null +++ b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/CheckBlock.groovy @@ -0,0 +1,16 @@ +package ru.nsu.nmashkin.task241.dsl + +import ru.nsu.nmashkin.task241.core.Check +import ru.nsu.nmashkin.task241.core.Config + +class CheckBlock { + private final Config config + + CheckBlock(Config config) { this.config = config } + + def methodMissing(String name, args) { + if (args.length == 1) { + config.addCheckCommand(new Check(args[0], name)) + } + } +} diff --git a/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/CheckpointsBlock.groovy b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/CheckpointsBlock.groovy new file mode 100644 index 0000000..b9cd2ca --- /dev/null +++ b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/CheckpointsBlock.groovy @@ -0,0 +1,20 @@ +package ru.nsu.nmashkin.task241.dsl + +import ru.nsu.nmashkin.task241.core.Checkpoint +import ru.nsu.nmashkin.task241.core.Config +import java.time.LocalDate +import java.time.format.DateTimeFormatter + +class CheckpointsBlock { + private final Config config + private static final DateTimeFormatter DF = DateTimeFormatter.ofPattern("dd.MM.yyyy") + + CheckpointsBlock(Config config) { this.config = config } + + def methodMissing(String name, args) { + if (args.length == 1 && args[0] instanceof String) { + config.addCheckpoint(new Checkpoint(name, + LocalDate.parse(args[0] as CharSequence, DF))) + } + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/ConfigBuilder.groovy b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/ConfigBuilder.groovy new file mode 100644 index 0000000..c7c1654 --- /dev/null +++ b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/ConfigBuilder.groovy @@ -0,0 +1,76 @@ +package ru.nsu.nmashkin.task241.dsl + +import org.codehaus.groovy.control.CompilerConfiguration +import ru.nsu.nmashkin.task241.core.Config +import ru.nsu.nmashkin.task241.core.Group + +class ConfigBuilder { + private final Config config = new Config() + + void tasks(Closure closure) { + def block = new TasksBlock(config) + closure.delegate = block + closure.resolveStrategy = Closure.DELEGATE_ONLY + closure.call() + } + + void group(String name, Closure closure) { + def group = new Group(name) + def block = new GroupBlock(group) + closure.delegate = block + closure.resolveStrategy = Closure.DELEGATE_ONLY + closure.call() + config.addGroup(group) + } + + void checkpoints(Closure closure) { + closure.delegate = new CheckpointsBlock(config) + closure.resolveStrategy = Closure.DELEGATE_ONLY + closure.call() + } + + void points(Closure closure) { + closure.delegate = new BonusBlock(config) + closure.resolveStrategy = Closure.DELEGATE_ONLY + closure.call() + } + + void criteria(Closure closure) { + closure.delegate = new CriteriaBlock(config) + closure.resolveStrategy = Closure.DELEGATE_ONLY + closure.call() + } + + void check(Closure closure) { + closure.delegate = new CheckBlock(config) + closure.resolveStrategy = Closure.DELEGATE_ONLY + closure.call() + } + + void settings(Closure closure) { + closure.delegate = new SettingsBlock(config) + closure.resolveStrategy = Closure.DELEGATE_ONLY + closure.call() + } + + void importConfig(String path) { + def file = new File(path) + if (!file.exists()) { + System.err.println("[ERROR] Config file not found: $path") + return + } + + def cc = new CompilerConfiguration() + cc.setScriptBaseClass(DelegatingScript.class.getName()) + + def shell = new GroovyShell(this.class.classLoader, new Binding(), cc) + + DelegatingScript script = (DelegatingScript) shell.parse(file) + script.setDelegate(this) + script.run() + } + + Config build() { + return config + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/CriteriaBlock.groovy b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/CriteriaBlock.groovy new file mode 100644 index 0000000..8738a5e --- /dev/null +++ b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/CriteriaBlock.groovy @@ -0,0 +1,13 @@ +package ru.nsu.nmashkin.task241.dsl + +import ru.nsu.nmashkin.task241.core.Config + +class CriteriaBlock { + private final Config config + + CriteriaBlock(Config config) { this.config = config } + + def methodMissing(String name, args) { + config.addGradeCriteria(args[0] as int, name); + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/GroupBlock.groovy b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/GroupBlock.groovy new file mode 100644 index 0000000..072b725 --- /dev/null +++ b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/GroupBlock.groovy @@ -0,0 +1,20 @@ +package ru.nsu.nmashkin.task241.dsl + +import ru.nsu.nmashkin.task241.core.Group +import ru.nsu.nmashkin.task241.core.Student + +class GroupBlock { + private final Group group + + GroupBlock(Group group) { this.group = group } + + void student(String githubNick, String fullName, String repoUrl) { + group.addStudent(new Student(githubNick, fullName, repoUrl)) + } + + def methodMissing(String name, args) { + if (args.length == 2) { + group.addStudent(new Student(name, args[0], args[1])); + } + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/SettingsBlock.groovy b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/SettingsBlock.groovy new file mode 100644 index 0000000..04b0a4c --- /dev/null +++ b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/SettingsBlock.groovy @@ -0,0 +1,23 @@ +package ru.nsu.nmashkin.task241.dsl + +import ru.nsu.nmashkin.task241.core.Config + +class SettingsBlock { + private final Config config + + SettingsBlock(Config config) { this.config = config; } + + void methodMissing(String name, String... args) { + if (name.equals("testTimeout")) { + if (args[1].endsWith('s')) { + config.setTestTimeoutSeconds(args[1].dropRight(1).toLong()) + } + if (args[1].endsWith('m')) { + config.setTestTimeoutSeconds(args[1].dropRight(1).toLong() * 60) + } + } + if (name.equals("skipAuthCheck")) { + config.setSkipAuthCheck(args[1].toBoolean()); + } + } +} diff --git a/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/TasksBlock.groovy b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/TasksBlock.groovy new file mode 100644 index 0000000..db44d20 --- /dev/null +++ b/Task_2_4_1/src/main/groovy/ru/nsu/nmashkin/task241/dsl/TasksBlock.groovy @@ -0,0 +1,27 @@ +package ru.nsu.nmashkin.task241.dsl + +import ru.nsu.nmashkin.task241.core.Config +import ru.nsu.nmashkin.task241.core.Task + +import java.time.LocalDate +import java.time.format.DateTimeFormatter + +class TasksBlock { + private final Config config + private static final DateTimeFormatter DF = DateTimeFormatter.ofPattern("dd.MM.yyyy") + + TasksBlock(Config config) { this.config = config } + + void task(String id, String name, Integer maxPoints, + String softDeadline, String hardDeadline) { + config.addTask(new Task(id, name, maxPoints, + LocalDate.parse(softDeadline, DF), LocalDate.parse(hardDeadline, DF))) + } + + def methodMissing(String name, args) { + if (args.length == 4) { + config.addTask(new Task(name, args[0] as String, args[1] as int, + LocalDate.parse(args[2] as String, DF), LocalDate.parse(args[3] as String, DF))) + } + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/Main.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/Main.java new file mode 100644 index 0000000..ed71b45 --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/Main.java @@ -0,0 +1,156 @@ +package ru.nsu.nmashkin.task241; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import ru.nsu.nmashkin.task241.cli.ConfigLoader; +import ru.nsu.nmashkin.task241.core.Check; +import ru.nsu.nmashkin.task241.core.Config; +import ru.nsu.nmashkin.task241.core.Student; +import ru.nsu.nmashkin.task241.core.Task; +import ru.nsu.nmashkin.task241.service.BuildService; +import ru.nsu.nmashkin.task241.service.DocGenerator; +import ru.nsu.nmashkin.task241.service.GitService; +import ru.nsu.nmashkin.task241.service.HtmlReportGenerator; +import ru.nsu.nmashkin.task241.service.ScoreCalculator; +import ru.nsu.nmashkin.task241.service.StyleChecker; +import ru.nsu.nmashkin.task241.service.TaskVerificationResult; +import ru.nsu.nmashkin.task241.service.TestService; + +/** + * . + */ +public class Main { + + /** + * . + * + * @param build . + * @param docs . + * @param style . + * @param tests . + * @param score . + */ + public record TaskCheckResult(boolean build, boolean docs, boolean style, + TestService.TestResult tests, ScoreCalculator.ScoreResult score) { + static TaskCheckResult missing() { + return new TaskCheckResult(false, false, false, + new TestService.TestResult(0,0,0,false), + new ScoreCalculator.ScoreResult(0.0, "0", 0)); + } + } + + /** + * . + * + * @param student . + * @param taskId . + * @param taskResult . + * @param activityPercent . + */ + public record StudentData(Student student, String taskId, TaskCheckResult taskResult, double activityPercent) {} + + /** + * . + * + * @param args . + * @throws Exception . + */ + public static void main(String[] args) throws Exception { + String configPath = args.length > 0 ? args[0] : "config/test_check.groovy"; + System.err.println("[INFO] Loading config: " + new File(configPath).getAbsolutePath()); + Config config = ConfigLoader.load(new File(configPath)); + + GitService git = new GitService(); + BuildService build = new BuildService(); + TestService test = new TestService(); + StyleChecker style = new StyleChecker(); + DocGenerator docs = new DocGenerator(); + ScoreCalculator scorer = new ScoreCalculator(); + + Set results = new HashSet<>(); + Map gitCloned = new HashMap<>(); + + for (Check check : config.getChecks()) { + Student student = config.getStudent(check.studentNick()); + Task task = config.getTasks().get(check.taskId()); + + System.err.println("[INFO] Processing: " + student.name() + " (" + task.name() + ")"); + Path tempDir; + Path repoDir; + + try { + tempDir = gitCloned.getOrDefault(student.nick(), null); + if (tempDir == null) { + System.err.println("[INFO] Cloning repo \"" + student.url() + "\""); + tempDir = Files.createTempDirectory("oop-" + student.nick()); + repoDir = git.cloneRepository(student.url(), tempDir, "default"); + gitCloned.put(student.nick(), tempDir); + } else { + repoDir = tempDir.resolve("repo"); + } + + Path taskDir = repoDir.resolve(task.id()); + + if (!Files.exists(taskDir)) { + results.add(new StudentData(student, task.id(), + TaskCheckResult.missing(), 100)); + System.err.println("[ERROR] Project not found: " + task.id()); + continue; + } + + System.err.println("[INFO] Building project: " + task.id()); + boolean buildOk = build.compileJava(taskDir); + boolean docsOk = false; + boolean styleOk = false; + + if (buildOk) { + System.err.println("[INFO] Generating doc: " + task.id()); + docsOk = docs.generateDocs(taskDir); + System.err.println("[INFO] Checking style: " + task.id()); + styleOk = style.checkStyle(taskDir); + } + + TestService.TestResult testRes; + if (buildOk && docsOk && styleOk) { + System.err.println("[INFO] Running tests: " + task.id()); + testRes = test.runTests(taskDir, config.getTestTimeoutSeconds()); + } else { + testRes = new TestService.TestResult(0, 0, 0, false); + } + + System.err.println("[INFO] Calculating score: " + task.id()); + ScoreCalculator.ScoreResult score = scorer.calculate(task, + new TaskVerificationResult(buildOk, docsOk, styleOk, testRes, null), + student.nick(), config); + + results.add(new StudentData(student, task.id(), + new TaskCheckResult(buildOk, docsOk, styleOk, testRes, score), + 100)); + System.err.println("[INFO] Score: " + score.numericScore()); + + } catch (Exception e) { + System.err.println("[ERROR] Fatal error: " + e.getMessage()); + } + + } + + for (Path tempDir : gitCloned.values()) { + try { + if (tempDir != null) { + git.cleanup(tempDir); + } + } catch (Exception ignored) { + + } + } + + System.err.println("[INFO] Generating HTML report..."); + HtmlReportGenerator htmlReportGenerator = new HtmlReportGenerator(); + htmlReportGenerator.generate(config, results, System.out); + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/cli/ConfigLoader.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/cli/ConfigLoader.java new file mode 100644 index 0000000..6a05beb --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/cli/ConfigLoader.java @@ -0,0 +1,43 @@ +package ru.nsu.nmashkin.task241.cli; + +import groovy.lang.GroovyShell; +import groovy.util.DelegatingScript; +import java.io.File; +import java.io.IOException; +import org.codehaus.groovy.control.CompilerConfiguration; +import ru.nsu.nmashkin.task241.core.Config; +import ru.nsu.nmashkin.task241.dsl.ConfigBuilder; + +/** + * . + */ +public class ConfigLoader { + /** + * . + * + * @param configFile . + * @return . + * @throws IOException . + */ + public static Config load(File configFile) throws IOException { + if (!configFile.exists()) { + throw new RuntimeException("Config not found: " + configFile.getAbsolutePath()); + } + + CompilerConfiguration cc = new CompilerConfiguration(); + cc.setScriptBaseClass(DelegatingScript.class.getName()); + + GroovyShell shell = new GroovyShell( + ConfigBuilder.class.getClassLoader(), + new groovy.lang.Binding(), cc); + + DelegatingScript script = (DelegatingScript) shell.parse(configFile); + + ConfigBuilder builder = new ConfigBuilder(); + script.setDelegate(builder); + + script.run(); + + return builder.build(); + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Check.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Check.java new file mode 100644 index 0000000..c8f011e --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Check.java @@ -0,0 +1,9 @@ +package ru.nsu.nmashkin.task241.core; + +/** + * . + * + * @param taskId . + * @param studentNick . + */ +public record Check(String taskId, String studentNick) {} diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Checkpoint.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Checkpoint.java new file mode 100644 index 0000000..055b357 --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Checkpoint.java @@ -0,0 +1,11 @@ +package ru.nsu.nmashkin.task241.core; + +import java.time.LocalDate; + +/** + * . + * + * @param id . + * @param date . + */ +public record Checkpoint(String id, LocalDate date) {} diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Config.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Config.java new file mode 100644 index 0000000..ec1081b --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Config.java @@ -0,0 +1,201 @@ +package ru.nsu.nmashkin.task241.core; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * . + */ +public class Config { + private final Map tasks = new HashMap<>(); + private final Map groups = new HashMap<>(); + private final List checkpoints = new ArrayList<>(); + private final List checks = new ArrayList<>(); + private final Map bonusPoints = new HashMap<>(); + private final Map gradeCriteria = new TreeMap<>(Collections.reverseOrder()); + + private long testTimeoutSeconds = 30; + private boolean skipAuthCheck = false; + + /** + * . + * + * @param task . + */ + public void addTask(Task task) { + tasks.put(task.id(), task); + } + + /** + * . + * + * @param group . + */ + public void addGroup(Group group) { + groups.put(group.name(), group); + } + + /** + * . + * + * @param cp . + */ + public void addCheckpoint(Checkpoint cp) { + checkpoints.add(cp); + } + + /** + * . + * + * @param cmd . + */ + public void addCheckCommand(Check cmd) { + checks.add(cmd); + } + + /** + * . + * + * @param studentNick . + * @param points . + */ + public void addBonus(String studentNick, int points) { + bonusPoints.put(studentNick, points); + } + + /** + * . + * + * @param minScore . + * @param grade . + */ + public void addGradeCriteria(int minScore, String grade) { + gradeCriteria.put(minScore, grade); + } + + /** + * . + * + * @param timeoutSeconds . + */ + public void setTestTimeoutSeconds(long timeoutSeconds) { + testTimeoutSeconds = timeoutSeconds; + } + + /** + * . + * + * @return . + */ + public Map getTasks() { + return tasks; + } + + /** + * . + * + * @return . + */ + public Map getGroups() { + return groups; + } + + /** + * . + * + * @return . + */ + public List getCheckpoints() { + return checkpoints; + } + + /** + * . + * + * @return . + */ + public List getChecks() { + return checks; + } + + /** + * . + * + * @return . + */ + public Map getBonusPoints() { + return bonusPoints; + } + + /** + * . + * + * @return . + */ + public Map getGradeCriteria() { + return gradeCriteria; + } + + /** + * . + * + * @return . + */ + public long getTestTimeoutSeconds() { + return testTimeoutSeconds; + } + + /** + * . + * + * @return . + */ + public boolean skipAuthCheck() { + return skipAuthCheck; + } + + /** + * . + * + * @param skipAuthCheck . + */ + public void setSkipAuthCheck(boolean skipAuthCheck) { + this.skipAuthCheck = skipAuthCheck; + } + + /** + * . + * + * @param nick . + * @return . + */ + public Student getStudent(String nick) { + return groups.values().stream() + .flatMap(g -> g.students().stream()) + .filter(s -> s.nick().equals(nick)) + .findAny().orElse(null); + } + + /** + * . + * + * @return . + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder("\n=== Parsed Configuration ===\n"); + sb.append("Tasks: ").append(tasks.keySet()).append("\n"); + sb.append("Groups: ").append(groups.keySet()).append("\n"); + sb.append("Checkpoints: ").append(checkpoints).append("\n"); + sb.append("Checks: ").append(checks.size()).append(" command(s)\n"); + for (Check c : checks) + sb.append(" -> ").append(c.taskId()).append(" / ").append(c.studentNick()).append("\n"); + sb.append("Bonuses: ").append(bonusPoints).append("\n"); + sb.append("Criteria: ").append(gradeCriteria).append("\n"); + return sb.toString(); + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Group.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Group.java new file mode 100644 index 0000000..86f3943 --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Group.java @@ -0,0 +1,30 @@ +package ru.nsu.nmashkin.task241.core; + +import java.util.ArrayList; +import java.util.List; + +/** + * . + * + * @param name . + * @param students . + */ +public record Group(String name, List students) { + /** + * . + * + * @param name . + */ + public Group(String name) { + this(name, new ArrayList<>()); + } + + /** + * . + * + * @param student . + */ + public void addStudent(Student student) { + students.add(student); + } +} diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Student.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Student.java new file mode 100644 index 0000000..30bf7b9 --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Student.java @@ -0,0 +1,10 @@ +package ru.nsu.nmashkin.task241.core; + +/** + * . + * + * @param nick . + * @param name . + * @param url . + */ +public record Student(String nick, String name, String url) {} diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Task.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Task.java new file mode 100644 index 0000000..2587c0c --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Task.java @@ -0,0 +1,15 @@ +package ru.nsu.nmashkin.task241.core; + +import java.time.LocalDate; + +/** + * . + * + * @param id . + * @param name . + * @param maxScore . + * @param softDeadline . + * @param hardDeadline . + */ +public record Task(String id, String name, int maxScore, + LocalDate softDeadline, LocalDate hardDeadline) {} \ No newline at end of file diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/BuildService.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/BuildService.java new file mode 100644 index 0000000..7fcd2c4 --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/BuildService.java @@ -0,0 +1,24 @@ +package ru.nsu.nmashkin.task241.service; + +import java.nio.file.Path; + +/** + * . + */ +public class BuildService { + /** + * . + * + * @param taskDir . + * @return . + */ + public boolean compileJava(Path taskDir) { + try { + GradleExecutor.run(taskDir, "compileJava", 0); + return true; + } catch (Exception e) { + System.err.println("[ERROR] Build failed: " + e.getMessage()); + return false; + } + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/DocGenerator.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/DocGenerator.java new file mode 100644 index 0000000..4ec87f2 --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/DocGenerator.java @@ -0,0 +1,24 @@ +package ru.nsu.nmashkin.task241.service; + +import java.nio.file.Path; + +/** + * . + */ +public class DocGenerator { + /** + * . + * + * @param taskDir . + * @return . + */ + public boolean generateDocs(Path taskDir) { + try { + GradleExecutor.run(taskDir, "javadoc", 60); + return true; + } catch (Exception e) { + System.err.println("[ERROR] Javadoc generation failed"); + return false; + } + } +} diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GitService.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GitService.java new file mode 100644 index 0000000..44f7a3f --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GitService.java @@ -0,0 +1,102 @@ +package ru.nsu.nmashkin.task241.service; // Замените на ваш пакет + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.concurrent.TimeUnit; + +/** + * . + */ +public class GitService { + /** + * . + * + * @param repoUrl . + * @param targetDir . + * @param branch . + * @return . + * @throws IOException . + * @throws InterruptedException . + */ + public Path cloneRepository(String repoUrl, Path targetDir, String branch) throws IOException, InterruptedException { + if (!Files.exists(targetDir)) Files.createDirectories(targetDir); + + String[] branchesToTry = branch.equalsIgnoreCase("default") + ? new String[]{"main", "master"} + : new String[]{branch}; + + for (String b : branchesToTry) { + Path cloneDir = targetDir.resolve("repo"); + if (tryClone(repoUrl, b, cloneDir)) { + return cloneDir; + } + + cleanup(cloneDir); + } + throw new IOException("Failed to clone repo. Neither 'main' nor 'master' found or accessible."); + } + + private boolean tryClone(String url, String branch, Path dir) { + try { + ProcessBuilder pb = new ProcessBuilder( + "git", "clone", "--depth", "1", "--single-branch", + "--branch", branch, url, dir.toString() + ); + pb.redirectErrorStream(true); + Process p = pb.start(); + + try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) { + while (br.readLine() != null); + } + return p.waitFor() == 0; + } catch (Exception e) { + return false; + } + } + + /** + * . + * + * @param repoPath . + * @param start . + * @param end . + * @return . + * @throws IOException . + * @throws InterruptedException . + */ + public boolean hasCommitBetween(Path repoPath, LocalDateTime start, LocalDateTime end) throws IOException, InterruptedException { + ProcessBuilder pb = new ProcessBuilder( + "git", "-C", repoPath.toString(), "log", + "--pretty=format:%H", + "--since", start.toString(), + "--until", end.toString() + ); + Process p = pb.start(); + try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) { + boolean hasCommit = br.readLine() != null; + p.waitFor(5, TimeUnit.SECONDS); + return hasCommit; + } + } + + /** + * . + * + * @param repoPath . + * @throws IOException . + */ + public void cleanup(Path repoPath) throws IOException { + if (repoPath == null || !Files.exists(repoPath)) return; + try (var stream = Files.walk(repoPath)) { + stream.sorted(java.util.Comparator.reverseOrder()) + .forEach(p -> { + try { Files.delete(p); } catch (IOException ignored) {} + }); + } + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GradleExecutor.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GradleExecutor.java new file mode 100644 index 0000000..12ed061 --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GradleExecutor.java @@ -0,0 +1,78 @@ +package ru.nsu.nmashkin.task241.service; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * . + */ +public class GradleExecutor { + + /** + * . + * + * @param projectDir . + * @param task . + * @param timeoutSec . + * @return . + * @throws IOException . + * @throws InterruptedException . + */ + public static boolean run(Path projectDir, String task, long timeoutSec) throws IOException, InterruptedException { + List command = buildGradleCommand(projectDir, task); + + ProcessBuilder pb = new ProcessBuilder(command); + pb.directory(projectDir.toFile()); + pb.redirectErrorStream(true); + + Process proc = pb.start(); + + StringBuilder output = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append(System.lineSeparator()); + } + } + + boolean finished = timeoutSec > 0 + ? proc.waitFor(timeoutSec, TimeUnit.SECONDS) + : proc.waitFor() == 0; + if (!finished) { + proc.destroyForcibly(); + System.err.println("[ERROR] Task \"" + task + "\" timed out after " + timeoutSec + "s"); + return true; + } + + if (proc.exitValue() != 0) { + System.err.println("[ERROR] Task \"" + task + "'\" failed with code " + proc.exitValue() + "\n" + output); + } + return false; + } + + private static List buildGradleCommand(Path dir, String task) { + boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win"); + Path wrapper = dir.resolve(isWindows ? "gradlew.bat" : "gradlew"); + + List cmd = new ArrayList<>(); + + if (Files.exists(wrapper)) { + if (isWindows) { + cmd.addAll(List.of("cmd", "/c", "gradlew.bat")); + } else { + cmd.add("./gradlew"); + } + } else { + cmd.add("gradle"); + } + + cmd.addAll(List.of(task, "--rerun-tasks", "--no-daemon", "--console=plain", "-q")); + return cmd; + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java new file mode 100644 index 0000000..410c24c --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java @@ -0,0 +1,94 @@ +package ru.nsu.nmashkin.task241.service; + +import ru.nsu.nmashkin.task241.Main; +import ru.nsu.nmashkin.task241.core.*; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.*; +import java.util.stream.Collectors; + +public class HtmlReportGenerator { + + /** + * Генерирует HTML-отчёт в требуемом формате: + * одна таблица на группу, строки = (студент, задача) из checks. + */ + public void generate(Config config, Set results, Appendable output) throws IOException { + output.append("") + .append("\n"); + + // Группируем результаты по группам + Map> byGroup = results.stream() + .collect(Collectors.groupingBy( + d -> config.getGroups().values().stream() + .filter(g -> g.students().contains(d.student())) + .findFirst() + .orElse(null), + LinkedHashMap::new, + Collectors.toList() + )); + + for (Map.Entry> entry : byGroup.entrySet()) { + Group group = entry.getKey(); + if (group == null) continue; + + List groupResults = entry.getValue(); + + output.append("

Group ").append(escapeHtml(group.name())).append("

\n") + .append("") + .append("") + .append("\n"); + + // Сортируем: сначала по студенту, потом по задаче (для читаемости) + groupResults.stream() + .sorted(Comparator + .comparing((Main.StudentData d) -> d.student().name()) + .thenComparing(d -> d.taskId())) + .forEach(data -> { + try { + Main.TaskCheckResult res = data.taskResult(); + System.err.println(res); + String build = res.build() ? "ok" : "fail"; + String docs = res.docs() ? "ok" : "fail"; + String style = res.style() ? "ok" : "fail"; + String tests = res.tests() != null ? res.tests().toString() : "-"; + String total = String.format("%.1f / %d", + res.score().numericScore(), + config.getTasks().get(data.taskId()).maxScore()); + + output.append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("\n"); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + + output.append("
StudentTaskBuildDocStyleTestsTotal
").append(escapeHtml(data.student().name())).append("").append(escapeHtml(data.taskId())).append("").append(build).append("").append(docs).append("").append(style).append("").append(tests).append("").append(total).append("
\n"); + } + + output.append(""); + } + + private String escapeHtml(String text) { + if (text == null) return ""; + return text.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """) + .replace("'", "'"); + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/ScoreCalculator.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/ScoreCalculator.java new file mode 100644 index 0000000..bd486d3 --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/ScoreCalculator.java @@ -0,0 +1,66 @@ +package ru.nsu.nmashkin.task241.service; + +import ru.nsu.nmashkin.task241.core.Config; +import ru.nsu.nmashkin.task241.core.Task; + +import java.util.Map; + +/** + * . + */ +public class ScoreCalculator { + + /** + * . + * + * @param numericScore . + * @param grade . + * @param bonusApplied . + */ + public record ScoreResult(double numericScore, String grade, int bonusApplied) {} + + /** + * . + * + * @param task . + * @param verification . + * @param studentNick . + * @param config . + * @return . + */ + public ScoreResult calculate(Task task, TaskVerificationResult verification, + String studentNick, Config config) { + + if (!verification.buildOk() || !verification.docsOk() || !verification.styleOk()) { + return new ScoreResult(0.0, "0", 0); + } + + double baseScore = 0.0; + TestService.TestResult testRes = verification.tests(); + if (testRes != null) { + int total = testRes.passed() + testRes.failed() + testRes.skipped(); + if (total > 0) { + baseScore = ((double) testRes.passed() / total) * task.maxScore(); + } + } + + int bonus = config.getBonusPoints().getOrDefault(studentNick, 0); + double finalScore = Math.max(0.0, baseScore + bonus); + + String grade = mapToGrade(finalScore, config.getGradeCriteria()); + + return new ScoreResult(finalScore, grade, bonus); + } + + private String mapToGrade(double score, Map criteria) { + String result = "2"; + int maxMetCriteria = 0; + for (var entry : criteria.entrySet()) { + if (entry.getKey() >= maxMetCriteria && score >= entry.getKey()) { + maxMetCriteria = entry.getKey(); + result = entry.getValue(); + } + } + return result; + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/StyleChecker.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/StyleChecker.java new file mode 100644 index 0000000..7546c34 --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/StyleChecker.java @@ -0,0 +1,121 @@ +package ru.nsu.nmashkin.task241.service; + +import java.io.*; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.*; +import java.util.concurrent.TimeUnit; + +/** + * . + */ +public class StyleChecker { + + private static final String CHECKSTYLE_JAR_URL = + "https://github.com/checkstyle/checkstyle/releases/download/checkstyle-10.17.0/checkstyle-10.17.0-all.jar"; + private static final String JAR_NAME = "checkstyle-10.17.0-all.jar"; + private final Path cacheDir; + + /** + * . + */ + public StyleChecker() { + this.cacheDir = Path.of(System.getProperty("user.home"), ".oop-checker", "tools"); + } + + /** + * . + * + * @param taskDir . + * @return . + */ + public boolean checkStyle(Path taskDir) { + try { + Path jarPath = downloadTool(); + Path configPath = extractConfigFromResources(); + Path src = taskDir.resolve("src"); + + if (!Files.exists(src)) { + System.err.println("[ERROR] \"src\" not found"); + return false; + } + + return runCheckstyle(jarPath, configPath, src); + } catch (Exception e) { + System.err.println("[ERROR] Style check failed: " + e.getMessage()); + return false; + } + } + + private Path downloadTool() throws IOException { + Files.createDirectories(cacheDir); + Path jarPath = cacheDir.resolve(JAR_NAME); + + if (!Files.exists(jarPath)) { + System.err.println("[INFO] Downloading checkstyle..."); + try (InputStream in = new URL(CHECKSTYLE_JAR_URL).openStream()) { + Files.copy(in, jarPath, StandardCopyOption.REPLACE_EXISTING); + } + } + return jarPath; + } + + private Path extractConfigFromResources() throws IOException, URISyntaxException { + URL url = getClass().getClassLoader().getResource("google_style.xml"); + if (url == null) { + throw new IOException("No google_style.xml found in resources"); + } + + String urlPath = url.toString(); + if (urlPath.contains("!")) { + Path tempConfig = Files.createTempFile("checkstyle-config-", ".xml"); + try (InputStream in = url.openStream()) { + Files.copy(in, tempConfig, StandardCopyOption.REPLACE_EXISTING); + } + tempConfig.toFile().deleteOnExit(); + return tempConfig; + } + return Path.of(url.toURI()); + } + + private boolean runCheckstyle(Path jarPath, Path configPath, Path srcDir) throws IOException, InterruptedException { + ProcessBuilder pb = new ProcessBuilder( + "java", "-jar", jarPath.toString(), + "-c", configPath.toAbsolutePath().toString(), + srcDir.toAbsolutePath().toString() + ); + pb.redirectErrorStream(true); + Process proc = pb.start(); + + StringBuilder output = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) output.append(line).append("\n"); + } + + boolean finished = proc.waitFor(60, TimeUnit.SECONDS); + if (!finished) { + proc.destroyForcibly(); + System.err.println("[ERROR] Checkstyle timed out after 60s"); + return false; + } + + if (proc.exitValue() == 0) { + System.err.println("[INFO] Checkstyle: PASSED"); + return true; + } + + System.err.println("[WARNING] Style violations found:"); + String[] lines = output.toString().split("\n"); + int count = 0; + for (String line : lines) { + if (line.contains("[ERROR]") || line.contains("[WARN]")) { + String clean = line.replaceFirst("^.+\\b(src[/\\\\]main[/\\\\]java)", "$1"); + System.err.println(" " + clean); + count++; + } + } + System.err.println(" Total violations: " + count); + return false; + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/TaskVerificationResult.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/TaskVerificationResult.java new file mode 100644 index 0000000..07048c6 --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/TaskVerificationResult.java @@ -0,0 +1,14 @@ +package ru.nsu.nmashkin.task241.service; + +/** + * . + * + * @param buildOk . + * @param docsOk . + * @param styleOk . + * @param tests . + * @param error . + */ +public record TaskVerificationResult( + boolean buildOk, boolean docsOk, boolean styleOk, + TestService.TestResult tests, String error) {} diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/TestService.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/TestService.java new file mode 100644 index 0000000..3339746 --- /dev/null +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/TestService.java @@ -0,0 +1,74 @@ +package ru.nsu.nmashkin.task241.service; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * . + */ +public class TestService { + + /** + * . + * + * @param passed . + * @param failed . + * @param skipped . + * @param timeout . + */ + public record TestResult(int passed, int failed, int skipped, boolean timeout) { + @Override + public String toString() { + return String.format("%d/%d/%d", passed, failed, skipped); + } + } + + /** + * . + * + * @param taskDir . + * @param timeoutSec . + * @return . + */ + public TestResult runTests(Path taskDir, long timeoutSec) { + try { + if (GradleExecutor.run(taskDir, "test", timeoutSec)) { + System.out.println("[INFO] Test execution timed out"); + return new TestResult(0, 0, 0, true); + } + return parseHtmlReport(taskDir); + } catch (Exception e) { + System.err.println("[ERROR] Test execution failed"); + return new TestResult(0, 0, 0, false); + } + } + + private TestResult parseHtmlReport(Path taskDir) throws IOException { + String html = Files.readString(taskDir.resolve("build/reports/tests/test/index.html")); + + int tests = extractBySelector(html, "id=\"tests\""); + int failures = extractBySelector(html, "id=\"failures\""); + int ignored = extractBySelector(html, "id=\"ignored\""); + + return new TestResult(Math.max(0, tests - failures - ignored), failures, ignored, false); + } + + private int extractBySelector(String html, String infoBoxSelector) { + int start = html.indexOf(infoBoxSelector); + if (start == -1) return 0; + + int counterStart = html.indexOf("
", start); + if (counterStart == -1) return 0; + + int valueStart = counterStart + "
".length(); + int valueEnd = html.indexOf("
", valueStart); + if (valueEnd == -1) return 0; + + try { + return Integer.parseInt(html.substring(valueStart, valueEnd).trim()); + } catch (NumberFormatException e) { + return 0; + } + } +} \ No newline at end of file diff --git a/Task_2_4_1/src/main/resources/META-INF/MANIFEST.MF b/Task_2_4_1/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..c7f58a6 --- /dev/null +++ b/Task_2_4_1/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: ru.nsu.nmashkin.task241.Main + diff --git a/Task_2_4_1/src/main/resources/google_style.xml b/Task_2_4_1/src/main/resources/google_style.xml new file mode 100644 index 0000000..c63eb28 --- /dev/null +++ b/Task_2_4_1/src/main/resources/google_style.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 5de3df0af290d0da430e2e2782fcca353915f103 Mon Sep 17 00:00:00 2001 From: LookAsLukas Date: Sat, 25 Apr 2026 22:46:01 +0700 Subject: [PATCH 2/4] A ISHO PISAT TESTI --- .../java/ru/nsu/nmashkin/task241/Main.java | 13 ++-- .../ru/nsu/nmashkin/task241/core/Config.java | 6 +- .../nmashkin/task241/service/GitService.java | 57 +++++++----------- .../task241/service/GradleExecutor.java | 10 +++- .../task241/service/HtmlReportGenerator.java | 59 +++++++++++++------ .../task241/service/ScoreCalculator.java | 3 +- .../task241/service/StyleChecker.java | 22 +++++-- .../nmashkin/task241/service/TestService.java | 18 ++++-- 8 files changed, 106 insertions(+), 82 deletions(-) diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/Main.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/Main.java index ed71b45..c5166ec 100644 --- a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/Main.java +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/Main.java @@ -39,7 +39,7 @@ public record TaskCheckResult(boolean build, boolean docs, boolean style, TestService.TestResult tests, ScoreCalculator.ScoreResult score) { static TaskCheckResult missing() { return new TaskCheckResult(false, false, false, - new TestService.TestResult(0,0,0,false), + new TestService.TestResult(0, 0, 0, false), new ScoreCalculator.ScoreResult(0.0, "0", 0)); } } @@ -52,7 +52,8 @@ static TaskCheckResult missing() { * @param taskResult . * @param activityPercent . */ - public record StudentData(Student student, String taskId, TaskCheckResult taskResult, double activityPercent) {} + public record StudentData(Student student, String taskId, + TaskCheckResult taskResult, double activityPercent) {} /** * . @@ -140,13 +141,7 @@ public static void main(String[] args) throws Exception { } for (Path tempDir : gitCloned.values()) { - try { - if (tempDir != null) { - git.cleanup(tempDir); - } - } catch (Exception ignored) { - - } + git.cleanup(tempDir); } System.err.println("[INFO] Generating HTML report..."); diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Config.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Config.java index ec1081b..487e01f 100644 --- a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Config.java +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/core/Config.java @@ -192,8 +192,10 @@ public String toString() { sb.append("Groups: ").append(groups.keySet()).append("\n"); sb.append("Checkpoints: ").append(checkpoints).append("\n"); sb.append("Checks: ").append(checks.size()).append(" command(s)\n"); - for (Check c : checks) - sb.append(" -> ").append(c.taskId()).append(" / ").append(c.studentNick()).append("\n"); + for (Check c : checks) { + sb.append(" -> ").append(c.taskId()).append(" / ") + .append(c.studentNick()).append("\n"); + } sb.append("Bonuses: ").append(bonusPoints).append("\n"); sb.append("Criteria: ").append(gradeCriteria).append("\n"); return sb.toString(); diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GitService.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GitService.java index 44f7a3f..7022cb9 100644 --- a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GitService.java +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GitService.java @@ -5,9 +5,6 @@ import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.concurrent.TimeUnit; /** * . @@ -21,10 +18,13 @@ public class GitService { * @param branch . * @return . * @throws IOException . - * @throws InterruptedException . */ - public Path cloneRepository(String repoUrl, Path targetDir, String branch) throws IOException, InterruptedException { - if (!Files.exists(targetDir)) Files.createDirectories(targetDir); + public Path cloneRepository(String repoUrl, + Path targetDir, + String branch) throws IOException { + if (!Files.exists(targetDir)) { + Files.createDirectories(targetDir); + } String[] branchesToTry = branch.equalsIgnoreCase("default") ? new String[]{"main", "master"} @@ -38,7 +38,8 @@ public Path cloneRepository(String repoUrl, Path targetDir, String branch) throw cleanup(cloneDir); } - throw new IOException("Failed to clone repo. Neither 'main' nor 'master' found or accessible."); + throw new IOException("Failed to clone repo." + + "Neither 'main' nor 'master' found or accessible."); } private boolean tryClone(String url, String branch, Path dir) { @@ -50,8 +51,11 @@ private boolean tryClone(String url, String branch, Path dir) { pb.redirectErrorStream(true); Process p = pb.start(); - try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) { - while (br.readLine() != null); + try (BufferedReader br = new BufferedReader( + new InputStreamReader(p.getInputStream()))) { + while (br.readLine() != null) { + + } } return p.waitFor() == 0; } catch (Exception e) { @@ -59,31 +63,6 @@ private boolean tryClone(String url, String branch, Path dir) { } } - /** - * . - * - * @param repoPath . - * @param start . - * @param end . - * @return . - * @throws IOException . - * @throws InterruptedException . - */ - public boolean hasCommitBetween(Path repoPath, LocalDateTime start, LocalDateTime end) throws IOException, InterruptedException { - ProcessBuilder pb = new ProcessBuilder( - "git", "-C", repoPath.toString(), "log", - "--pretty=format:%H", - "--since", start.toString(), - "--until", end.toString() - ); - Process p = pb.start(); - try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) { - boolean hasCommit = br.readLine() != null; - p.waitFor(5, TimeUnit.SECONDS); - return hasCommit; - } - } - /** * . * @@ -91,11 +70,17 @@ public boolean hasCommitBetween(Path repoPath, LocalDateTime start, LocalDateTim * @throws IOException . */ public void cleanup(Path repoPath) throws IOException { - if (repoPath == null || !Files.exists(repoPath)) return; + if (repoPath == null || !Files.exists(repoPath)) { + return; + } try (var stream = Files.walk(repoPath)) { stream.sorted(java.util.Comparator.reverseOrder()) .forEach(p -> { - try { Files.delete(p); } catch (IOException ignored) {} + try { + Files.delete(p); + } catch (IOException ignored) { + + } }); } } diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GradleExecutor.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GradleExecutor.java index 12ed061..344d2e8 100644 --- a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GradleExecutor.java +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GradleExecutor.java @@ -24,7 +24,9 @@ public class GradleExecutor { * @throws IOException . * @throws InterruptedException . */ - public static boolean run(Path projectDir, String task, long timeoutSec) throws IOException, InterruptedException { + public static boolean run(Path projectDir, + String task, + long timeoutSec) throws IOException, InterruptedException { List command = buildGradleCommand(projectDir, task); ProcessBuilder pb = new ProcessBuilder(command); @@ -34,7 +36,8 @@ public static boolean run(Path projectDir, String task, long timeoutSec) throws Process proc = pb.start(); StringBuilder output = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) { + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(proc.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { output.append(line).append(System.lineSeparator()); @@ -51,7 +54,8 @@ public static boolean run(Path projectDir, String task, long timeoutSec) throws } if (proc.exitValue() != 0) { - System.err.println("[ERROR] Task \"" + task + "'\" failed with code " + proc.exitValue() + "\n" + output); + System.err.println("[ERROR] Task \"" + task + + "'\" failed with code " + proc.exitValue() + "\n" + output); } return false; } diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java index 410c24c..d86422d 100644 --- a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java @@ -1,30 +1,45 @@ package ru.nsu.nmashkin.task241.service; -import ru.nsu.nmashkin.task241.Main; -import ru.nsu.nmashkin.task241.core.*; import java.io.IOException; import java.io.UncheckedIOException; -import java.util.*; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; +import ru.nsu.nmashkin.task241.Main; +import ru.nsu.nmashkin.task241.core.Config; +import ru.nsu.nmashkin.task241.core.Group; + +/** + * . + */ public class HtmlReportGenerator { /** - * Генерирует HTML-отчёт в требуемом формате: - * одна таблица на группу, строки = (студент, задача) из checks. + * . + * + * @param config . + * @param results . + * @param output . + * @throws IOException . */ - public void generate(Config config, Set results, Appendable output) throws IOException { + public void generate(Config config, + Set results, + Appendable output) throws IOException { output.append("") .append("\n"); - // Группируем результаты по группам Map> byGroup = results.stream() .collect(Collectors.groupingBy( d -> config.getGroups().values().stream() @@ -37,20 +52,22 @@ public void generate(Config config, Set results, Appendable ou for (Map.Entry> entry : byGroup.entrySet()) { Group group = entry.getKey(); - if (group == null) continue; + if (group == null) { + continue; + } List groupResults = entry.getValue(); output.append("

Group ").append(escapeHtml(group.name())).append("

\n") .append("") - .append("") + .append("" + + "") .append("\n"); - // Сортируем: сначала по студенту, потом по задаче (для читаемости) groupResults.stream() .sorted(Comparator .comparing((Main.StudentData d) -> d.student().name()) - .thenComparing(d -> d.taskId())) + .thenComparing(Main.StudentData::taskId)) .forEach(data -> { try { Main.TaskCheckResult res = data.taskResult(); @@ -64,11 +81,15 @@ public void generate(Config config, Set results, Appendable ou config.getTasks().get(data.taskId()).maxScore()); output.append("") - .append("") - .append("") - .append("") - .append("") - .append("") + .append("") .append("") + .append("") + .append("") + .append("") .append("") .append("") .append("\n"); @@ -84,7 +105,9 @@ public void generate(Config config, Set results, Appendable ou } private String escapeHtml(String text) { - if (text == null) return ""; + if (text == null) { + return ""; + } return text.replace("&", "&") .replace("<", "<") .replace(">", ">") diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/ScoreCalculator.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/ScoreCalculator.java index bd486d3..247fc02 100644 --- a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/ScoreCalculator.java +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/ScoreCalculator.java @@ -1,10 +1,9 @@ package ru.nsu.nmashkin.task241.service; +import java.util.Map; import ru.nsu.nmashkin.task241.core.Config; import ru.nsu.nmashkin.task241.core.Task; -import java.util.Map; - /** * . */ diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/StyleChecker.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/StyleChecker.java index 7546c34..b5e916c 100644 --- a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/StyleChecker.java +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/StyleChecker.java @@ -1,9 +1,14 @@ package ru.nsu.nmashkin.task241.service; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URISyntaxException; import java.net.URL; -import java.nio.file.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.concurrent.TimeUnit; /** @@ -12,7 +17,8 @@ public class StyleChecker { private static final String CHECKSTYLE_JAR_URL = - "https://github.com/checkstyle/checkstyle/releases/download/checkstyle-10.17.0/checkstyle-10.17.0-all.jar"; + "https://github.com/checkstyle/checkstyle/releases/" + + "download/checkstyle-10.17.0/checkstyle-10.17.0-all.jar"; private static final String JAR_NAME = "checkstyle-10.17.0-all.jar"; private final Path cacheDir; @@ -78,7 +84,8 @@ private Path extractConfigFromResources() throws IOException, URISyntaxException return Path.of(url.toURI()); } - private boolean runCheckstyle(Path jarPath, Path configPath, Path srcDir) throws IOException, InterruptedException { + private boolean runCheckstyle(Path jarPath, Path configPath, Path srcDir) + throws IOException, InterruptedException { ProcessBuilder pb = new ProcessBuilder( "java", "-jar", jarPath.toString(), "-c", configPath.toAbsolutePath().toString(), @@ -88,9 +95,12 @@ private boolean runCheckstyle(Path jarPath, Path configPath, Path srcDir) throws Process proc = pb.start(); StringBuilder output = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) { + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(proc.getInputStream()))) { String line; - while ((line = reader.readLine()) != null) output.append(line).append("\n"); + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + } } boolean finished = proc.waitFor(60, TimeUnit.SECONDS); diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/TestService.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/TestService.java index 3339746..ae9d3de 100644 --- a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/TestService.java +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/TestService.java @@ -12,8 +12,8 @@ public class TestService { /** * . * - * @param passed . - * @param failed . + * @param passed . + * @param failed . * @param skipped . * @param timeout . */ @@ -27,7 +27,7 @@ public String toString() { /** * . * - * @param taskDir . + * @param taskDir . * @param timeoutSec . * @return . */ @@ -56,14 +56,20 @@ private TestResult parseHtmlReport(Path taskDir) throws IOException { private int extractBySelector(String html, String infoBoxSelector) { int start = html.indexOf(infoBoxSelector); - if (start == -1) return 0; + if (start == -1) { + return 0; + } int counterStart = html.indexOf("
", start); - if (counterStart == -1) return 0; + if (counterStart == -1) { + return 0; + } int valueStart = counterStart + "
".length(); int valueEnd = html.indexOf("
", valueStart); - if (valueEnd == -1) return 0; + if (valueEnd == -1) { + return 0; + } try { return Integer.parseInt(html.substring(valueStart, valueEnd).trim()); From 5f57f780efd66545b6455dadfd521e838d6d3112 Mon Sep 17 00:00:00 2001 From: LookAsLukas Date: Sat, 25 Apr 2026 22:51:16 +0700 Subject: [PATCH 3/4] CRINGE --- .../nmashkin/task241/service/GitService.java | 4 +-- .../task241/service/HtmlReportGenerator.java | 25 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GitService.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GitService.java index 7022cb9..6e2dc3d 100644 --- a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GitService.java +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/GitService.java @@ -78,8 +78,8 @@ public void cleanup(Path repoPath) throws IOException { .forEach(p -> { try { Files.delete(p); - } catch (IOException ignored) { - + } catch (IOException e) { + e.printStackTrace(); } }); } diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java index d86422d..89abaef 100644 --- a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java @@ -8,7 +8,6 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; - import ru.nsu.nmashkin.task241.Main; import ru.nsu.nmashkin.task241.core.Config; import ru.nsu.nmashkin.task241.core.Group; @@ -81,18 +80,18 @@ public void generate(Config config, config.getTasks().get(data.taskId()).maxScore()); output.append("
") - .append("") .append("") - .append("") - .append("") - .append("") - .append("") - .append("") - .append("\n"); + .append("") .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("\n"); } catch (IOException e) { throw new UncheckedIOException(e); } From ae8e2c4b2dcf07ab33af8916ebeed0a6482f8b72 Mon Sep 17 00:00:00 2001 From: LookAsLukas Date: Sat, 25 Apr 2026 22:52:25 +0700 Subject: [PATCH 4/4] CRINGE --- .../ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java index 89abaef..67de6f1 100644 --- a/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java +++ b/Task_2_4_1/src/main/java/ru/nsu/nmashkin/task241/service/HtmlReportGenerator.java @@ -81,7 +81,7 @@ public void generate(Config config, output.append("") .append("") .append("").append("") .append("")
StudentTaskBuildDocStyleTestsTotalStudentTaskBuildDocStyleTestsTotal
").append(escapeHtml(data.student().name())).append("").append(escapeHtml(data.taskId())).append("").append(build).append("").append(docs).append("").append(style).append("").append(escapeHtml(data.student().name())) + .append("") + .append(escapeHtml(data.taskId())).append("").append(build).append("").append(docs).append("").append(style).append("").append(tests).append("").append(total).append("
").append(escapeHtml(data.student().name())) - .append("") - .append(escapeHtml(data.taskId())).append("").append(build).append("").append(docs).append("").append(style).append("").append(tests).append("").append(total).append("
").append(escapeHtml(data.student().name())) + .append("") + .append(escapeHtml(data.taskId())).append("").append(build).append("").append(docs).append("").append(style).append("").append(tests).append("").append(total).append("
").append(escapeHtml(data.student().name())) - .append("") + .append("") .append(escapeHtml(data.taskId())).append("").append(build).append("