diff --git a/.gitignore b/.gitignore index edd2d06..6245947 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ .gradle/ .nb-gradle/ +.nb-gradle-properties /build/classes/* /build/tmp/* +/build/docs/* +/build/distributions/* !/build/libs/* gradle.properties \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..809e21c --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Matt Crow + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 6f3c396..aacba11 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,24 @@ -# AutoCADDrawingChecker -A simple, extendable Java program used to compare vector exports from AutoCAD. +# AutoCAD Drawing Checker +A simple, extendable Java program used to compare Excel exports from AutoCAD. ## Project Summary +Instructors and teachers in college classes are expected to teach their students by giving lectures and providing learning resources. +One of the most common elements of college classes is the use of homework to practice the concepts taught in class. +Of course, homework can be tedious and time consuming for the instructor to grade, and is an inefficient process when a single objective solution exists for any given problem. +Fortunately, computers are very good at doing tedious processes with objective solutions. + +This program is meant to compare data exports from AutoCAD to each other, grading them based upon how similar they both are. +These AutoCAD exports are in either XLS or XLSX format, which is easily parse-able by the Apache POI Library. + +## Required Software +* Users need only ensure they have [Java](https://java.com/en/) installed on their computer to run the application. +* Developers wishing to change this application for their own use will need [Gradle](https://gradle.org/) installed to build the application. ## How to Use the Application The application is stored in the JAR file under ```/build/libs/AutoCADDrawingChecker.jar```. This file is a self-contained application, so you can move it wherever you want on your computer: it doesn't rely on the other project files to run. Simply double-click the JAR file to run it. + ### Steps * Step 1: Choosing instructor and student files. The first page of the application has you choose at least 2 files: the instructor file, and 1 or more student files. The instructor file is what the student files will be graded on: @@ -14,15 +26,24 @@ The more similar their file is to the instructor file, the higher their grade wi * Choose 1 or more Excel files * Choose 1 or more folders. Note that the program locates all Excel files under this folder, so it's OK if it has other files in there, the program will just ignore them. * A combination of the above. -* Step 2: Choosing Grading Criteria. You can choose what the program grades students on by toggling these check boxes on or off. -* Step 3: Running the Autograder. Once the program is done grading, it will ask you where you want to save the grading report. -The program automatically adds an xlsx extension if you don't provide one, so no need to worry about that. +Also of interest: you can drag and drop files into the two selectors instead of clicking the button. +* Step 2: Choosing Grading Criteria. You can choose what the program grades students on by toggling these check boxes on or off. Regardless of what you select, the program will still grade every column in the instructor file. +* Step 3: Running the Autograder. (Don't forget to click 'Run'!) Once the program is done grading, it will ask you where you want to save the grading report. Simply choose a folder, and the program will automatically name the file for you. -## To Do -* Make AutoCADExcelParser easier to work with and extend. - Maybe have an ElementLoader which checks if it has an element type for each element name +### Troubleshooting +If anything goes wrong, and you are unsure what to do about it, you'll want to click Log -> Save Log in the program menu bar along the top. +You can contact Matt if you need help, and you provide him with the Log file you just created: This will help him figure out what went wrong. + +## Matt's To Do List * see AutoCADElementMatcher -* Should we match rows for each comparison, or for the export as a whole? +* Should we match rows for each comparison, or for the export as a whole? (wait on this) +* Given double X and double Y, don't do "X == Y", instead do "Math.abs(X - Y) < threshold" +* Look into Federal Section 508 + * https://www.epa.gov/accessibility/what-section-508 + * https://www.section508.gov/ + * https://www.section508.gov/create + * https://www.access-board.gov/guidelines-and-standards/communications-and-it/about-the-ict-refresh/final-rule/text-of-the-standards-and-guidelines + ## Helpful Links * [Excel Library](https://poi.apache.org/apidocs/4.1/) \ No newline at end of file diff --git a/build.gradle b/build.gradle index 9ed419b..7dcb025 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'java' +apply plugin: 'application' sourceCompatibility = '1.8' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' @@ -14,6 +15,8 @@ if (!hasProperty('mainClass')) { ext.mainClass = 'autocadDrawingChecker.start.Main' } +mainClassName = 'autocadDrawingChecker.start.Main' // need this for the application plugin + jar { //need to manually set main class, otherwise, it just compiles the JAR as a library manifest { diff --git a/build/libs/AutoCADDrawingChecker.jar b/build/libs/AutoCADDrawingChecker.jar index de4335d..9674d52 100644 Binary files a/build/libs/AutoCADDrawingChecker.jar and b/build/libs/AutoCADDrawingChecker.jar differ diff --git a/build/scripts/AutoCADDrawingChecker b/build/scripts/AutoCADDrawingChecker new file mode 100644 index 0000000..b381c22 --- /dev/null +++ b/build/scripts/AutoCADDrawingChecker @@ -0,0 +1,188 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or 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. +# + +############################################################################## +## +## AutoCADDrawingChecker start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/.." >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="AutoCADDrawingChecker" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and AUTO_CAD_DRAWING_CHECKER_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/lib/AutoCADDrawingChecker.jar:$APP_HOME/lib/poi-ooxml-4.1.2.jar:$APP_HOME/lib/poi-4.1.2.jar:$APP_HOME/lib/commons-codec-1.13.jar:$APP_HOME/lib/commons-collections4-4.4.jar:$APP_HOME/lib/commons-math3-3.6.1.jar:$APP_HOME/lib/SparseBitSet-1.2.jar:$APP_HOME/lib/poi-ooxml-schemas-4.1.2.jar:$APP_HOME/lib/commons-compress-1.19.jar:$APP_HOME/lib/curvesapi-1.06.jar:$APP_HOME/lib/xmlbeans-3.1.0.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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $AUTO_CAD_DRAWING_CHECKER_OPTS -classpath "\"$CLASSPATH\"" autocadDrawingChecker.start.Main "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/build/scripts/AutoCADDrawingChecker.bat b/build/scripts/AutoCADDrawingChecker.bat new file mode 100644 index 0000000..4eda231 --- /dev/null +++ b/build/scripts/AutoCADDrawingChecker.bat @@ -0,0 +1,100 @@ +@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 AutoCADDrawingChecker 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 Add default JVM options here. You can also use JAVA_OPTS and AUTO_CAD_DRAWING_CHECKER_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@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 init + +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 init + +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 + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\lib\AutoCADDrawingChecker.jar;%APP_HOME%\lib\poi-ooxml-4.1.2.jar;%APP_HOME%\lib\poi-4.1.2.jar;%APP_HOME%\lib\commons-codec-1.13.jar;%APP_HOME%\lib\commons-collections4-4.4.jar;%APP_HOME%\lib\commons-math3-3.6.1.jar;%APP_HOME%\lib\SparseBitSet-1.2.jar;%APP_HOME%\lib\poi-ooxml-schemas-4.1.2.jar;%APP_HOME%\lib\commons-compress-1.19.jar;%APP_HOME%\lib\curvesapi-1.06.jar;%APP_HOME%\lib\xmlbeans-3.1.0.jar + +@rem Execute AutoCADDrawingChecker +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %AUTO_CAD_DRAWING_CHECKER_OPTS% -classpath "%CLASSPATH%" autocadDrawingChecker.start.Main %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable AUTO_CAD_DRAWING_CHECKER_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%AUTO_CAD_DRAWING_CHECKER_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/createWeeklog.bat b/createWeeklog.bat new file mode 100644 index 0000000..2b90fd1 --- /dev/null +++ b/createWeeklog.bat @@ -0,0 +1,8 @@ +@echo off +REM set datestr = %date% +REM echo Date format %datestr:~0,2% +REM git log --since="5 days ago" --reverse > %userprofile%\Desktop\weeklyLog %datestr%.txt +REM echo Created weekly log in %userprofile%\Desktop + +git log --since="5 days ago" --reverse > %userprofile%\Desktop\weeklyLog.txt +echo Created weekly log in %userprofile%\Desktop \ No newline at end of file diff --git a/someAutoCADColumns.txt b/someAutoCADColumns.txt new file mode 100644 index 0000000..7da4780 --- /dev/null +++ b/someAutoCADColumns.txt @@ -0,0 +1,42 @@ +# These are just a bunch of columns AutoCAD Excel files may contain. +# I'll hold onto this for now, but I likely won't need this once I've abstractified grading criteria +Count +Name +Angle +Center X +Center Y +Center Z +Color +Delta X +Delta Y +Delta Z +Diameter +Drawing +End X +End Y +End Z +Layer +Length +Linetype +Linetype Scale +Lineweight +Plot Style +Start X +Start Y +Start Z +Thickness +Contents +ContentsRTF +Position X +Position Y +Position Z +Rotation +ShowBorders +Width +Area +Closed +Global Width +Dim Style +DynamicDimension +TextDefinedSize +Value diff --git a/src/main/java/autocadDrawingChecker/autocadData/AutoCADAttribute.java b/src/main/java/autocadDrawingChecker/autocadData/AutoCADAttribute.java deleted file mode 100644 index 2c1ecf0..0000000 --- a/src/main/java/autocadDrawingChecker/autocadData/AutoCADAttribute.java +++ /dev/null @@ -1,66 +0,0 @@ -package autocadDrawingChecker.autocadData; - -/** - * An AutoCADAttribute represents - * a column in an AutoCAD export. - * - * @author Matt Crow - */ -public enum AutoCADAttribute { - COUNT("Count"), - NAME("Name"), - ANGLE("Angle"), - COLOR("Color"), - DELTA_X("Delta X"), - DELTA_Y("Delta Y"), - DELTA_Z("Delta Z"), - DRAWING("Drawing"), - END_X("End X"), - END_Y("End Y"), - END_Z("End Z"), - LAYER("Layer"), - LENGTH("Length"), - LINE_TYPE("Linetype"), - LINE_TYPE_SCALE("Linetype Scale"), - LINE_WEIGTH("Lineweight"), - PLOT_STYLE("Plot Style"), - START_X("Start X"), - START_Y("Start Y"), - START_Z("Start Z"), - THICKNESS("Thickness"), - CONTENTS("Contents"), - CONTENTS_RTF("ContentsRTF"), - POSITION_X("Position X"), - POSITION_Y("Position Y"), - POSITION_Z("Position Z"), - ROTATION("Rotation"), - SHOW_BORDERS("ShowBorders"), - WIDTH("Width"), - AREA("Area"), - CLOSED("Closed"), - GLOBAL_WIDTH("Global Width"), - DIM_STYLE("Dim Style"), - DYNAMIC_DIMENSION("DynamicDimension"), - TEXT_DEFINED_SIZE("TextDefinedSize"); - - private final String header; - - /** - * - * @param sheetHeader the header of this attribute - * as it appears in the AutoCAD export sheet. - */ - private AutoCADAttribute(String sheetHeader){ - header = sheetHeader; - } - - /** - * - * @return the header of this attribute - * as it appears in the AutoCAD export - * sheet. - */ - public final String getHeader(){ - return header; - } -} diff --git a/src/main/java/autocadDrawingChecker/autocadData/AutoCADDimension.java b/src/main/java/autocadDrawingChecker/autocadData/AutoCADDimension.java deleted file mode 100644 index 43a458c..0000000 --- a/src/main/java/autocadDrawingChecker/autocadData/AutoCADDimension.java +++ /dev/null @@ -1,41 +0,0 @@ -package autocadDrawingChecker.autocadData; - -/** - * - * @author Matt Crow - */ -public class AutoCADDimension extends AutoCADElement { - private final String style; - private final int dynamicDimension; - private final String textDefSize; - - public AutoCADDimension(String style, int dynamicDim, String textSize){ - super(); - this.style = style; - dynamicDimension = dynamicDim; - textDefSize = textSize; - } - - public final String getDimensionStyle(){ - return style; - } - - public final int getDynamicDimension(){ - return dynamicDimension; - } - - public final String getTextDefinedSize(){ - return textDefSize; - } - - @Override - public String toString(){ - StringBuilder sb = new StringBuilder(); - sb.append("AutoCAD Dimension:"); - sb.append("\n").append(super.toString()); - sb.append(String.format("\n\t* Style: %s", style)); - sb.append(String.format("\n\t* Dynamic Dimension: %d", dynamicDimension)); - sb.append(String.format("\n\t* Text defined size: %s", textDefSize)); - return sb.toString(); - } -} diff --git a/src/main/java/autocadDrawingChecker/autocadData/AutoCADElement.java b/src/main/java/autocadDrawingChecker/autocadData/AutoCADElement.java deleted file mode 100644 index dc23b07..0000000 --- a/src/main/java/autocadDrawingChecker/autocadData/AutoCADElement.java +++ /dev/null @@ -1,102 +0,0 @@ -package autocadDrawingChecker.autocadData; - -/** - * An AutoCADElement represents - a single record in an AutoCADExport. - * - * @author Matt Crow - */ -public class AutoCADElement { - private int count; - private String name; - private String color; - private String inLayer; - private String lineType; - private double lineTypeScale; - private String lineWeight; - private String plot; - - public AutoCADElement(){ - - } - - public final void setCount(int c){ - count = c; - } - public final int getCount(){ - return count; - } - - public final void setName(String newName){ - name = newName; - } - public final String getName(){ - return name; - } - - public final void setColor(String colorType){ - color = colorType; - } - public final String getColor(){ - return color; - } - - public final void setLayer(String layer){ - inLayer = layer; - } - /** - * - * @return the AutoCAD layer this - * resides within - */ - public final String getLayer(){ - return inLayer; - } - - public final void setLineType(String type){ - lineType = type; - } - public final String getLineType(){ - return lineType; - } - - public final void setLineTypeScale(double scale){ - lineTypeScale = scale; - } - public final double getLineTypeScale(){ - return lineTypeScale; - } - - public final void setLineWeight(String weight){ - lineWeight = weight; - } - public final String getLineWeight(){ - return lineWeight; - } - - public final void setPlot(String plotType){ - plot = plotType; - } - public final String getPlot(){ - return plot; - } - - private String blankIfNull(String s){ - return (s == null) ? "" : s; - } - - @Override - public String toString(){ - StringBuilder sb = new StringBuilder(); - sb.append("AutoCAD Element:"); - sb.append(String.format("\n\t* Count: %d", count)); - sb.append(String.format("\n\t* Name: %s", blankIfNull(name))); - sb.append(String.format("\n\t* Color: %s", blankIfNull(color))); - sb.append(String.format("\n\t* Layer: %s", blankIfNull(inLayer))); - sb.append(String.format("\n\t* Line Type: %s", blankIfNull(lineType))); - sb.append(String.format("\n\t* Line Type Scale: %f", lineTypeScale)); - sb.append(String.format("\n\t* Line Weight: %s", blankIfNull(lineWeight))); - sb.append(String.format("\n\t* Plot: %s", blankIfNull(plot))); - return sb.toString(); - } -} diff --git a/src/main/java/autocadDrawingChecker/autocadData/AutoCADExcelParser.java b/src/main/java/autocadDrawingChecker/autocadData/AutoCADExcelParser.java deleted file mode 100644 index dbc4242..0000000 --- a/src/main/java/autocadDrawingChecker/autocadData/AutoCADExcelParser.java +++ /dev/null @@ -1,275 +0,0 @@ -package autocadDrawingChecker.autocadData; - -import autocadDrawingChecker.logging.Logger; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; - -/** - * The AutoCADExcelParser is used to - * read an Excel spreadsheet, - * extracting the AutoCAD date within as an - * AutoCADExport object. - * - * @author Matt Crow - */ -public class AutoCADExcelParser { - private final String fileName; - private final HashMap headerToCol; - private Row currRow; - - public AutoCADExcelParser(String fileToParse){ - fileName = fileToParse; - headerToCol = new HashMap<>(); - currRow = null; - } - - /** - * Interprets the current row of the - * spreadsheet as an AutoCADLine, and - * returns it. - * - * @return the current row, as an AutoCADLine - */ - private AutoCADLine extractLine(){ - AutoCADLine ret = null; - try { - ret = new AutoCADLine( - getCellInt(AutoCADAttribute.ANGLE), - new double[]{ - getCellDouble(AutoCADAttribute.START_X), - getCellDouble(AutoCADAttribute.START_Y), - getCellDouble(AutoCADAttribute.START_Z) - }, - new double[]{ - getCellDouble(AutoCADAttribute.END_X), - getCellDouble(AutoCADAttribute.END_Y), - getCellDouble(AutoCADAttribute.END_Z) - }, - new double[]{ - getCellDouble(AutoCADAttribute.DELTA_X), - getCellDouble(AutoCADAttribute.DELTA_Y), - getCellDouble(AutoCADAttribute.DELTA_Z) - }, - getCellDouble(AutoCADAttribute.LENGTH), - getCellDouble(AutoCADAttribute.THICKNESS) - ); - } catch (Exception ex){ - Logger.logError(String.format("Error while parsing line %s", currRow.toString())); - Logger.logError(ex); - - } - return ret; - } - - private AutoCADPolyline extractPolyline(){ - return new AutoCADPolyline( - getCellDouble(AutoCADAttribute.LENGTH), - getCellDouble(AutoCADAttribute.THICKNESS), - getCellDouble(AutoCADAttribute.AREA), - getCellString(AutoCADAttribute.CLOSED), - getCellDouble(AutoCADAttribute.GLOBAL_WIDTH) - ); - } - - private AutoCADText extractText(){ - return new AutoCADText( - getCellString(AutoCADAttribute.CONTENTS), - getCellString(AutoCADAttribute.CONTENTS_RTF), - new double[]{ - getCellDouble(AutoCADAttribute.POSITION_X), - getCellDouble(AutoCADAttribute.POSITION_Y), - getCellDouble(AutoCADAttribute.POSITION_Z) - }, - getCellInt(AutoCADAttribute.ROTATION), - getCellInt(AutoCADAttribute.SHOW_BORDERS), - getCellDouble(AutoCADAttribute.WIDTH) - ); - } - - private AutoCADDimension extractDim(){ - return new AutoCADDimension( - getCellString(AutoCADAttribute.DIM_STYLE), - getCellInt(AutoCADAttribute.DYNAMIC_DIMENSION), - getCellString(AutoCADAttribute.TEXT_DEFINED_SIZE) - ); - } - - /** - * Interprets the current row as - an AutoCADElement, and returns it. - * - * @return the current row, as an AutoCADElement. - */ - private AutoCADElement extractRow(){ - return new AutoCADElement(); - } - - /** - * Locates required headers within the given - * row, and populates headerToCol appropriately. - * The header row is expected to contain each header - * detailed in AutoCADAttribute. - * - * @see AutoCADAttribute - * - * @param headerRow the first row of the spreadsheet, - * containing headers. - */ - private void locateColumns(Row headerRow){ - headerToCol.clear(); - ArrayList headers = new ArrayList<>(); - headerRow.cellIterator().forEachRemaining((Cell c)->{ - headers.add(c.getStringCellValue().toUpperCase()); - }); - for(AutoCADAttribute reqAttr : AutoCADAttribute.values()){ - headerToCol.put(reqAttr, headers.indexOf(reqAttr.getHeader().toUpperCase())); - // may be -1. Not sure how we should handle missing headers - } - } - - /** - * - * @param sheet - * @return the number of rows containing data in the given sheet - */ - private int getRowCount(Sheet sheet){ - int ret = 0; - Iterator iter = sheet.rowIterator(); - while(iter.hasNext()){ - if(iter.next().getLastCellNum() == -1){ - break; - } else { - ret++; - } - } - return ret; - } - - /** - * Gets the string value of the cell in the current - * row, in the given column. - * - * @param col the column to get the cell value for - * @return the string value of the cell. - * @throws RuntimeException if the given column is not found - */ - private String getCellString(AutoCADAttribute col){ - int colIdx = headerToCol.get(col); - if(colIdx == -1){ - throw new RuntimeException(String.format("Missing column: %s", col.getHeader())); - } - return currRow.getCell(colIdx).toString(); // returns the contents of the cell as text - } - - private double getCellDouble(AutoCADAttribute col){ - String data = getCellString(col); - double ret = 0.0; - try { - ret = Double.parseDouble(data); - } catch(NumberFormatException ex){ - Logger.logError(String.format("Cannot convert \"%s\" to a double", data)); - throw ex; - } - return ret; - } - - private int getCellInt(AutoCADAttribute col){ - return (int)getCellDouble(col); - } - - private String currRowToString(){ - StringBuilder b = new StringBuilder(); - b.append("["); - Iterator iter = currRow.cellIterator(); - while(iter.hasNext()){ - b.append(iter.next().toString()); - if(iter.hasNext()){ - b.append(", "); - } - } - b.append("]"); - return b.toString(); - } - - public final AutoCADExport parse() throws IOException { - InputStream in = new FileInputStream(fileName); - // new Excel format old Excel format - Workbook workbook = (fileName.endsWith("xlsx")) ? new XSSFWorkbook(in) : new HSSFWorkbook(in); - Sheet sheet = workbook.getSheetAt(0); - AutoCADExport containedTherein = new AutoCADExport(fileName); - locateColumns(sheet.getRow(0)); - int max = getRowCount(sheet); - AutoCADElement data = null; - // skip headers - for(int rowNum = 1; rowNum < max; rowNum++){ - currRow = sheet.getRow(rowNum); - try { - if(getCellString(AutoCADAttribute.NAME).equals("Line")){ - data = extractLine(); - } else if(getCellString(AutoCADAttribute.NAME).equals("Polyline")){ - data = extractPolyline(); - } else if(getCellString(AutoCADAttribute.NAME).equals("MText")){ - data = extractText(); - } else if(getCellString(AutoCADAttribute.NAME).equals("Rotated Dimension")){ - data = extractDim(); - }else { - data = extractRow(); - } - } catch(Exception ex){ - Logger.logError(String.format("Error while parsing row: %s", currRowToString())); - Logger.logError(ex); - } - if(data != null){ - // sets these attributes for every element - // which do we need? - try { - data.setLayer(getCellString(AutoCADAttribute.LAYER)); - data.setCount(getCellInt(AutoCADAttribute.COUNT)); - data.setName(getCellString(AutoCADAttribute.NAME)); - data.setColor(getCellString(AutoCADAttribute.COLOR)); - data.setLineType(getCellString(AutoCADAttribute.LINE_TYPE)); - data.setLineTypeScale(getCellDouble(AutoCADAttribute.LINE_TYPE_SCALE)); - data.setLineWeight(getCellString(AutoCADAttribute.LINE_WEIGTH)); - data.setPlot(getCellString(AutoCADAttribute.PLOT_STYLE)); - } catch(Exception ex){ - Logger.logError(String.format("Error while parsing row: %s", currRowToString())); - Logger.logError(ex); - } - containedTherein.add(data); - data = null; - } - } - //Logger.log("In AutoCADExcelParser.parse...\n" + containedTherein.toString()); - workbook.close(); - return containedTherein; - } - - /** - * Reads the Excel file with the - * given complete file path, and - * returns its contents as an AutoCADExport. - * - * This is a shorthand for - *

-     * new AutoCADExcelParser(fileName).parse();
-     * 
- * - * @param fileName the complete path to an Excel file. - * @return the contents of the first sheet of the given Excel file , as - * an AutoCADExport. - * @throws IOException if the fileName given does not point to an Excel file - */ - public static AutoCADExport parse(String fileName) throws IOException{ - return new AutoCADExcelParser(fileName).parse(); - } -} diff --git a/src/main/java/autocadDrawingChecker/autocadData/AutoCADLine.java b/src/main/java/autocadDrawingChecker/autocadData/AutoCADLine.java deleted file mode 100644 index 39f93d2..0000000 --- a/src/main/java/autocadDrawingChecker/autocadData/AutoCADLine.java +++ /dev/null @@ -1,79 +0,0 @@ -package autocadDrawingChecker.autocadData; - -import java.util.Arrays; - -/** - * The AutoCADLine class is used to differentiate - * between geometric lines and other AutoCAD elements. - * - * I'm not quite sure how AutoCAD works, so I - * don't know if I'll need different classes - * for each element type. - * - * @author Matt Crow - */ -public class AutoCADLine extends AutoCADElement { - private final int angle; - private final double[] deltas; - private final double[] r0; // r naught - private final double[] r; // r final - private final double length; - private final double thickness; - - public static final int DIMENSION_COUNT = 3; - - public AutoCADLine(int theta, double[] start, double[] end, double[] ds, double len, double thick) { - super(); - angle = theta; - r0 = Arrays.copyOf(start, DIMENSION_COUNT); - r = Arrays.copyOf(end, DIMENSION_COUNT); - deltas = Arrays.copyOf(ds, DIMENSION_COUNT); - length = len; - thickness = thick; - } - - public final double getStart(int dimension){ - return r0[dimension]; - } - public final double getEnd(int dimension){ - return r[dimension]; - } - public final double getDelta(int dimension){ - return deltas[dimension]; - } - - /** - * - * @return the physical length of this line. - */ - public final double getLength(){ - return length; - } - - public final double getThickness(){ - return thickness; - } - - /** - * - * @return the angle between this line - * and the x-axis, measured in degrees - */ - public final int getAngle(){ - return angle; - } - - @Override - public String toString(){ - StringBuilder sb = new StringBuilder(); - sb.append("AutoCAD Line:"); - sb.append("\n").append(super.toString()); - sb.append(String.format("\n\t* Angle: %d", angle)); - sb.append(String.format("\n\t* Start: (%f, %f, %f)", r0[0], r0[1], r0[2])); - sb.append(String.format("\n\t* End: (%f, %f, %f)", r[0], r[1], r[2])); - sb.append(String.format("\n\t* Deltas: (%f, %f, %f)", deltas[0], deltas[1], deltas[2])); - sb.append(String.format("\n\t* Length: %f", length)); - sb.append(String.format("\n\t* Thickness: %f", thickness)); - return sb.toString(); - } -} diff --git a/src/main/java/autocadDrawingChecker/autocadData/AutoCADPolyline.java b/src/main/java/autocadDrawingChecker/autocadData/AutoCADPolyline.java deleted file mode 100644 index c0fc702..0000000 --- a/src/main/java/autocadDrawingChecker/autocadData/AutoCADPolyline.java +++ /dev/null @@ -1,51 +0,0 @@ -package autocadDrawingChecker.autocadData; - -/** - * - * @author Matt - */ -public class AutoCADPolyline extends AutoCADElement { - private final double length; - private final double thickness; - private final double area; - private final String closed; - private final double globalWidth; - - public AutoCADPolyline(double len, double thick, double area, String closed, double globalWidth){ - super(); - length = len; - thickness = thick; - this.area = area; - this.closed = closed; - this.globalWidth = globalWidth; - } - - public final double getLength(){ - return length; - } - public final double getThickness(){ - return thickness; - } - public final double getArea(){ - return area; - } - public final String getClosed(){ - return closed; - } - public final double getGlobalWidth(){ - return globalWidth; - } - - @Override - public String toString(){ - StringBuilder sb = new StringBuilder(); - sb.append("AutoCAD Polyline:"); - sb.append("\n").append(super.toString()); - sb.append(String.format("\n\t* Length: %f", length)); - sb.append(String.format("\n\t* Thickness: %f", thickness)); - sb.append(String.format("\n\t* Area: %f", area)); - sb.append(String.format("\n\t* Closed: %s", closed)); - sb.append(String.format("\n\t* Global Width: %f", globalWidth)); - return sb.toString(); - } -} diff --git a/src/main/java/autocadDrawingChecker/autocadData/AutoCADText.java b/src/main/java/autocadDrawingChecker/autocadData/AutoCADText.java deleted file mode 100644 index fa89f9d..0000000 --- a/src/main/java/autocadDrawingChecker/autocadData/AutoCADText.java +++ /dev/null @@ -1,65 +0,0 @@ -package autocadDrawingChecker.autocadData; - -import java.util.Arrays; - -/** - * - * @author Matt - */ -public class AutoCADText extends AutoCADElement { - private final String contents; - private final String contentsRTF; - private final double[] position; - private final int rotation; - private final int showBorders; - private final double width; - - public AutoCADText(String textContents, String rtfContents, double[] coords, int theta, int showBorders, double width) { - super(); - contents = textContents; - contentsRTF = rtfContents; - position = Arrays.copyOf(coords, coords.length); - rotation = theta; - this.showBorders = showBorders; - this.width = width; - } - - public final String getTextContents(){ - return contents; - } - public final String getRTFContents(){ - return contentsRTF; - } - public final double getX(){ - return position[0]; - } - public final double getY(){ - return position[1]; - } - public final double getZ(){ - return position[2]; - } - public final int getRotation(){ - return rotation; - } - public final int getShowBorders(){ - return showBorders; - } - public final double getWidth(){ - return width; - } - - @Override - public String toString(){ - StringBuilder sb = new StringBuilder(); - sb.append("AutoCAD Text:"); - sb.append("\n").append(super.toString()); - sb.append(String.format("\n\t* Contents: %s", contents)); - sb.append(String.format("\n\t* RTF: %s", contentsRTF)); - sb.append(String.format("\n\t* Position: (%f, %f, %f)", position[0], position[1], position[2])); - sb.append(String.format("\n\t* Rotation: %d", rotation)); - sb.append(String.format("\n\t* Show Borders: %d", showBorders)); - sb.append(String.format("\n\t* Width: %f", width)); - return sb.toString(); - } -} diff --git a/src/main/java/autocadDrawingChecker/autocadData/package-info.java b/src/main/java/autocadDrawingChecker/autocadData/package-info.java deleted file mode 100644 index 9cd4639..0000000 --- a/src/main/java/autocadDrawingChecker/autocadData/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * The autocadData package contains classes - * used to convert AutoCAD Excel exports into - * a more useful format for the program. - */ -package autocadDrawingChecker.autocadData; \ No newline at end of file diff --git a/src/main/java/autocadDrawingChecker/data/AutoCADElement.java b/src/main/java/autocadDrawingChecker/data/AutoCADElement.java new file mode 100644 index 0000000..f197ef3 --- /dev/null +++ b/src/main/java/autocadDrawingChecker/data/AutoCADElement.java @@ -0,0 +1,122 @@ +package autocadDrawingChecker.data; + +import autocadDrawingChecker.logging.Logger; +import java.util.HashMap; +import java.util.Set; + +/** + * An AutoCADElement represents + a single record in an AutoCADExport. + * + * @author Matt Crow + */ +public class AutoCADElement { + private final HashMap attributes; + + public static final String NAME_COL = "Name"; + public static final String LAYER_COL = "Layer"; + + // no access modifier, so only classes in this package can invoke this + AutoCADElement(){ + this.attributes = new HashMap<>(); + } + + public final Set getAttributes(){ + return attributes.keySet(); + } + + /** + * + * @return the value in this' "Name" column. + * This value is the type of AutoCAD element this is + * (line, circle, etc.) + */ + public final String getName(){ + return getAttributeString(NAME_COL); + } + + /** + * + * @return the AutoCAD layer this + * resides within + */ + public final String getLayer(){ + return (String)getAttribute(LAYER_COL); + } + + private String sanitizeAttributeName(String name){ + return name.trim().toUpperCase(); + } + + final AutoCADElement setAttribute(String attrName, Object value){ + attributes.put(sanitizeAttributeName(attrName), value); + return this; + } + + /** + * Checks to see if this has the given attribute + * @param attributeName the name to check for + * @return whether or not this has the given attribute + */ + public final boolean hasAttribute(String attributeName){ + return attributes.containsKey(sanitizeAttributeName(attributeName)); + } + + /** + * Gets this' attribute with the given name. + * If this has no such attribute, throws an exception. + * You should use getAttributeString(), getAttributeDouble(), or getAttributeInt() + * if you want the attribute in another format. + * + * @param attributeName the name of the attribute to get. + * @return the given attribute as an Object + * @throws NullPointerException if this has no attribute with the given name. + */ + public final Object getAttribute(String attributeName) throws NullPointerException { + if(!hasAttribute(attributeName)){ + throw new NullPointerException(String.format("Doesn't have attribute \"%s\"", attributeName)); + } + return attributes.get(sanitizeAttributeName(attributeName)); + } + + /** + * + * @param attributeName the name of the attribute to get + * @return the attribute with the given name + * @throws NullPointerException if this has no such attribute with the given name + */ + public final String getAttributeString(String attributeName) throws NullPointerException { + return getAttribute(attributeName).toString(); + } + + /** + * + * @param attributeName the attribute to get + * @return the given attribute, as a double + * @throws NumberFormatException if the given attribute is not a number + */ + public final double getAttributeDouble(String attributeName) throws NumberFormatException { + String s = getAttributeString(attributeName); + double ret = 0.0; + try { + ret = Double.valueOf(s); + } catch(NumberFormatException ex){ + Logger.logError(String.format("Attribute with name \"%s\" cannot be formatted as a number. Its value is \"%s\"", attributeName, s)); + throw ex; + } + return ret; + } + public final int getAttributeInt(String attributeName){ + return (int)getAttributeDouble(attributeName); + } + + @Override + public String toString(){ + StringBuilder sb = new StringBuilder(); + sb.append("AutoCAD Element:"); + attributes.forEach((k, v)->{ + sb.append(String.format("\n- %s : %s", k, v.toString())); + }); + return sb.toString(); + } +} diff --git a/src/main/java/autocadDrawingChecker/data/AutoCADElementExtractor.java b/src/main/java/autocadDrawingChecker/data/AutoCADElementExtractor.java new file mode 100644 index 0000000..d0c074f --- /dev/null +++ b/src/main/java/autocadDrawingChecker/data/AutoCADElementExtractor.java @@ -0,0 +1,88 @@ +package autocadDrawingChecker.data; + +import autocadDrawingChecker.logging.Logger; +import java.util.HashMap; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; + +/** + * @author Matt + */ +class AutoCADElementExtractor { + private HashMap currentCols; + private Row currentRow; + + private Object getCell(String col){ + Object ret = null; + int colIdx = currentCols.get(col); + if(colIdx == -1){ + throw new RuntimeException(String.format("Missing column: %s", col)); + } + Cell c = currentRow.getCell(colIdx); + switch(c.getCellType()){ + case BOOLEAN: + ret = c.getBooleanCellValue(); + break; + case NUMERIC: + ret = c.getNumericCellValue(); + break; + case STRING: + ret = c.getStringCellValue(); + break; + default: + Logger.logError(String.format("AutoCADElementExtractor encountered cell with type %s", c.getCellType().name())); + //ret = c.toString(); + break; + } + return ret; + } + + protected String getCellString(String col){ + String ret = null; + try { + ret = (String)getCell(col); + } catch(ClassCastException ex){ + Logger.logError(String.format("Column \"%s\" is not a string", col)); + throw ex; + } + return ret; + } + + protected boolean currentRowHasCell(String col){ + int colIdx = currentCols.get(col); + boolean hasCol = + colIdx != -1 && + currentRow.getCell(colIdx) != null && currentRow.getCell(colIdx).getCellType() != CellType.BLANK; + // getCell doesn't throw an exception if it doesn't have a cell for the given column: it just returns null + + return hasCol; + } + + /** + * Extracts data from the given row, and converts it to an AutoCAD element. + * @param columns the mapping of columns to the index of the column + * in the given row. + * @param currentRow the row to extract data from + * @return the extracted AutoCADElement. + */ + public synchronized final AutoCADElement extract(HashMap columns, Row currentRow){ + // temporarily set the columns and row. Note this method is synchronized to prevent multithreading issues + this.currentCols = columns; + this.currentRow = currentRow; + AutoCADElement ret = doExtract(); + this.currentCols = null; + this.currentRow = null; + return ret; + } + + private AutoCADElement doExtract(){ + AutoCADElement ret = new AutoCADElement(); + currentCols.keySet().forEach((header)->{ + if(this.currentRowHasCell(header)){ + ret.setAttribute(header, getCell(header)); + } + }); + return ret; + } +} diff --git a/src/main/java/autocadDrawingChecker/data/AutoCADExcelParser.java b/src/main/java/autocadDrawingChecker/data/AutoCADExcelParser.java new file mode 100644 index 0000000..5133eda --- /dev/null +++ b/src/main/java/autocadDrawingChecker/data/AutoCADExcelParser.java @@ -0,0 +1,150 @@ +package autocadDrawingChecker.data; + +import autocadDrawingChecker.logging.Logger; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +/** + * The AutoCADExcelParser is used to + * read an Excel spreadsheet, + * extracting the AutoCAD date within as an + * AutoCADExport object. + * + * @author Matt Crow + */ +public class AutoCADExcelParser { + private final String fileName; + private final HashMap headerToCol; + private Row currRow; + + /** + * @param fileToParse a path to the Excel file this should parse. + */ + public AutoCADExcelParser(String fileToParse){ + fileName = fileToParse; + headerToCol = new HashMap<>(); + currRow = null; + } + + /** + * Locates headers within the given + * row, and populates headerToCol appropriately. + * + * @param headerRow the first row of the spreadsheet, + * containing headers. + */ + private synchronized void locateColumns(Row headerRow){ + headerToCol.clear(); + ArrayList headers = new ArrayList<>(); + headerRow.cellIterator().forEachRemaining((Cell c)->{ + headers.add(c.toString().toUpperCase()); + }); + for(int i = 0; i < headers.size(); i++){ + headerToCol.put(headers.get(i), i); + } + } + + private boolean isRowEmpty(Row row){ + boolean couldBeEmpty = true; + Iterator cellIter = row.cellIterator(); + while(cellIter.hasNext() && couldBeEmpty){ + if(!cellIter.next().getCellType().equals(CellType.BLANK)){ + couldBeEmpty = false; + } + } + return couldBeEmpty; + } + private boolean isValidRow(Row row){ + return row != null && row.getLastCellNum() != -1 && !isRowEmpty(row); + } + + private String currRowToString(){ + StringBuilder b = new StringBuilder(); + b.append("["); + Iterator iter = currRow.cellIterator(); + while(iter.hasNext()){ + b.append(iter.next().toString()); + if(iter.hasNext()){ + b.append(", "); + } + } + b.append("]"); + return b.toString(); + } + + public final AutoCADExport parse() throws IOException { + InputStream in = new FileInputStream(fileName); + // new Excel format old Excel format + Workbook workbook = (fileName.endsWith("xlsx")) ? new XSSFWorkbook(in) : new HSSFWorkbook(in); + Sheet sheet = workbook.getSheetAt(0); + AutoCADExport containedTherein = new AutoCADExport(fileName); + locateColumns(sheet.getRow(0)); + + + AutoCADElementExtractor recExtr = new AutoCADElementExtractor(); + + int numRows = sheet.getLastRowNum() + 1; // need the + 1, otherwise it sometimes doesn't get the last row + /* + Note that numRows will be greater than or + equal to the last row with data, but not + every row is guaranteed to contain data. + + From the Apache POI javadoc: + """ + Gets the last row on the sheet + Note: rows which had content before and were set to empty later might still be counted as rows by Excel and Apache POI, + so the result of this method will include such rows and thus the returned value might be higher than expected! + """ + */ + + AutoCADElement rec = null; + + // skip headers + for(int rowNum = 1; rowNum < numRows; rowNum++){ + currRow = sheet.getRow(rowNum); + if(isValidRow(currRow)){ + try { + rec = recExtr.extract(headerToCol, currRow); + containedTherein.add(rec); + } catch(Exception ex){ + Logger.logError(String.format("Error while parsing row: %s", currRowToString())); + Logger.logError(ex); + } + } + } + //Logger.log("In AutoCADExcelParser.parse...\n" + containedTherein.toString()); + + workbook.close(); + return containedTherein; + } + + /** + * Reads the Excel file with the + * given complete file path, and + * returns its contents as an AutoCADExport. + * + * Later versions may extract multiple Exports based on column values, + * or this functionality may be deferred to AutoCADExport. + * + * @param fileName the complete path to an Excel file. + * @return the contents of the first sheet of the given Excel file , as + * an AutoCADExport. + * @throws IOException if the fileName given does not point to an Excel file + */ + public static AutoCADExport parse(String fileName) throws IOException{ + return new AutoCADExcelParser( + fileName + ).parse(); + } +} diff --git a/src/main/java/autocadDrawingChecker/autocadData/AutoCADExport.java b/src/main/java/autocadDrawingChecker/data/AutoCADExport.java similarity index 58% rename from src/main/java/autocadDrawingChecker/autocadData/AutoCADExport.java rename to src/main/java/autocadDrawingChecker/data/AutoCADExport.java index ec8bdae..853512a 100644 --- a/src/main/java/autocadDrawingChecker/autocadData/AutoCADExport.java +++ b/src/main/java/autocadDrawingChecker/data/AutoCADExport.java @@ -1,19 +1,21 @@ -package autocadDrawingChecker.autocadData; +package autocadDrawingChecker.data; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; +import java.util.Set; /** * An AutoCADExport is used to store the data extracted by * AutoCADExcelParser. One should use LinkedList methods, * such as LinkedList::forEach and LinkedList::stream, to - * operate on the contents of the Export + * operate on the contents of the Export. * * @author Matt Crow */ public class AutoCADExport extends LinkedList { private final String fileName; - + public AutoCADExport(String fileName){ super(); this.fileName = fileName; @@ -45,6 +47,34 @@ public final HashMap getLayerLineCounts(){ return lineCounts; } + public final HashMap> sortRecordsByColumn(String column){ + HashMap> valueToRecords = new HashMap<>(); + forEach((record)->{ + Object value = record.getAttribute(column); + if(!valueToRecords.containsKey(value)){ + valueToRecords.put(value, new LinkedList<>()); + } + valueToRecords.get(value).add(record); + }); + return valueToRecords; + } + public final HashMap getCountsPerColumnValue(String column){ + HashMap> sorted = sortRecordsByColumn(column); + HashMap counts = new HashMap<>(); + sorted.forEach((key, list)->{ + counts.put(key, list.size()); + }); + return counts; + } + + public final Set getColumns(){ + Set cols = new HashSet<>(); + forEach((AutoCADElement e)->{ + cols.addAll(e.getAttributes()); + }); + return cols; + } + @Override public String toString(){ StringBuilder sb = new StringBuilder(); diff --git a/src/main/java/autocadDrawingChecker/data/package-info.java b/src/main/java/autocadDrawingChecker/data/package-info.java new file mode 100644 index 0000000..69ad61f --- /dev/null +++ b/src/main/java/autocadDrawingChecker/data/package-info.java @@ -0,0 +1,14 @@ +/** + * The autocadData package contains classes + * used to convert AutoCAD Excel exports into + * a more useful format for the program. + * + * + * + * + * + * + * + *
Class Purpose
AutoCADElement Stores the data from one row in the AutoCAD Excel file.
AutoCADElementExtractorPackage-private. Used to convert rows in the Excel file to AutoCADElements.
AutoCADExcelParser Reads an Excel file, and extracts an AutoCADExport from it.
AutoCADExport Stores a list of all AutoCADElements stored in a file.
+ */ +package autocadDrawingChecker.data; \ No newline at end of file diff --git a/src/main/java/autocadDrawingChecker/grading/AbstractElementCriteria.java b/src/main/java/autocadDrawingChecker/grading/AbstractElementCriteria.java deleted file mode 100644 index f565026..0000000 --- a/src/main/java/autocadDrawingChecker/grading/AbstractElementCriteria.java +++ /dev/null @@ -1,46 +0,0 @@ -package autocadDrawingChecker.grading; - -import autocadDrawingChecker.autocadData.AutoCADElement; -import autocadDrawingChecker.autocadData.AutoCADElementMatcher; -import autocadDrawingChecker.autocadData.AutoCADExport; -import autocadDrawingChecker.autocadData.MatchingAutoCADElements; -import java.util.List; - -/** - * AbstractElementCriteria adds behavior to AbstractGradingCriteria to add - * explicit support for grading exports at the element level, meaning an export - * is graded based on how well its individual elements score. - * - * @author Matt Crow - * @param the type of elements to grade - */ -public interface AbstractElementCriteria extends AbstractGradingCriteria { - /** - * Converts e to the given type T. If that - * is not possible, return null instead. - * @param e - * @return e as T, if possible - */ - public abstract T cast(AutoCADElement e); - - public abstract double getMatchScore(T e1, T e2); - - /** - * Computes the average match score of elements in the given exports. - * @param exp1 - * @param exp2 - * @return - */ - @Override - public default double computeScore(AutoCADExport exp1, AutoCADExport exp2){ - List> matches = new AutoCADElementMatcher<>(exp1, exp2, this::cast, this::getMatchScore).findMatches(); - double netScore = matches.stream().map((MatchingAutoCADElements match)->{ - return getMatchScore(match.getElement1(), match.getElement2()); - }).reduce(0.0, Double::sum); - - if(!matches.isEmpty()){ - netScore /= matches.size(); - } - return netScore; - } -} diff --git a/src/main/java/autocadDrawingChecker/autocadData/AutoCADElementMatcher.java b/src/main/java/autocadDrawingChecker/grading/AutoCADElementMatcher.java similarity index 53% rename from src/main/java/autocadDrawingChecker/autocadData/AutoCADElementMatcher.java rename to src/main/java/autocadDrawingChecker/grading/AutoCADElementMatcher.java index edbbb08..dbd1472 100644 --- a/src/main/java/autocadDrawingChecker/autocadData/AutoCADElementMatcher.java +++ b/src/main/java/autocadDrawingChecker/grading/AutoCADElementMatcher.java @@ -1,9 +1,12 @@ -package autocadDrawingChecker.autocadData; +package autocadDrawingChecker.grading; +import autocadDrawingChecker.data.AutoCADExport; +import autocadDrawingChecker.data.AutoCADElement; +import autocadDrawingChecker.logging.Logger; import java.util.LinkedList; import java.util.List; import java.util.function.BiFunction; -import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; /** @@ -13,7 +16,7 @@ another. Essentially, this class compares two exports, and asks "which elements from the second export are supposed to match which elements in the first?" * *

A Rigorous Definition of what this Should do

- * Given sets A and B, and function f(a from A, b from B) => real + * Given sets A and B, and function
f(a from A, b from B) => real
* this class returns the set C such that each element c in C contains * an ordered pair (a from A, b from B) such that * there exists no set c in C such that for any c2 in C, c2 shares an element with c and c2 is not c @@ -27,28 +30,27 @@ *
  • C must contain the maximum average value of f(c) possible This is the hard one
  • * * - * @author Matt Crow - * @param the type of AutoCADElements to match. Elements - * which cannot be converted to this type will be ignored. Use - * AutoCADElement to keep all elements. + * @author Matt Crow */ -public class AutoCADElementMatcher { - private final AutoCADExport exp1; - private final AutoCADExport exp2; - private final Function caster; - private final BiFunction score; +public class AutoCADElementMatcher { + private final List exp1; + private final List exp2; + private final Predicate accepter; + private final BiFunction score; /** * - * @param src - * @param cmp - * @param tryCast a function returning the given AutoCADElement as type T, or null if it cannot cast - * @param scoringFunction + * @param src the instructor AutoCADExport the student's file should conform to + * @param cmp the student's file + * @param accepter a function which accepts an AutoCADElement, and returns true iff the element should be included in matching. + * @param scoringFunction a function which returns a double between 0.0 and 1.0. When given an instructor and student file, it should return a number + * within this range, with higher scores meaning the student's export is similar to the instructor export, and lower ones meaning the two exports are + * different. Essentially, this acts as a grader assigning a score based on how well the student did by some metric. */ - public AutoCADElementMatcher(AutoCADExport src, AutoCADExport cmp, Function tryCast, BiFunction scoringFunction){ + public AutoCADElementMatcher(List src, List cmp, Predicate accepter, BiFunction scoringFunction){ exp1 = src; exp2 = cmp; - caster = tryCast; + this.accepter = accepter; score = scoringFunction; } @@ -65,28 +67,26 @@ public AutoCADElementMatcher(AutoCADExport src, AutoCADExport cmp, Function> findMatches(){ - List> matches = new LinkedList<>(); + public List findMatches(){ + List matches = new LinkedList<>(); // pool of unmatched elements - List pool = exp2.stream().filter((AutoCADElement e)->{ - //return e instanceof T; doesn't work because of how generics are implemented - return true; - }).map(caster).filter((T casted)->{ // use caster to cast ... - return casted != null; // ... then ignore element which it couldn't casr - }).collect(Collectors.toList()); + List pool = exp2.stream().filter(accepter).collect(Collectors.toList()); - exp1.stream().map(caster).filter((T casted)->{ - return casted != null; - }).forEach((T srcRow)->{ + exp1.stream().filter(accepter).forEach((AutoCADElement srcRow)->{ // find the closest match to srcRow double bestScore = 0.0; double currScore = 0.0; - T bestRow = null; + AutoCADElement bestRow = null; // find the highest score - for(T cmpRow : pool){ - currScore = score.apply(srcRow, cmpRow); + for(AutoCADElement cmpRow : pool){ + try { + currScore = score.apply(srcRow, cmpRow); + } catch (Exception ex){ + Logger.logError(ex); + currScore = 0.0; + } if(currScore > bestScore){ bestScore = currScore; bestRow = cmpRow; @@ -95,7 +95,7 @@ public List> findMatches(){ // some rows may not match at all if(bestRow != null){ - MatchingAutoCADElements m = new MatchingAutoCADElements<>(srcRow, bestRow); + MatchingAutoCADElements m = new MatchingAutoCADElements(srcRow, bestRow); //System.out.println("In AutoCADElementMatcher.findMatches: " + m); matches.add(m); pool.remove(bestRow); diff --git a/src/main/java/autocadDrawingChecker/files/ExcelFileLocator.java b/src/main/java/autocadDrawingChecker/grading/ExcelFileLocator.java similarity index 97% rename from src/main/java/autocadDrawingChecker/files/ExcelFileLocator.java rename to src/main/java/autocadDrawingChecker/grading/ExcelFileLocator.java index 0a7d62d..8e2f33f 100644 --- a/src/main/java/autocadDrawingChecker/files/ExcelFileLocator.java +++ b/src/main/java/autocadDrawingChecker/grading/ExcelFileLocator.java @@ -1,4 +1,4 @@ -package autocadDrawingChecker.files; +package autocadDrawingChecker.grading; import java.io.File; import java.nio.file.Paths; diff --git a/src/main/java/autocadDrawingChecker/grading/GradedExport.java b/src/main/java/autocadDrawingChecker/grading/GradedExport.java index 10f5bd7..a5db41c 100644 --- a/src/main/java/autocadDrawingChecker/grading/GradedExport.java +++ b/src/main/java/autocadDrawingChecker/grading/GradedExport.java @@ -1,35 +1,55 @@ package autocadDrawingChecker.grading; -import autocadDrawingChecker.autocadData.AutoCADExport; +import autocadDrawingChecker.grading.criteria.AbstractGradingCriteria; +import autocadDrawingChecker.data.AutoCADExport; import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * The GradedExport class handles grading a single student file to the instructor file. * Note that this comparison may - * not be commutative: + * not be commutative: *
    GradedExport(x, y) != GradedExport(y, x)
    - * * * @author Matt Crow. */ public class GradedExport { private final AutoCADExport instructorExport; private final AutoCADExport studentExport; - private final List gradedCriteria; + private final Set gradedCriteria; private final HashMap grades; private final double finalGrade; - public GradedExport(AutoCADExport instructorsExport, AutoCADExport studentsExport, List gradeOnThese){ + /** + * Note that this constructor is is package-private, + * so it can only be instantiated from within this package. + * This is done by the Grader class. + * + * @param instructorsExport the instructor's AutoCADExport + * @param studentsExport the student's AutoCADExport + * @param gradeOnThese the criteria to grade on. + */ + GradedExport(AutoCADExport instructorsExport, AutoCADExport studentsExport, List gradeOnThese){ instructorExport = instructorsExport; studentExport = studentsExport; - gradedCriteria = gradeOnThese; + gradedCriteria = new HashSet<>(gradeOnThese); grades = new HashMap<>(); finalGrade = runComparison(); } + /** + * Computes the grade for the student + * file. It grades the export on each + * provided criteria, and gives the final + * grade as the average of the grades for + * each criteria. + * + * @return the student's final grade. + */ private double runComparison(){ double similarityScore = 0.0; double newScore = 0.0; @@ -38,29 +58,62 @@ private double runComparison(){ grades.put(attr, newScore); similarityScore += newScore; } - return similarityScore / gradedCriteria.size(); // average similarity score + + return similarityScore / (gradedCriteria.size()); // average similarity score } + /** + * + * @return the instructor file this grades based on. + */ public final AutoCADExport getInstructorFile(){ return instructorExport; } + /** + * + * @return the student file this grades. + */ public final AutoCADExport getStudentFile(){ return studentExport; } + /** + * Note that the GradingReport uses + * a formula to dynamically calculate + * this in the spreadsheet it generates, + * so this method isn't used. + * + * @return the final grade for this export. + */ public final double getFinalGrade(){ return finalGrade; } + /** + * + * @param criteria the criteria to get the grade for + * @return the grade the student got for the given criteria, + * or null if this didn't grade on the given criteria. + */ public final double getGradeFor(AbstractGradingCriteria criteria){ return grades.get(criteria); } + /** + * + * @return a string representation of this + * graded export. If you wish to save this + * information to a text file, you may, but + * you can also save to an Excel file using + * the GradingReport class, which may be more + * helpful. + * @see GradingReport + */ @Override public String toString(){ StringBuilder sb = new StringBuilder(); - sb.append(String.format("Comparing %s to %s:", instructorExport.getFileName(), studentExport.getFileName())); + sb.append(String.format("Comparing %s to \n%s:", instructorExport.getFileName(), studentExport.getFileName())); grades.forEach((attr, score)->{ sb.append(String.format("\n* %s: %d%%", attr.getName(), (int)(score * 100))); }); diff --git a/src/main/java/autocadDrawingChecker/grading/Grader.java b/src/main/java/autocadDrawingChecker/grading/Grader.java index a953b3d..8d67d16 100644 --- a/src/main/java/autocadDrawingChecker/grading/Grader.java +++ b/src/main/java/autocadDrawingChecker/grading/Grader.java @@ -1,12 +1,17 @@ package autocadDrawingChecker.grading; -import autocadDrawingChecker.files.ExcelFileLocator; -import autocadDrawingChecker.autocadData.AutoCADExcelParser; -import autocadDrawingChecker.autocadData.AutoCADExport; +import autocadDrawingChecker.grading.criteria.AbstractGradingCriteria; +import autocadDrawingChecker.data.AutoCADExcelParser; +import autocadDrawingChecker.data.AutoCADExport; +import autocadDrawingChecker.grading.criteria.implementations.CompareColumn; import autocadDrawingChecker.logging.Logger; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; /** @@ -71,7 +76,6 @@ private List getStudentFiles(){ */ public final GradingReport grade(){ GradingReport report = new GradingReport(); - criteria.forEach((crit)->report.addCriteria(crit)); AutoCADExport trySrc = null; List cmp = null; @@ -87,8 +91,25 @@ public final GradingReport grade(){ cmp = getStudentFiles(); + /* + see which columns exist in the instructor export, + and add those columns to the list of criteria this + should grade on. Don't directly add them to this.criteria + though, as that could cause problems + */ + Set colsToGrade = (src == null) ? new HashSet<>() : src.getColumns(); + LinkedList colCriteria = new LinkedList<>(); + for(String column : colsToGrade){ + colCriteria.add(new CompareColumn(column)); + } + + List finalGradedCriteria = new ArrayList<>(); + finalGradedCriteria.addAll(criteria); + finalGradedCriteria.addAll(colCriteria); + finalGradedCriteria.forEach((c)->report.addCriteria(c)); + cmp.stream().forEach((exp)->{ - report.add(new GradedExport(src, exp, criteria)); + report.add(new GradedExport(src, exp, finalGradedCriteria)); }); return report; diff --git a/src/main/java/autocadDrawingChecker/grading/GradingCriteriaLoader.java b/src/main/java/autocadDrawingChecker/grading/GradingCriteriaLoader.java deleted file mode 100644 index a09612c..0000000 --- a/src/main/java/autocadDrawingChecker/grading/GradingCriteriaLoader.java +++ /dev/null @@ -1,33 +0,0 @@ -package autocadDrawingChecker.grading; - -import autocadDrawingChecker.grading.implementations.CheckDims; -import autocadDrawingChecker.grading.implementations.LinesPerLayer; -import autocadDrawingChecker.grading.implementations.LineStart; -import autocadDrawingChecker.grading.implementations.LineLength; -import autocadDrawingChecker.grading.implementations.LineEnd; -import autocadDrawingChecker.grading.implementations.LineCount; -import autocadDrawingChecker.grading.implementations.LineAngle; -import autocadDrawingChecker.grading.implementations.TextMatches; -import java.util.ArrayList; - -/** - * The GradingCriteriaLoader is used - * to gather all subclasses of AbstractGradingCriteria. - * - * TODO: make this scan class path - * @author Matt Crow - */ -public class GradingCriteriaLoader { - public static final ArrayList getAllCriteria(){ - ArrayList ret = new ArrayList<>(); - ret.add(new LineCount()); - ret.add(new LinesPerLayer()); - ret.add(new LineLength()); - ret.add(new LineAngle()); - ret.add(new LineStart()); - ret.add(new LineEnd()); - ret.add(new CheckDims()); - ret.add(new TextMatches()); - return ret; - } -} diff --git a/src/main/java/autocadDrawingChecker/grading/GradingReport.java b/src/main/java/autocadDrawingChecker/grading/GradingReport.java index 4f80b20..6a780e0 100644 --- a/src/main/java/autocadDrawingChecker/grading/GradingReport.java +++ b/src/main/java/autocadDrawingChecker/grading/GradingReport.java @@ -1,22 +1,29 @@ package autocadDrawingChecker.grading; +import autocadDrawingChecker.grading.criteria.AbstractGradingCriteria; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.ComparisonOperator; +import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellAddress; +import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFConditionalFormattingRule; import org.apache.poi.xssf.usermodel.XSSFDataFormat; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFSheetConditionalFormatting; import org.apache.poi.xssf.usermodel.XSSFWorkbook; /** * The GradingReport class is used to bundle a set of graded exports * together so they can be outputted together into one file. This - * class will likely be rewritten in later versions to write the - * report in a more easily readable format. + * class can be used to write the graded files to either a text file, + * or an Excel file. * * @author Matt Crow */ @@ -29,7 +36,7 @@ public class GradingReport extends LinkedList { private static final String CMP_FILE_HEADER = "Student File"; private static final String FINAL_GRADE_HEADER = "Final Grade"; - public GradingReport(){ + GradingReport(){ super(); headerToCol = new HashMap<>(); headers = new ArrayList<>(); @@ -39,20 +46,41 @@ public GradingReport(){ addHeader(FINAL_GRADE_HEADER); } + /** + * Adds the given header to the list of + * headers the output file should contain. + * + * @param header the header to add. + */ private void addHeader(String header){ headerToCol.put(header, headers.size()); headers.add(header); } + /** + * Adds the given criteria to the list of criteria this should + * report in its output. Not this does not affect how the files + * are graded, it merely sets which grades to report. + * + * @param criteria the criteria to include in this' output. + */ public final void addCriteria(AbstractGradingCriteria criteria){ gradedCriteria.put(criteria.getName(), criteria); addHeader(criteria.getName()); } + /** + * + * @return this comprehensive grading report, + * as an Excel file. Note this file is not yet + * created by this method, so it must still + * be written to a file on the user's computer. + */ public final Workbook getAsWorkBook(){ XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet reportSheet = workbook.createSheet("Grading Report"); + // create the headers XSSFRow headerRow = reportSheet.createRow(0); for(int i = 0; i < headers.size(); i++){ headerRow.createCell(i, CellType.STRING).setCellValue(headers.get(i)); @@ -78,15 +106,15 @@ public final Workbook getAsWorkBook(){ case SRC_FILE_HEADER: data = currExp.getInstructorFile().getFileName(); newCell.setCellValue((String)data); - newCell.setCellStyle(null); break; case CMP_FILE_HEADER: data = currExp.getStudentFile().getFileName(); newCell.setCellValue((String)data); break; case FINAL_GRADE_HEADER: - data = currExp.getFinalGrade(); - newCell.setCellValue((double)data); + CellAddress oneToTheRight = new CellAddress(newCell.getRowIndex(), newCell.getColumnIndex() + 1); + CellAddress endOfRow = new CellAddress(newCell.getRowIndex(), headers.size() - 1); + newCell.setCellFormula(String.format("AVERAGE(%s:%s)", oneToTheRight.formatAsString(), endOfRow.formatAsString())); newCell.setCellStyle(style); break; default: @@ -98,6 +126,36 @@ public final Workbook getAsWorkBook(){ } } + if(newCell != null){ + XSSFSheetConditionalFormatting conditionalFormatting = reportSheet.getSheetConditionalFormatting(); + + XSSFConditionalFormattingRule gotAnA = conditionalFormatting.createConditionalFormattingRule(ComparisonOperator.GE, "0.9"); + gotAnA.createPatternFormatting().setFillBackgroundColor(IndexedColors.GREEN.getIndex()); + + XSSFConditionalFormattingRule gotAB = conditionalFormatting.createConditionalFormattingRule(ComparisonOperator.BETWEEN, "0.9", "0.8"); + gotAB.createPatternFormatting().setFillBackgroundColor(IndexedColors.YELLOW.getIndex()); + + XSSFConditionalFormattingRule gotAC = conditionalFormatting.createConditionalFormattingRule(ComparisonOperator.BETWEEN, "0.8", "0.7"); + gotAC.createPatternFormatting().setFillBackgroundColor(IndexedColors.ORANGE.getIndex()); + + XSSFConditionalFormattingRule gotAnF = conditionalFormatting.createConditionalFormattingRule(ComparisonOperator.LT, "0.7"); + gotAnF.createPatternFormatting().setFillBackgroundColor(IndexedColors.RED.getIndex()); + + // apparently, I can't have more than 3 rules in one batch... + XSSFConditionalFormattingRule[] rules = { + gotAnA, + gotAB, + gotAC + }; + + CellRangeAddress[] ranges = { + // address of first "final grade" cell + CellRangeAddress.valueOf(String.format("C2:%s", newCell.getAddress().formatAsString())) + // address of last cell + }; + conditionalFormatting.addConditionalFormatting(ranges, rules); + conditionalFormatting.addConditionalFormatting(ranges, gotAnF); // ... so use this to include more than 3 rules + } return workbook; } diff --git a/src/main/java/autocadDrawingChecker/autocadData/MatchingAutoCADElements.java b/src/main/java/autocadDrawingChecker/grading/MatchingAutoCADElements.java similarity index 58% rename from src/main/java/autocadDrawingChecker/autocadData/MatchingAutoCADElements.java rename to src/main/java/autocadDrawingChecker/grading/MatchingAutoCADElements.java index 1e054c6..14e9679 100644 --- a/src/main/java/autocadDrawingChecker/autocadData/MatchingAutoCADElements.java +++ b/src/main/java/autocadDrawingChecker/grading/MatchingAutoCADElements.java @@ -1,8 +1,10 @@ -package autocadDrawingChecker.autocadData; +package autocadDrawingChecker.grading; + +import autocadDrawingChecker.data.AutoCADElement; /** * The MatchingAutoCADElements class is - * used to connect AutoCADRows from one + * used to connect AutoCADElements from one * file to another. Essentially, a match * between element a in drawing 1 and * element b in drawing 2 means that, @@ -12,22 +14,21 @@ * other. * * @author Matt Crow - * @param the type of AutoCADElements this contains */ -public class MatchingAutoCADElements { - private final T element1; - private final T element2; +public class MatchingAutoCADElements { + private final AutoCADElement element1; + private final AutoCADElement element2; - public MatchingAutoCADElements(T a, T b){ + public MatchingAutoCADElements(AutoCADElement a, AutoCADElement b){ element1 = a; element2 = b; } - public final T getElement1(){ + public final AutoCADElement getElement1(){ return element1; } - public final T getElement2(){ + public final AutoCADElement getElement2(){ return element2; } diff --git a/src/main/java/autocadDrawingChecker/grading/MathUtil.java b/src/main/java/autocadDrawingChecker/grading/MathUtil.java index 3ae70d2..27268df 100644 --- a/src/main/java/autocadDrawingChecker/grading/MathUtil.java +++ b/src/main/java/autocadDrawingChecker/grading/MathUtil.java @@ -18,7 +18,7 @@ public class MathUtil { * @return a double between 0.0 and 1.0. The greater the difference between * the two given parameters, the greater this number will be. This returns 0.0 * if and only if the two parameters are exactly the same. Any percent - * error greater than 1.0 is rounded down to 1.0. + * error greater than 1.0 is rounded down to 1.0. */ public static double percentError(double whatIExpected, double whatIGot){ double ret = 0.0; diff --git a/src/main/java/autocadDrawingChecker/grading/criteria/AbstractElementCriteria.java b/src/main/java/autocadDrawingChecker/grading/criteria/AbstractElementCriteria.java new file mode 100644 index 0000000..652b507 --- /dev/null +++ b/src/main/java/autocadDrawingChecker/grading/criteria/AbstractElementCriteria.java @@ -0,0 +1,69 @@ +package autocadDrawingChecker.grading.criteria; + +import autocadDrawingChecker.data.AutoCADElement; +import autocadDrawingChecker.data.AutoCADExport; +import autocadDrawingChecker.grading.AutoCADElementMatcher; +import autocadDrawingChecker.grading.MatchingAutoCADElements; +import java.util.Arrays; +import java.util.List; + +/** + * AbstractElementCriteria adds behavior to AbstractGradingCriteria to add + * explicit support for grading exports at the element level, meaning an export + * is graded based on how well its individual elements score. + * + * @author Matt Crow + */ +public interface AbstractElementCriteria extends AbstractGradingCriteria { + public static final String[] ANY_TYPE = new String[0]; + + public abstract double getMatchScore(AutoCADElement e1, AutoCADElement e2); + + /** + * Computes the average match score of elements in the given exports. + * @param exp1 the instructor export + * @param exp2 the student export to grade + * @return the student's net score for this criteria. Ranges from 0.0 to 1.0 + */ + @Override + public default double computeScore(AutoCADExport exp1, AutoCADExport exp2){ + List matches = new AutoCADElementMatcher(exp1, exp2, this::canAccept, this::getMatchScore).findMatches(); + double netScore = matches.stream().map((MatchingAutoCADElements match)->{ + return getMatchScore(match.getElement1(), match.getElement2()); + }).reduce(0.0, Double::sum); + + if(!matches.isEmpty()){ + netScore /= matches.size(); + } + return netScore; + } + + /** + * Checks to see if the given AutoCADElement can -or should- + * be graded by this criteria. This is used by the AutoCADElementMatcher + * to decide if the given element should be graded. + * @see AutoCADElementMatcher + * @param e the AutoCADElement to check + * @return whether or not this criteria can grade e + */ + public default boolean canAccept(AutoCADElement e){ + String[] types = getAllowedTypes(); + String eType = e.getName(); + boolean acceptable = Arrays.equals(types, ANY_TYPE); + for(int i = 0; i < types.length && !acceptable; i++){ + acceptable = eType.equalsIgnoreCase(types[i]); + } + return acceptable; + } + + /** + * + * @return a list of AutoCAD Name column values. + * This is meant to filter out unwanted rows. + * May remove later. + * + * You can make this return AbstractElementCriteria.ANY_TYPE + * to allow all record types. + */ + public abstract String[] getAllowedTypes(); +} diff --git a/src/main/java/autocadDrawingChecker/grading/AbstractGradingCriteria.java b/src/main/java/autocadDrawingChecker/grading/criteria/AbstractGradingCriteria.java similarity index 92% rename from src/main/java/autocadDrawingChecker/grading/AbstractGradingCriteria.java rename to src/main/java/autocadDrawingChecker/grading/criteria/AbstractGradingCriteria.java index f36f06a..a1e4bb8 100644 --- a/src/main/java/autocadDrawingChecker/grading/AbstractGradingCriteria.java +++ b/src/main/java/autocadDrawingChecker/grading/criteria/AbstractGradingCriteria.java @@ -1,6 +1,6 @@ -package autocadDrawingChecker.grading; +package autocadDrawingChecker.grading.criteria; -import autocadDrawingChecker.autocadData.AutoCADExport; +import autocadDrawingChecker.data.AutoCADExport; /** * The AbstractGradingCriteria interface is used to diff --git a/src/main/java/autocadDrawingChecker/grading/criteria/GradingCriteriaLoader.java b/src/main/java/autocadDrawingChecker/grading/criteria/GradingCriteriaLoader.java new file mode 100644 index 0000000..6c18df2 --- /dev/null +++ b/src/main/java/autocadDrawingChecker/grading/criteria/GradingCriteriaLoader.java @@ -0,0 +1,31 @@ +package autocadDrawingChecker.grading.criteria; + +import autocadDrawingChecker.grading.criteria.implementations.LinesPerLayer; +import autocadDrawingChecker.grading.criteria.implementations.LineStart; +import autocadDrawingChecker.grading.criteria.implementations.LineLength; +import autocadDrawingChecker.grading.criteria.implementations.LineEnd; +import autocadDrawingChecker.grading.criteria.implementations.LineCount; +import autocadDrawingChecker.grading.criteria.implementations.LineAngle; +import autocadDrawingChecker.grading.criteria.implementations.TextMatches; +import autocadDrawingChecker.util.AbstractLoader; + +/** + * The GradingCriteriaLoader is used + * to gather all subclasses of AbstractGradingCriteria. + * + * @author Matt Crow + */ +public class GradingCriteriaLoader extends AbstractLoader{ + @Override + public final AbstractGradingCriteria[] getAll(){ + return new AbstractGradingCriteria[]{ + new LineCount(), + new LinesPerLayer(), + new LineLength(), + new LineAngle(), + new LineStart(), + new LineEnd(), + new TextMatches() + }; + } +} diff --git a/src/main/java/autocadDrawingChecker/grading/implementations/AbstractVectorCriteria.java b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/AbstractVectorCriteria.java similarity index 56% rename from src/main/java/autocadDrawingChecker/grading/implementations/AbstractVectorCriteria.java rename to src/main/java/autocadDrawingChecker/grading/criteria/implementations/AbstractVectorCriteria.java index 70dc4b6..4cee1c5 100644 --- a/src/main/java/autocadDrawingChecker/grading/implementations/AbstractVectorCriteria.java +++ b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/AbstractVectorCriteria.java @@ -1,8 +1,7 @@ -package autocadDrawingChecker.grading.implementations; +package autocadDrawingChecker.grading.criteria.implementations; -import autocadDrawingChecker.autocadData.AutoCADElement; -import autocadDrawingChecker.grading.AbstractElementCriteria; -import autocadDrawingChecker.grading.MathUtil; +import autocadDrawingChecker.data.AutoCADElement; +import autocadDrawingChecker.grading.criteria.AbstractElementCriteria; /** * AbstractVectorCriteria is used grading based on some multi-element grading @@ -10,18 +9,17 @@ * but I may change to normalized dot product later. * * @author Matt Crow - * @param the type of AutoCADElement this will grade */ -public interface AbstractVectorCriteria extends AbstractElementCriteria { +public interface AbstractVectorCriteria extends AbstractElementCriteria { @Override - public default double getMatchScore(T e1, T e2){ + public default double getMatchScore(AutoCADElement e1, AutoCADElement e2){ double score = 0.0; double[] v1 = extractVector(e1); double[] v2 = extractVector(e2); int numComponents = Math.min(v1.length, v2.length); for(int i = 0; i < numComponents; i++){ - score += 1.0 - MathUtil.percentError(v1[i], v2[i]); + score += (v1[i] == v2[i]) ? 1.0 : 0.0;//1.0 - MathUtil.percentError(v1[i], v2[i]); } score /= numComponents; // average percent correct @@ -30,9 +28,9 @@ public default double getMatchScore(T e1, T e2){ /** * - * @param e + * @param e the element to extract a vector from * @return the vector interpretation of the given element, * which this will grade on */ - public abstract double[] extractVector(T e); + public abstract double[] extractVector(AutoCADElement e); } diff --git a/src/main/java/autocadDrawingChecker/grading/criteria/implementations/CompareColumn.java b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/CompareColumn.java new file mode 100644 index 0000000..2bbdcc4 --- /dev/null +++ b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/CompareColumn.java @@ -0,0 +1,46 @@ +package autocadDrawingChecker.grading.criteria.implementations; + +import autocadDrawingChecker.data.AutoCADElement; +import autocadDrawingChecker.grading.criteria.AbstractElementCriteria; + +/** + * + * @author Matt + */ +public class CompareColumn implements AbstractElementCriteria { + private final String column; + + /** + * + * @param column the column to compare + */ + public CompareColumn(String column){ + this.column = column; + } + + @Override + public double getMatchScore(AutoCADElement e1, AutoCADElement e2) { + double ret = 0.0; + if(e1.hasAttribute(column) && e2.hasAttribute(column)){ + ret = (e1.getAttribute(column).equals(e2.getAttribute(column))) ? 1.0 : 0.0; + } + return ret; + } + + @Override + public String[] getAllowedTypes() { + return AbstractElementCriteria.ANY_TYPE; + } + + @Override + public String getName() { + // Easier to read when it simply starts with the column name + return String.format("%s column", column); + } + + @Override + public String getDescription() { + return String.format("Grades the student file based on how closely its %s column matches with that of the instructor file", column); + } + +} diff --git a/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineAngle.java b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineAngle.java new file mode 100644 index 0000000..872a663 --- /dev/null +++ b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineAngle.java @@ -0,0 +1,42 @@ +package autocadDrawingChecker.grading.criteria.implementations; + +import autocadDrawingChecker.data.AutoCADElement; +import autocadDrawingChecker.grading.criteria.AbstractElementCriteria; +import autocadDrawingChecker.grading.MathUtil; + +/** + * @author Matt Crow + */ +public class LineAngle implements AbstractElementCriteria { + /** + * + * @param r1 a line in the instructor's file + * @param r2 a line in the student's file + * @return 1.0 if r2 is parallel to r1, else 0.0 + */ + @Override + public double getMatchScore(AutoCADElement r1, AutoCADElement r2){ + int r1Angle = r1.getAttributeInt("angle"); + int r2Angle = r2.getAttributeInt("angle"); + + return (r1Angle == r2Angle || r1Angle == MathUtil.rotate180(r2Angle)) ? 1.0 : 0.0; + // check if they got a line reversed + } + + @Override + public String getDescription() { + return "Grades the students based on how their lines' angles match up with those of the instructor file"; + } + + @Override + public String getName() { + return "Line Angle"; + } + + @Override + public String[] getAllowedTypes() { + return new String[]{"Line"}; + + } + +} diff --git a/src/main/java/autocadDrawingChecker/grading/implementations/LineCount.java b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineCount.java similarity index 59% rename from src/main/java/autocadDrawingChecker/grading/implementations/LineCount.java rename to src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineCount.java index 277c8c9..2f9879b 100644 --- a/src/main/java/autocadDrawingChecker/grading/implementations/LineCount.java +++ b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineCount.java @@ -1,8 +1,7 @@ -package autocadDrawingChecker.grading.implementations; +package autocadDrawingChecker.grading.criteria.implementations; -import autocadDrawingChecker.autocadData.AutoCADExport; -import autocadDrawingChecker.grading.AbstractGradingCriteria; -import autocadDrawingChecker.grading.MathUtil; +import autocadDrawingChecker.data.AutoCADExport; +import autocadDrawingChecker.grading.criteria.AbstractGradingCriteria; /** * @author Matt Crow @@ -11,7 +10,7 @@ public class LineCount implements AbstractGradingCriteria { @Override public double computeScore(AutoCADExport exp1, AutoCADExport exp2) { - return 1.0 - MathUtil.percentError(exp1.size(), exp2.size()); + return (exp1.size() == exp2.size()) ? 1.0 : 0.0;//1.0 - MathUtil.percentError(exp1.size(), exp2.size()); } @Override diff --git a/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineEnd.java b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineEnd.java new file mode 100644 index 0000000..46cb244 --- /dev/null +++ b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineEnd.java @@ -0,0 +1,35 @@ +package autocadDrawingChecker.grading.criteria.implementations; + +import autocadDrawingChecker.data.AutoCADElement; + +/** + * + * @author Matt + */ +public class LineEnd implements AbstractVectorCriteria { + @Override + public String getDescription() { + return "Grades based on how closesly the student's line end points match up with those of the instructor's"; + } + + @Override + public String getName() { + return "Line End"; + } + + @Override + public double[] extractVector(AutoCADElement e) { + double[] v = new double[]{ + e.getAttributeDouble("end x"), + e.getAttributeDouble("end y"), + e.getAttributeDouble("end y") + }; + return v; + } + + @Override + public String[] getAllowedTypes() { + return new String[]{"Line"}; + + } +} diff --git a/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineLength.java b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineLength.java new file mode 100644 index 0000000..2291fdd --- /dev/null +++ b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineLength.java @@ -0,0 +1,50 @@ +package autocadDrawingChecker.grading.criteria.implementations; + +import autocadDrawingChecker.data.AutoCADExport; +import autocadDrawingChecker.data.AutoCADElement; +import autocadDrawingChecker.grading.criteria.AbstractElementCriteria; + +/** + * + * @author Matt Crow + */ +public class LineLength implements AbstractElementCriteria { + + @Override + public double getMatchScore(AutoCADElement r1, AutoCADElement r2){ + return (r1.getAttributeDouble("length") == r2.getAttributeDouble("length")) ? 1.0 : 0.0; + } + + private double getTotalLineLength(AutoCADExport exp){ + return exp.stream().filter((AutoCADElement e)->{ + return this.canAccept(e); + }).map((AutoCADElement imReallyALine)->{ + return imReallyALine.getAttributeDouble("length"); + }).reduce(0.0, Double::sum); + } + + @Override + public double computeScore(AutoCADExport exp1, AutoCADExport exp2) { + double srcTotalLength = getTotalLineLength(exp1); + double cmpTotalLength = getTotalLineLength(exp2); + double avgLenScore = AbstractElementCriteria.super.computeScore(exp1, exp2); + double totalLengthScore = (srcTotalLength == cmpTotalLength) ? 1.0 : 0.0; + return (avgLenScore + totalLengthScore) / 2;//* (1.0 - MathUtil.percentError(srcTotalLength, cmpTotalLength)); + } + + @Override + public String getDescription() { + return "Grades the drawing based on how closely the length of lines match the original drawing"; + } + + @Override + public String getName() { + return "Line Length"; + } + + @Override + public String[] getAllowedTypes() { + return new String[]{"Line"}; + } + +} diff --git a/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineStart.java b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineStart.java new file mode 100644 index 0000000..5b7d905 --- /dev/null +++ b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LineStart.java @@ -0,0 +1,34 @@ +package autocadDrawingChecker.grading.criteria.implementations; + +import autocadDrawingChecker.data.AutoCADElement; + +/** + * + * @author Matt Crow + */ +public class LineStart implements AbstractVectorCriteria { + @Override + public String getDescription() { + return "Grades based on how closesly the student's line start points match up with those of the instructor's"; + } + + @Override + public String getName() { + return "Line Start"; + } + + @Override + public double[] extractVector(AutoCADElement e) { + double[] v = new double[]{ + e.getAttributeDouble("start x"), + e.getAttributeDouble("start y"), + e.getAttributeDouble("start z") + }; + return v; + } + + @Override + public String[] getAllowedTypes(){ + return new String[]{"Line"}; + } +} diff --git a/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LinesPerLayer.java b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LinesPerLayer.java new file mode 100644 index 0000000..2547aa1 --- /dev/null +++ b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/LinesPerLayer.java @@ -0,0 +1,44 @@ +package autocadDrawingChecker.grading.criteria.implementations; + +import autocadDrawingChecker.data.AutoCADExport; +import autocadDrawingChecker.grading.criteria.AbstractGradingCriteria; +import java.util.HashMap; +import java.util.Objects; + +/** + * + * @author Matt Crow + */ +public class LinesPerLayer implements AbstractGradingCriteria { + + @Override + public double computeScore(AutoCADExport exp1, AutoCADExport exp2) { + double score = 0.0; + HashMap srcLines = exp1.getCountsPerColumnValue("Layer");//exp1.getLayerLineCounts(); + HashMap cmpLines = exp2.getCountsPerColumnValue("Layer");//exp2.getLayerLineCounts(); + + for(Object layer : srcLines.keySet()){ + if(cmpLines.containsKey(layer) && Objects.equals(cmpLines.get(layer), srcLines.get(layer))){ + score++; // only gives points on layers which contain the exact same number of lines + } + /* + if(cmpLines.containsKey(layer)){ + score += MathUtil.percentError(srcLines.get(layer), cmpLines.get(layer)); + } + */ + } + return score / srcLines.size(); + //return 1.0 - (score / srcLines.size()); + } + + @Override + public String getDescription() { + return "Grades the student on how close the number of lines in each of their layers matches up with the comparison file's equivalent layer."; + } + + @Override + public String getName() { + return "Lines per layer"; + } + +} diff --git a/src/main/java/autocadDrawingChecker/grading/criteria/implementations/TextMatches.java b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/TextMatches.java new file mode 100644 index 0000000..29401d2 --- /dev/null +++ b/src/main/java/autocadDrawingChecker/grading/criteria/implementations/TextMatches.java @@ -0,0 +1,46 @@ +package autocadDrawingChecker.grading.criteria.implementations; + +import autocadDrawingChecker.data.AutoCADElement; +import autocadDrawingChecker.grading.criteria.AbstractElementCriteria; + +/** + * + * @author Matt + */ +public class TextMatches implements AbstractElementCriteria { + + @Override + public double getMatchScore(AutoCADElement row1, AutoCADElement row2){ + // "if it matches exactly, you get 100%, else, 0." + String text1 = null; + String text2 = null; + try { + text1 = row1.getAttributeString("contents"); + } catch (NullPointerException ex){ + text1 = row1.getAttributeString("value"); + } + try { + text2 = row2.getAttributeString("contents"); + } catch (NullPointerException ex){ + text2 = row2.getAttributeString("value"); + } + + return (text1.equals(text2)) ? 1.0 : 0.0; + } + + @Override + public String getDescription() { + return "Checks to see if the text in a student file's text components matches that of the professor's export"; + } + + @Override + public String getName() { + return "Text Matches"; + } + + @Override + public String[] getAllowedTypes() { + return new String[]{"Text", "MText"}; + } + +} diff --git a/src/main/java/autocadDrawingChecker/grading/implementations/CheckDims.java b/src/main/java/autocadDrawingChecker/grading/implementations/CheckDims.java deleted file mode 100644 index 1a02faf..0000000 --- a/src/main/java/autocadDrawingChecker/grading/implementations/CheckDims.java +++ /dev/null @@ -1,43 +0,0 @@ -package autocadDrawingChecker.grading.implementations; - -import autocadDrawingChecker.autocadData.AutoCADDimension; -import autocadDrawingChecker.autocadData.AutoCADElement; -import autocadDrawingChecker.grading.AbstractElementCriteria; -import autocadDrawingChecker.grading.MathUtil; - -/** - * - * @author Matt - */ -public class CheckDims implements AbstractElementCriteria { - - @Override - public String getName() { - return "Check Dimensions"; - } - - @Override - public double getMatchScore(AutoCADDimension d1, AutoCADDimension d2){ - double ret = 0.0; - - // grade on 3 things... - ret += 1.0 - MathUtil.percentError(d1.getDynamicDimension(), d2.getDynamicDimension()); - ret += (d1.getDimensionStyle().equals(d2.getDimensionStyle())) ? 1.0 : 0.0; - ret += (d1.getTextDefinedSize().equals(d2.getTextDefinedSize())) ? 1.0 : 0.0; - // ... so take the average - ret /= 3.0; - - return ret; - } - - @Override - public String getDescription() { - return "Checks the dimensions of a student export against those of the instructor"; - } - - @Override - public AutoCADDimension cast(AutoCADElement e) { - return (e instanceof AutoCADDimension) ? (AutoCADDimension)e : null; - } - -} diff --git a/src/main/java/autocadDrawingChecker/grading/implementations/LineAngle.java b/src/main/java/autocadDrawingChecker/grading/implementations/LineAngle.java deleted file mode 100644 index 3fb5bd0..0000000 --- a/src/main/java/autocadDrawingChecker/grading/implementations/LineAngle.java +++ /dev/null @@ -1,35 +0,0 @@ -package autocadDrawingChecker.grading.implementations; - -import autocadDrawingChecker.autocadData.AutoCADLine; -import autocadDrawingChecker.autocadData.AutoCADElement; -import autocadDrawingChecker.grading.AbstractElementCriteria; -import autocadDrawingChecker.grading.MathUtil; - -/** - * @author Matt Crow - */ -public class LineAngle implements AbstractElementCriteria { - - @Override - public double getMatchScore(AutoCADLine r1, AutoCADLine r2){ - double percErr = MathUtil.percentError(r1.getAngle(), r2.getAngle()); - double percErrRot = MathUtil.percentError(r1.getAngle(), MathUtil.rotate180(r2.getAngle())); - return 1.0 - Math.min(percErr, percErrRot); // check if they just got the line reversed - } - - @Override - public String getDescription() { - return "Grades the students based on how their lines' angles match up with those of the instructor file"; - } - - @Override - public String getName() { - return "Line Angle"; - } - - @Override - public AutoCADLine cast(AutoCADElement e) { - return (e instanceof AutoCADLine) ? (AutoCADLine)e : null; - } - -} diff --git a/src/main/java/autocadDrawingChecker/grading/implementations/LineEnd.java b/src/main/java/autocadDrawingChecker/grading/implementations/LineEnd.java deleted file mode 100644 index 4bec4a2..0000000 --- a/src/main/java/autocadDrawingChecker/grading/implementations/LineEnd.java +++ /dev/null @@ -1,34 +0,0 @@ -package autocadDrawingChecker.grading.implementations; - -import autocadDrawingChecker.autocadData.AutoCADLine; -import autocadDrawingChecker.autocadData.AutoCADElement; - -/** - * - * @author Matt - */ -public class LineEnd implements AbstractVectorCriteria { - @Override - public String getDescription() { - return "Grades based on how closesly the student's line end points match up with those of the instructor's"; - } - - @Override - public String getName() { - return "Line End"; - } - - @Override - public double[] extractVector(AutoCADLine e) { - double[] v = new double[AutoCADLine.DIMENSION_COUNT]; - for(int i = 0; i < v.length; i++){ - v[i] = e.getEnd(i); - } - return v; - } - - @Override - public AutoCADLine cast(AutoCADElement e) { - return (e instanceof AutoCADLine) ? (AutoCADLine)e : null; - } -} diff --git a/src/main/java/autocadDrawingChecker/grading/implementations/LineLength.java b/src/main/java/autocadDrawingChecker/grading/implementations/LineLength.java deleted file mode 100644 index f3c9c69..0000000 --- a/src/main/java/autocadDrawingChecker/grading/implementations/LineLength.java +++ /dev/null @@ -1,55 +0,0 @@ -package autocadDrawingChecker.grading.implementations; - -import autocadDrawingChecker.autocadData.AutoCADExport; -import autocadDrawingChecker.autocadData.AutoCADLine; -import autocadDrawingChecker.autocadData.AutoCADElement; -import autocadDrawingChecker.grading.AbstractElementCriteria; -import autocadDrawingChecker.grading.MathUtil; - -/** - * Looks like this works! - * - * @author Matt Crow - */ -public class LineLength implements AbstractElementCriteria { - - @Override - public double getMatchScore(AutoCADLine r1, AutoCADLine r2){ - return 1.0 - MathUtil.percentError(r1.getLength(), r2.getLength()); - } - - private double getTotalLineLength(AutoCADExport exp){ - return exp.stream().filter((AutoCADElement e)->{ - return e instanceof AutoCADLine; - }).map((AutoCADElement imReallyALine)->{ - return (AutoCADLine)imReallyALine; - }).map((AutoCADLine line)->{ - return line.getLength(); - }).reduce(0.0, Double::sum); - } - - @Override - public double computeScore(AutoCADExport exp1, AutoCADExport exp2) { - double srcTotalLength = getTotalLineLength(exp1); - double cmpTotalLength = getTotalLineLength(exp2); - double avgLenScore = AbstractElementCriteria.super.computeScore(exp1, exp2); - - return avgLenScore * (1.0 - MathUtil.percentError(srcTotalLength, cmpTotalLength)); - } - - @Override - public String getDescription() { - return "Grades the drawing based on how closely the length of lines match the original drawing"; - } - - @Override - public String getName() { - return "Line Length"; - } - - @Override - public AutoCADLine cast(AutoCADElement e) { - return (e instanceof AutoCADLine) ? (AutoCADLine)e : null; - } - -} diff --git a/src/main/java/autocadDrawingChecker/grading/implementations/LineStart.java b/src/main/java/autocadDrawingChecker/grading/implementations/LineStart.java deleted file mode 100644 index 1603a66..0000000 --- a/src/main/java/autocadDrawingChecker/grading/implementations/LineStart.java +++ /dev/null @@ -1,34 +0,0 @@ -package autocadDrawingChecker.grading.implementations; - -import autocadDrawingChecker.autocadData.AutoCADLine; -import autocadDrawingChecker.autocadData.AutoCADElement; - -/** - * - * @author Matt Crow - */ -public class LineStart implements AbstractVectorCriteria { - @Override - public String getDescription() { - return "Grades based on how closesly the student's line start points match up with those of the instructor's"; - } - - @Override - public String getName() { - return "Line Start"; - } - - @Override - public double[] extractVector(AutoCADLine e) { - double[] v = new double[AutoCADLine.DIMENSION_COUNT]; - for(int i = 0; i < v.length; i++){ - v[i] = e.getStart(i); - } - return v; - } - - @Override - public AutoCADLine cast(AutoCADElement e) { - return (e instanceof AutoCADLine) ? (AutoCADLine)e : null; - } -} diff --git a/src/main/java/autocadDrawingChecker/grading/implementations/LinesPerLayer.java b/src/main/java/autocadDrawingChecker/grading/implementations/LinesPerLayer.java deleted file mode 100644 index 689ba52..0000000 --- a/src/main/java/autocadDrawingChecker/grading/implementations/LinesPerLayer.java +++ /dev/null @@ -1,38 +0,0 @@ -package autocadDrawingChecker.grading.implementations; - -import autocadDrawingChecker.autocadData.AutoCADExport; -import autocadDrawingChecker.grading.AbstractGradingCriteria; -import autocadDrawingChecker.grading.MathUtil; -import java.util.HashMap; - -/** - * - * @author Matt Crow - */ -public class LinesPerLayer implements AbstractGradingCriteria { - - @Override - public double computeScore(AutoCADExport exp1, AutoCADExport exp2) { - double score = 0.0; - HashMap srcLines = exp1.getLayerLineCounts(); - HashMap cmpLines = exp2.getLayerLineCounts(); - - for(String layer : srcLines.keySet()){ - if(cmpLines.containsKey(layer)){ - score += MathUtil.percentError(srcLines.get(layer), cmpLines.get(layer)); - } - } - return 1.0 - (score / srcLines.size()); - } - - @Override - public String getDescription() { - return "Grades the student on how close the number of lines in each of their layers matches up with the comparison file's equivalent layer."; - } - - @Override - public String getName() { - return "Lines per layer"; - } - -} diff --git a/src/main/java/autocadDrawingChecker/grading/implementations/TextMatches.java b/src/main/java/autocadDrawingChecker/grading/implementations/TextMatches.java deleted file mode 100644 index fc2979c..0000000 --- a/src/main/java/autocadDrawingChecker/grading/implementations/TextMatches.java +++ /dev/null @@ -1,34 +0,0 @@ -package autocadDrawingChecker.grading.implementations; - -import autocadDrawingChecker.autocadData.AutoCADElement; -import autocadDrawingChecker.autocadData.AutoCADText; -import autocadDrawingChecker.grading.AbstractElementCriteria; - -/** - * - * @author Matt - */ -public class TextMatches implements AbstractElementCriteria { - - @Override - public double getMatchScore(AutoCADText row1, AutoCADText row2){ - // currently just a basic "if it matches exactly, you get 100%, else, 0. - return (row1.getTextContents().equals(row2.getTextContents())) ? 1.0 : 0.0; - } - - @Override - public String getDescription() { - return "Checks to see if the text in a student file's text components matches that of the professor's export"; - } - - @Override - public String getName() { - return "Text Matches"; - } - - @Override - public AutoCADText cast(AutoCADElement e) { - return (e instanceof AutoCADText) ? (AutoCADText)e : null; - } - -} diff --git a/src/main/java/autocadDrawingChecker/grading/package-info.java b/src/main/java/autocadDrawingChecker/grading/package-info.java index aa369a8..cbb94ca 100644 --- a/src/main/java/autocadDrawingChecker/grading/package-info.java +++ b/src/main/java/autocadDrawingChecker/grading/package-info.java @@ -8,7 +8,7 @@ * can grade students' assignments; and * the Grader class, which uses these criteria. * - * @see autocadDrawingChecker.grading.AbstractGradingCriteria + * @see autocadDrawingChecker.grading.criteria.AbstractGradingCriteria * @see autocadDrawingChecker.grading.Grader */ package autocadDrawingChecker.grading; diff --git a/src/main/java/autocadDrawingChecker/gui/PageRenderer.java b/src/main/java/autocadDrawingChecker/gui/PageRenderer.java index d1fcc7b..7e2bd5f 100644 --- a/src/main/java/autocadDrawingChecker/gui/PageRenderer.java +++ b/src/main/java/autocadDrawingChecker/gui/PageRenderer.java @@ -6,6 +6,7 @@ import autocadDrawingChecker.logging.Logger; import java.awt.BorderLayout; import java.awt.CardLayout; +import java.awt.Color; import java.util.ArrayList; import java.util.HashMap; import javax.swing.JButton; @@ -54,6 +55,8 @@ public PageRenderer(){ }); buttons.add(prevButton); nextButton = new JButton("Next Step"); + nextButton.setBackground(Color.GREEN); + nextButton.setOpaque(true); nextButton.addActionListener((e)->{ tryNextPage(); }); diff --git a/src/main/java/autocadDrawingChecker/gui/ViewController.java b/src/main/java/autocadDrawingChecker/gui/ViewController.java index 362236d..30b1153 100644 --- a/src/main/java/autocadDrawingChecker/gui/ViewController.java +++ b/src/main/java/autocadDrawingChecker/gui/ViewController.java @@ -1,7 +1,8 @@ package autocadDrawingChecker.gui; -import autocadDrawingChecker.files.FileChooserUtil; +import autocadDrawingChecker.util.FileChooserUtil; import autocadDrawingChecker.logging.Logger; +import autocadDrawingChecker.start.Application; import java.awt.Toolkit; import javax.swing.JFrame; import javax.swing.JMenu; @@ -24,11 +25,13 @@ public ViewController(){ Logger.logError(ex); } + setTitle(Application.APP_NAME); + JMenuBar menuBar = new JMenuBar(); JMenu logMenu = new JMenu("Log"); JMenuItem saveLog = new JMenuItem("Save Log"); saveLog.addActionListener((e)->{ - FileChooserUtil.askCreateTextFile("Where do you want to save this log?", Logger.getLog()); + FileChooserUtil.askWhereSaveLog("Where do you want to save this log?", Logger.getLog()); }); logMenu.add(saveLog); menuBar.add(logMenu); diff --git a/src/main/java/autocadDrawingChecker/gui/chooseCriteria/ChooseCriteriaPage.java b/src/main/java/autocadDrawingChecker/gui/chooseCriteria/ChooseCriteriaPage.java index 3914bf6..d9dfa73 100644 --- a/src/main/java/autocadDrawingChecker/gui/chooseCriteria/ChooseCriteriaPage.java +++ b/src/main/java/autocadDrawingChecker/gui/chooseCriteria/ChooseCriteriaPage.java @@ -1,6 +1,6 @@ package autocadDrawingChecker.gui.chooseCriteria; -import autocadDrawingChecker.grading.AbstractGradingCriteria; +import autocadDrawingChecker.grading.criteria.AbstractGradingCriteria; import autocadDrawingChecker.gui.AbstractPage; import java.awt.GridLayout; import java.util.List; diff --git a/src/main/java/autocadDrawingChecker/gui/chooseCriteria/CriteriaSelectionList.java b/src/main/java/autocadDrawingChecker/gui/chooseCriteria/CriteriaSelectionList.java index 4cf5b0f..5490354 100644 --- a/src/main/java/autocadDrawingChecker/gui/chooseCriteria/CriteriaSelectionList.java +++ b/src/main/java/autocadDrawingChecker/gui/chooseCriteria/CriteriaSelectionList.java @@ -1,10 +1,11 @@ package autocadDrawingChecker.gui.chooseCriteria; -import autocadDrawingChecker.grading.AbstractGradingCriteria; -import autocadDrawingChecker.grading.GradingCriteriaLoader; +import autocadDrawingChecker.grading.criteria.AbstractGradingCriteria; +import autocadDrawingChecker.start.Application; import java.awt.BorderLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; @@ -36,7 +37,7 @@ public CriteriaSelectionList(){ gbc.weighty = 0.0; // this make things "stick" together gbc.fill = GridBagConstraints.HORIZONTAL; gbc.anchor = GridBagConstraints.PAGE_START; - GradingCriteriaLoader.getAllCriteria().stream().map((AbstractGradingCriteria criteria)->{ + Arrays.stream(Application.getInstance().getGradedCriteria()).map((AbstractGradingCriteria criteria)->{ return new CriteriaToggle(criteria); }).forEach((CriteriaToggle component)->{ criteriaList.put(component.getCriteria().getName(), component); diff --git a/src/main/java/autocadDrawingChecker/gui/chooseCriteria/CriteriaToggle.java b/src/main/java/autocadDrawingChecker/gui/chooseCriteria/CriteriaToggle.java index 31ad5c9..3ed5853 100644 --- a/src/main/java/autocadDrawingChecker/gui/chooseCriteria/CriteriaToggle.java +++ b/src/main/java/autocadDrawingChecker/gui/chooseCriteria/CriteriaToggle.java @@ -1,6 +1,6 @@ package autocadDrawingChecker.gui.chooseCriteria; -import autocadDrawingChecker.grading.AbstractGradingCriteria; +import autocadDrawingChecker.grading.criteria.AbstractGradingCriteria; import autocadDrawingChecker.start.Application; import java.awt.BorderLayout; import javax.swing.JCheckBox; diff --git a/src/main/java/autocadDrawingChecker/gui/chooseFiles/AbstractExcelFileChooser.java b/src/main/java/autocadDrawingChecker/gui/chooseFiles/AbstractExcelFileChooser.java index 2cd03e2..136a062 100644 --- a/src/main/java/autocadDrawingChecker/gui/chooseFiles/AbstractExcelFileChooser.java +++ b/src/main/java/autocadDrawingChecker/gui/chooseFiles/AbstractExcelFileChooser.java @@ -1,8 +1,21 @@ package autocadDrawingChecker.gui.chooseFiles; import java.awt.BorderLayout; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; @@ -13,7 +26,7 @@ * @author Matt * @param the type returned by AbstractExcelFileChooser::getSelected() */ -public abstract class AbstractExcelFileChooser extends JComponent implements ActionListener { +public abstract class AbstractExcelFileChooser extends JComponent implements ActionListener, DropTargetListener { private T selected; private final JTextArea text; private final String popupTitle; @@ -40,6 +53,10 @@ public AbstractExcelFileChooser(String title, String popupText){ add(bottom, BorderLayout.PAGE_END); popupTitle = popupText; + // https://blog.christoffer.online/2011-01-09-drag-and-dropping-files-to-java-desktop-application/ + new DropTarget(this, this); + new DropTarget(text, this); + selected = null; } @@ -56,6 +73,14 @@ public void actionPerformed(ActionEvent e){ selectButtonPressed(); } + /** + * Note that this method is called + * by Application, so don't call + * Application.getInstance().getData().set... + * in this method + * + * @param sel the file or files to select + */ protected void setSelected(T sel){ selected = sel; } @@ -65,5 +90,51 @@ public final T getSelected(){ } public abstract boolean isFileSelected(); - protected abstract void selectButtonPressed(); + protected abstract void selectButtonPressed(); + + /** + * Invoke this whenever the user selects a file + * or files. + * + * @param sel the file or files the user selected + */ + protected abstract void userSelectedFile(T sel); + protected abstract void filesDropped(List files); + + @Override + public void dragEnter(DropTargetDragEvent dtde) {} + + @Override + public void dragOver(DropTargetDragEvent dtde) {} + + @Override + public void dropActionChanged(DropTargetDragEvent dtde) {} + + @Override + public void dragExit(DropTargetEvent dte) {} + + @Override + public void drop(DropTargetDropEvent dtde) { + dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); + Transferable data = dtde.getTransferable(); + DataFlavor[] flavors = data.getTransferDataFlavors(); + for(DataFlavor flavor : flavors){ + if(flavor.isFlavorJavaFileListType()){ + try { + @SuppressWarnings("unchecked") + List files = (List)data.getTransferData(flavor); + List acceptableFiles = files.stream().filter((File f)->{ + return canAccept(f); + }).collect(Collectors.toList()); + filesDropped(acceptableFiles); + } catch (UnsupportedFlavorException ex) { + ex.printStackTrace(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } + } + + protected abstract boolean canAccept(File f); } diff --git a/src/main/java/autocadDrawingChecker/gui/chooseFiles/ChooseFilesPage.java b/src/main/java/autocadDrawingChecker/gui/chooseFiles/ChooseFilesPage.java index bcee9fb..592d106 100644 --- a/src/main/java/autocadDrawingChecker/gui/chooseFiles/ChooseFilesPage.java +++ b/src/main/java/autocadDrawingChecker/gui/chooseFiles/ChooseFilesPage.java @@ -3,8 +3,10 @@ import autocadDrawingChecker.gui.AbstractPage; import java.awt.GridLayout; import java.io.File; +import java.awt.Color; +import java.awt.Dimension; import javax.swing.JOptionPane; -import javax.swing.JPanel; +import javax.swing.JSplitPane; /** * The ChooseFilesPage is where the user selects which files they want to grade. @@ -18,14 +20,18 @@ public class ChooseFilesPage extends AbstractPage { public ChooseFilesPage() { super("Choose files to compare"); setLayout(new GridLayout(1, 1)); + setBackground(Color.CYAN); - JPanel choosers = new JPanel(); - choosers.setLayout(new GridLayout(1, 2, 20, 20)); srcChooser = new SourceExcelFileChooser("Instructor File", "choose the instructor Excel file"); - choosers.add(srcChooser); cmpChooser = new CompareExcelFileChooser("Student Files", "choose one or more student files, or a whole folder of them"); - choosers.add(cmpChooser); - add(choosers); + + Dimension d = new Dimension(50, 50); + srcChooser.setMinimumSize(d); + cmpChooser.setMinimumSize(d); + JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, srcChooser, cmpChooser); + split.setResizeWeight(0.5); + + add(split); } public final void setSrcFile(File f){ diff --git a/src/main/java/autocadDrawingChecker/gui/chooseFiles/CompareExcelFileChooser.java b/src/main/java/autocadDrawingChecker/gui/chooseFiles/CompareExcelFileChooser.java index b5dc707..fc9732c 100644 --- a/src/main/java/autocadDrawingChecker/gui/chooseFiles/CompareExcelFileChooser.java +++ b/src/main/java/autocadDrawingChecker/gui/chooseFiles/CompareExcelFileChooser.java @@ -1,10 +1,11 @@ package autocadDrawingChecker.gui.chooseFiles; -import autocadDrawingChecker.files.FileChooserUtil; -import autocadDrawingChecker.files.FileType; +import autocadDrawingChecker.util.FileChooserUtil; +import autocadDrawingChecker.util.FileType; import autocadDrawingChecker.start.Application; import java.io.File; import java.util.Arrays; +import java.util.List; /** * @@ -31,9 +32,24 @@ protected final void setSelected(File[] fs){ @Override protected void selectButtonPressed() { FileChooserUtil.askChooseFiles(getPopupTitle(), FileType.EXCEL_OR_FOLDER, (File[] fs)->{ - setSelected(fs); - String[] absPaths = Arrays.stream(fs).map((f)->f.getAbsolutePath()).toArray((size)->new String[size]); - Application.getInstance().getData().setStudentFilePaths(absPaths); + userSelectedFile(fs); }); } + + @Override + protected void filesDropped(List files) { + userSelectedFile(files.toArray(new File[files.size()])); + } + + @Override + protected void userSelectedFile(File[] sel) { + setSelected(sel); + String[] absPaths = Arrays.stream(sel).map((f)->f.getAbsolutePath()).toArray((s)->new String[s]); + Application.getInstance().getData().setStudentFilePaths(absPaths); + } + + @Override + protected boolean canAccept(File f) { + return f.isDirectory() || FileType.EXCEL.fileIsOfThisType(f); + } } diff --git a/src/main/java/autocadDrawingChecker/gui/chooseFiles/SourceExcelFileChooser.java b/src/main/java/autocadDrawingChecker/gui/chooseFiles/SourceExcelFileChooser.java index a2329d5..3a1eb51 100644 --- a/src/main/java/autocadDrawingChecker/gui/chooseFiles/SourceExcelFileChooser.java +++ b/src/main/java/autocadDrawingChecker/gui/chooseFiles/SourceExcelFileChooser.java @@ -1,15 +1,16 @@ package autocadDrawingChecker.gui.chooseFiles; -import autocadDrawingChecker.files.FileChooserUtil; -import autocadDrawingChecker.files.FileType; +import autocadDrawingChecker.util.FileChooserUtil; +import autocadDrawingChecker.util.FileType; import autocadDrawingChecker.start.Application; import java.io.File; +import java.util.List; /** * * @author Matt */ -public class SourceExcelFileChooser extends AbstractExcelFileChooser { +public class SourceExcelFileChooser extends AbstractExcelFileChooser{ public SourceExcelFileChooser(String title, String popupText) { super(title, popupText); } @@ -28,8 +29,25 @@ protected final void setSelected(File f){ @Override protected void selectButtonPressed() { FileChooserUtil.askChooseFile(getPopupTitle(), FileType.EXCEL, (File f)->{ - setSelected(f); - Application.getInstance().getData().setInstructorFilePath(f.getAbsolutePath()); + userSelectedFile(f); }); } + + @Override + protected void filesDropped(List files) { + if(!files.isEmpty()){ + userSelectedFile(files.get(0)); + } + } + + @Override + protected void userSelectedFile(File sel) { + setSelected(sel); + Application.getInstance().getData().setInstructorFilePath(sel.getAbsolutePath()); + } + + @Override + protected boolean canAccept(File f) { + return FileType.EXCEL.fileIsOfThisType(f); + } } diff --git a/src/main/java/autocadDrawingChecker/gui/runPage/GradingReportSaver.java b/src/main/java/autocadDrawingChecker/gui/runPage/GradingReportSaver.java index 0ed0698..b7b1bf1 100644 --- a/src/main/java/autocadDrawingChecker/gui/runPage/GradingReportSaver.java +++ b/src/main/java/autocadDrawingChecker/gui/runPage/GradingReportSaver.java @@ -1,7 +1,7 @@ package autocadDrawingChecker.gui.runPage; -import autocadDrawingChecker.files.FileChooserUtil; -import autocadDrawingChecker.files.FileType; +import autocadDrawingChecker.util.FileChooserUtil; +import autocadDrawingChecker.util.FileType; import autocadDrawingChecker.grading.GradingReport; import autocadDrawingChecker.logging.Logger; import java.io.File; @@ -21,23 +21,7 @@ */ public class GradingReportSaver { public static final void saveReport(GradingReport report, Consumer andThen){ - FileChooserUtil.askChooseFile("Where would you like to save this report to?", FileType.EXCEL, (File f)->{ - String fStrPath = f.getAbsolutePath(); - if(!fStrPath.endsWith(".xlsx")){ - // rename it if it's extension is incorrect - Path fPath = f.toPath(); - String pathStrIWant = fStrPath + ".xlsx"; - try { - if(Files.exists(fPath)){ - // rename it if it already exists. - Files.move(fPath, Paths.get(pathStrIWant)); - } - // regardless, redirect f to point to this new file path - f = new File(pathStrIWant); - } catch(Exception ex){ - Logger.logError(ex); - } - } + FileChooserUtil.askSaveFile("Where would you like to save this report to?", "Grading Report", "xlsx", (File f)->{ try (FileOutputStream out = new FileOutputStream(f)) { report.getAsWorkBook().write(out); } catch (FileNotFoundException ex) { diff --git a/src/main/java/autocadDrawingChecker/logging/Logger.java b/src/main/java/autocadDrawingChecker/logging/Logger.java index ea3b261..c12dd5c 100644 --- a/src/main/java/autocadDrawingChecker/logging/Logger.java +++ b/src/main/java/autocadDrawingChecker/logging/Logger.java @@ -25,6 +25,10 @@ public static final void log(String msg){ MSG_LISTENS.forEach((ml)->ml.messageLogged(msg)); } + public static final void log(Object obj){ + log(obj.toString()); + } + public static final void log(double d){ log(Double.toString(d)); } diff --git a/src/main/java/autocadDrawingChecker/start/Application.java b/src/main/java/autocadDrawingChecker/start/Application.java index e5302aa..3459ab5 100644 --- a/src/main/java/autocadDrawingChecker/start/Application.java +++ b/src/main/java/autocadDrawingChecker/start/Application.java @@ -1,5 +1,6 @@ package autocadDrawingChecker.start; +import autocadDrawingChecker.grading.criteria.AbstractGradingCriteria; import autocadDrawingChecker.grading.Grader; import autocadDrawingChecker.grading.GradingReport; import autocadDrawingChecker.gui.PageRenderer; @@ -17,13 +18,17 @@ public class Application { private final DrawingCheckerData data; private ViewController window; + private AbstractGradingCriteria[] criteria; + private static Application instance; + public static final String APP_NAME = "AutoCAD Drawing Checker"; private Application(){ if(instance != null){ throw new ExceptionInInitializerError("Application is supposed to be a singleton: No more than one instance!"); } - data = new DrawingCheckerData(); + data = new DrawingCheckerData(); + criteria = new AbstractGradingCriteria[0]; } public static final Application getInstance(){ @@ -33,6 +38,19 @@ public static final Application getInstance(){ return instance; } + public final Application setLoadedCriteria(AbstractGradingCriteria[] criteria){ + this.criteria = criteria; + data.clearCriteria(); + for(AbstractGradingCriteria crit : criteria){ + data.addCriteria(crit); + } + return this; + } + + public final AbstractGradingCriteria[] getGradedCriteria(){ + return criteria; + } + public final Application createGui(){ if(window != null){ window.dispose(); diff --git a/src/main/java/autocadDrawingChecker/start/DrawingCheckerData.java b/src/main/java/autocadDrawingChecker/start/DrawingCheckerData.java index c46787e..ed99cea 100644 --- a/src/main/java/autocadDrawingChecker/start/DrawingCheckerData.java +++ b/src/main/java/autocadDrawingChecker/start/DrawingCheckerData.java @@ -1,7 +1,7 @@ package autocadDrawingChecker.start; -import autocadDrawingChecker.grading.AbstractGradingCriteria; -import autocadDrawingChecker.grading.GradingCriteriaLoader; +import autocadDrawingChecker.grading.criteria.AbstractGradingCriteria; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map.Entry; @@ -22,10 +22,6 @@ public DrawingCheckerData(){ studentFilePaths = new String[0]; selectedCriteria = new HashMap<>(); nameToCriteria = new HashMap<>(); - GradingCriteriaLoader.getAllCriteria().forEach((c) -> { - selectedCriteria.put(c.getName(), Boolean.TRUE); - nameToCriteria.put(c.getName(), c); - }); } public final boolean isInstructorFilePathSet(){ @@ -48,6 +44,17 @@ public final String getInstructorFilePath(){ public final String[] getStudentFilePaths(){ return studentFilePaths; } + + public final void clearCriteria(){ + selectedCriteria.clear(); + nameToCriteria.clear(); + } + + public final void addCriteria(AbstractGradingCriteria crit){ + selectedCriteria.put(crit.getName(), Boolean.TRUE); + nameToCriteria.put(crit.getName(), crit); + } + public final List getSelectedCriteria(){ return selectedCriteria.entrySet().stream().filter((Entry nameToIsSelected)->{ return nameToIsSelected.getValue(); // the "isSelected" part of the entry diff --git a/src/main/java/autocadDrawingChecker/start/DrawingCheckerProperties.java b/src/main/java/autocadDrawingChecker/start/DrawingCheckerProperties.java index 709bd4d..083982f 100644 --- a/src/main/java/autocadDrawingChecker/start/DrawingCheckerProperties.java +++ b/src/main/java/autocadDrawingChecker/start/DrawingCheckerProperties.java @@ -1,6 +1,6 @@ package autocadDrawingChecker.start; -import autocadDrawingChecker.grading.AbstractGradingCriteria; +import autocadDrawingChecker.grading.criteria.AbstractGradingCriteria; import java.util.List; import java.util.Properties; diff --git a/src/main/java/autocadDrawingChecker/start/Main.java b/src/main/java/autocadDrawingChecker/start/Main.java index 4ae2c48..d7c7362 100644 --- a/src/main/java/autocadDrawingChecker/start/Main.java +++ b/src/main/java/autocadDrawingChecker/start/Main.java @@ -1,7 +1,11 @@ package autocadDrawingChecker.start; +import autocadDrawingChecker.grading.criteria.GradingCriteriaLoader; +import java.util.Arrays; +import java.util.HashSet; + /** - * Main servers as the starting point for the + * Main serves as the starting point for the * application. Future versions may add support * for running the application from the command * line, with no GUI. @@ -9,17 +13,40 @@ * @author Matt Crow */ public class Main { + private final Application app; + private Main(){ + app = Application.getInstance(); + } + /** - * @param args the command line arguments + * @param args the command line arguments. + * How do I set these in Netbeans? + * + * To pass arguments to Gradle, use + * + * gradle run --args="args go in here" */ public static void main(String[] args) { - Application app = Application.getInstance(); - /* - app.getData() - .setInstructorFilePath("C:\\Users\\Matt\\Desktop\\AutoCAD Drawing Checker\\sample files to work with\\Check Sample - Master File.xls.xlsx") - .setStudentFilePaths("C:\\Users\\Matt\\Desktop\\AutoCAD Drawing Checker\\sample files to work with") - .setCriteriaSelected(new CheckDims(), false); - */ - app.createGui(); + Main main = new Main(); + main.app.setLoadedCriteria(new GradingCriteriaLoader().getAll()); + + + + System.out.println("Args are " + Arrays.toString(args)); + + HashSet argSet = new HashSet<>(); + for(String arg : args){ + argSet.add(arg.toLowerCase()); + } + boolean debug = argSet.contains("--debug"); + + if(debug){ + main.app.getData() + .setInstructorFilePath("C:\\Users\\Matt\\Desktop\\AutoCAD Drawing Checker\\sample files to work with\\sample\\Check Sample - Master File.xls.xlsx") + .setStudentFilePaths("C:\\Users\\Matt\\Desktop\\AutoCAD Drawing Checker\\sample files to work with\\sample"); + System.out.println(main.app.grade().toString()); + } else { + main.app.createGui(); + } } } diff --git a/src/main/java/autocadDrawingChecker/util/AbstractLoader.java b/src/main/java/autocadDrawingChecker/util/AbstractLoader.java new file mode 100644 index 0000000..d143451 --- /dev/null +++ b/src/main/java/autocadDrawingChecker/util/AbstractLoader.java @@ -0,0 +1,28 @@ +package autocadDrawingChecker.util; + +import java.util.List; + +/** + * I will want to implement this + * class to explicitly load classes, + * not implicitly. Essentially, this + * will handle stuff like "get all + * GradingCriteria we want" and + * "get all AutoCAD elements" etc. + * @author Matt Crow + * @param the type of object + * this will load + */ +public abstract class AbstractLoader { + /** + * This method should return constructed + * objects of all subclasses + * of T that are relevant to this loader. + * These objects returned should not have + * mutable states, as they will be accessible + * by the whole program. + * + * @return a list of objects of T. + */ + public abstract T[] getAll(); +} diff --git a/src/main/java/autocadDrawingChecker/files/FileChooserUtil.java b/src/main/java/autocadDrawingChecker/util/FileChooserUtil.java similarity index 61% rename from src/main/java/autocadDrawingChecker/files/FileChooserUtil.java rename to src/main/java/autocadDrawingChecker/util/FileChooserUtil.java index bbcaff1..f7e1fdd 100644 --- a/src/main/java/autocadDrawingChecker/files/FileChooserUtil.java +++ b/src/main/java/autocadDrawingChecker/util/FileChooserUtil.java @@ -1,11 +1,15 @@ -package autocadDrawingChecker.files; +package autocadDrawingChecker.util; import autocadDrawingChecker.logging.Logger; +import autocadDrawingChecker.start.Application; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.function.Consumer; import javax.swing.JFileChooser; import javax.swing.filechooser.FileNameExtensionFilter; @@ -17,6 +21,8 @@ public class FileChooserUtil { private final JFileChooser chooser; + public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("MM-dd-uuuu_hh_mm_a"); + public FileChooserUtil(String dialogTitle, int dialogType, int fileSelectionMode){ chooser = new JFileChooser(); chooser.setDialogTitle(dialogTitle); @@ -41,15 +47,36 @@ public static void askChooseFiles(String dialogTitle, FileType allowedType, Cons andThen.accept(fcu.chooser.getSelectedFiles()); } } - public static void askCreateTextFile(String dialogTitle, String textFileContents){ - FileChooserUtil fcu = new FileChooserUtil(dialogTitle, JFileChooser.SAVE_DIALOG, JFileChooser.FILES_AND_DIRECTORIES); + + public static void askSaveFile(String dialogTitle, String fileNameMiddle, String ext, Consumer andThen){ + FileChooserUtil fcu = new FileChooserUtil(dialogTitle, JFileChooser.SAVE_DIALOG, JFileChooser.DIRECTORIES_ONLY); if(fcu.chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION){ + File folder = fcu.chooser.getSelectedFile(); + LocalDateTime currDate = LocalDateTime.now(); + String dateStr = currDate.format(DATE_FORMAT); + String desiredFilePath = Paths.get( + folder.getAbsolutePath(), + String.format("%s %s %s.%s", Application.APP_NAME, dateStr, fileNameMiddle, ext) + ).toString(); + File newFile = new File(desiredFilePath); + + int nextNum = 1; + while(newFile.exists()){ + newFile = new File(desiredFilePath.replace(String.format(".%s", ext), String.format("-%d.%s", nextNum, ext))); + nextNum++; + } + andThen.accept(newFile); + } + } + + public static void askWhereSaveLog(String dialogTitle, String textFileContents){ + askSaveFile(dialogTitle, "log", "txt", (f)->{ // may move this to a file writer util - try (BufferedWriter buff = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fcu.chooser.getSelectedFile())))) { + try (BufferedWriter buff = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f)))) { buff.write(textFileContents); } catch (IOException ex) { Logger.logError(ex); } - } + }); } } diff --git a/src/main/java/autocadDrawingChecker/files/FileType.java b/src/main/java/autocadDrawingChecker/util/FileType.java similarity index 50% rename from src/main/java/autocadDrawingChecker/files/FileType.java rename to src/main/java/autocadDrawingChecker/util/FileType.java index 800c1e2..9c3fb57 100644 --- a/src/main/java/autocadDrawingChecker/files/FileType.java +++ b/src/main/java/autocadDrawingChecker/util/FileType.java @@ -1,4 +1,6 @@ -package autocadDrawingChecker.files; +package autocadDrawingChecker.util; + +import java.io.File; /** * @author Matt @@ -22,4 +24,17 @@ public final String getName(){ public final String[] getExtensions(){ return extensions; } + + public final boolean fileIsOfThisType(File f){ + String path = f.getAbsolutePath(); + String[] split = path.split("\\."); + boolean isType = false; + if(split.length != 0){ + String ext = split[split.length - 1]; + for(int i = 0; i < this.extensions.length && !isType; i++){ + isType = ext.equalsIgnoreCase(this.extensions[i]); + } + } + return isType; + } }