Просмотр исходного кода

refactor the project as an independent service

Miaomz 6 лет назад
Родитель
Сommit
fc5d22ede4
27 измененных файлов с 345 добавлено и 193 удалено
  1. 1 0
      src/main/java/com/moekr/moocoder/logic/AsyncWrapper.java
  2. 7 2
      src/main/java/com/moekr/moocoder/logic/api/PMDWrapper.java
  3. 8 0
      src/main/java/com/moekr/moocoder/logic/service/ProjectService.java
  4. 4 3
      src/main/java/com/moekr/moocoder/logic/service/impl/CallGraphVisualizer.java
  5. 3 6
      src/main/java/com/moekr/moocoder/logic/service/impl/CodeMetricServiceImpl.java
  6. 14 15
      src/main/java/com/moekr/moocoder/logic/service/impl/OOPMetricAnalyzer.java
  7. 26 9
      src/main/java/com/moekr/moocoder/logic/service/impl/ProjectPath.java
  8. 59 0
      src/main/java/com/moekr/moocoder/logic/service/impl/ProjectServiceImpl.java
  9. 0 65
      src/main/java/com/moekr/moocoder/logic/service/impl/VisualizationServiceStub.java
  10. 2 2
      src/main/java/com/moekr/moocoder/logic/vo/CallGraphVO.java
  11. 0 46
      src/main/java/com/moekr/moocoder/logic/vo/CodeVO.java
  12. 1 0
      src/main/java/com/moekr/moocoder/util/FileUtil.java
  13. 5 0
      src/main/java/com/moekr/moocoder/util/MavenUtil.java
  14. 39 0
      src/main/java/com/moekr/moocoder/util/ToolKit.java
  15. 16 0
      src/main/java/com/moekr/moocoder/util/enums/ProblemType.java
  16. 10 1
      src/main/java/com/moekr/moocoder/web/ProjectController.java
  17. 38 1
      src/main/java/com/moekr/moocoder/web/VisualizationController.java
  18. 19 0
      src/main/java/com/moekr/moocoder/web/dto/CodeDTO.java
  19. 0 0
      src/main/resources/data/.gitkeep
  20. 9 6
      src/main/resources/templates/views/callgraph.html
  21. 0 24
      src/test/java/com/moekr/moocoder/logic/service/VisualizationServiceTest.java
  22. 7 3
      src/test/java/com/moekr/moocoder/logic/service/impl/CallGraphVisualizerTest.java
  23. 3 3
      src/test/java/com/moekr/moocoder/logic/service/impl/OOPMetricAnalyzerTest.java
  24. 29 7
      src/test/java/com/moekr/moocoder/logic/service/impl/ProjectServiceImplTest.java
  25. 36 0
      src/test/java/com/moekr/moocoder/util/ToolKitTest.java
  26. 9 0
      src/test/resources/mock/a.json
  27. 0 0
      src/test/resources/mock/jackson-datatype-guava.txt

+ 1 - 0
src/main/java/com/moekr/moocoder/logic/AsyncWrapper.java

@@ -15,6 +15,7 @@ public class AsyncWrapper {
 			method.invoke();
 		} catch (Exception e) {
 			log.error("进行异步调用时发生异常[" + e.getClass() + "]:" + e.getMessage());
+			log.error(e);
 		}
 	}
 

+ 7 - 2
src/main/java/com/moekr/moocoder/logic/api/PMDWrapper.java

@@ -1,16 +1,21 @@
 package com.moekr.moocoder.logic.api;
 
+import lombok.extern.apachecommons.CommonsLog;
 import net.sourceforge.pmd.PMD;
 import net.sourceforge.pmd.PMDConfiguration;
 import net.sourceforge.pmd.cpd.CPD;
 import org.springframework.stereotype.Component;
 
-import java.io.*;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
 
 /**
  * @author miaomuzhi
  * @since 2020/5/24
  */
+@CommonsLog
 @Component
 public class PMDWrapper {
 
@@ -69,7 +74,7 @@ public class PMDWrapper {
                 CPD.main(arguments);
             }
         } catch (IOException e) {
-            e.printStackTrace();
+            log.warn(e);
         } finally {
             System.setOut(original);
         }

+ 8 - 0
src/main/java/com/moekr/moocoder/logic/service/ProjectService.java

@@ -1,5 +1,6 @@
 package com.moekr.moocoder.logic.service;
 
+import com.moekr.moocoder.web.dto.CodeDTO;
 import org.springframework.web.multipart.MultipartFile;
 
 /**
@@ -15,6 +16,13 @@ public interface ProjectService {
      */
     boolean addProject(String project, MultipartFile projectFile);
 
+    /**
+     * generate a random UUID and save the project of codeVO under the directory of 'projectDir/UUID'
+     * @param codeDTO project files
+     * @return UUID, or null if failed
+     */
+    String addProject(CodeDTO codeDTO);
+
     /**
      * determine if a project has been added
      * @param project project name, formed as 'a.jar' or 'a'

+ 4 - 3
src/main/java/com/moekr/moocoder/logic/service/impl/CallGraphVisualizer.java

@@ -7,8 +7,8 @@ import com.moekr.moocoder.logic.vo.CallGraphVO;
 import com.moekr.moocoder.util.enums.Granularity;
 import com.moekr.moocoder.util.enums.Source;
 import lombok.NonNull;
+import lombok.extern.apachecommons.CommonsLog;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Component;
 
 import java.util.*;
@@ -17,6 +17,7 @@ import java.util.*;
  * @author miaomuzhi
  * @since 2020/4/12
  */
+@CommonsLog
 @Component
 public class CallGraphVisualizer {
     private WalaController walaController;
@@ -171,12 +172,12 @@ public class CallGraphVisualizer {
 
     private CallGraphVO getForceDirectedGraphOfMethods(String project) {
         try {
-            String srcDir = ProjectPath.getProjectFullPath(project);
+            String srcDir = ProjectPath.getBytecodeFullPath(project);
             String dstDir = ProjectPath.getOutputFullPath(project);
             List<String> walaOutput = walaController.getWalaOutput(srcDir, dstDir, Source.CLASS_FILE);
             return walaToForceDirected(walaOutput);
         } catch (Exception e) {
-            e.printStackTrace();
+            log.warn(e);
             return null;
         }
     }

+ 3 - 6
src/main/java/com/moekr/moocoder/logic/service/impl/CodeMetricServiceImpl.java

@@ -1,7 +1,6 @@
 package com.moekr.moocoder.logic.service.impl;
 
 import com.moekr.moocoder.logic.api.PMDController;
-import com.moekr.moocoder.logic.api.WalaController;
 import com.moekr.moocoder.logic.service.CodeMetricService;
 import com.moekr.moocoder.logic.vo.*;
 import com.moekr.moocoder.util.FileUtil;
@@ -20,14 +19,12 @@ import java.util.Set;
  */
 @Component
 public class CodeMetricServiceImpl implements CodeMetricService{
-    private WalaController walaController;
     private OOPMetricAnalyzer analyzer;
     private CallGraphVisualizer visualizer;
     private PMDController pmdController;
 
     @Autowired
-    public CodeMetricServiceImpl(WalaController walaController, OOPMetricAnalyzer analyzer, CallGraphVisualizer visualizer, PMDController pmdController) {
-        this.walaController = walaController;
+    public CodeMetricServiceImpl(OOPMetricAnalyzer analyzer, CallGraphVisualizer visualizer, PMDController pmdController) {
         this.analyzer = analyzer;
         this.visualizer = visualizer;
         this.pmdController = pmdController;
@@ -105,7 +102,7 @@ public class CodeMetricServiceImpl implements CodeMetricService{
 
     @Override
     public PMDRelatedVO getPMDRelatedMetric(String project) {
-        String srcDir = ProjectPath.getSrcCodeDir(project);
+        String srcDir = ProjectPath.getSrcCodeFullPath(project);
         String dstDir = ProjectPath.pmdOutputDir;
         if (pmdController.needAnalysis(srcDir, dstDir, project)){
             pmdController.makeReport(srcDir, dstDir, project);
@@ -115,7 +112,7 @@ public class CodeMetricServiceImpl implements CodeMetricService{
 
     @Override
     public String getDetailedProject(String project) {
-        String srcDir = ProjectPath.getSrcCodeDir(project);
+        String srcDir = ProjectPath.getSrcCodeFullPath(project);
         String dstDir = ProjectPath.pmdOutputDir;
         if (pmdController.needAnalysis(srcDir, dstDir, project)){
             pmdController.makeReport(srcDir, dstDir, project);

+ 14 - 15
src/main/java/com/moekr/moocoder/logic/service/impl/OOPMetricAnalyzer.java

@@ -2,41 +2,39 @@ package com.moekr.moocoder.logic.service.impl;
 
 import com.ibm.wala.ipa.callgraph.CallGraphBuilderCancelException;
 import com.moekr.moocoder.logic.api.WalaController;
-import com.moekr.moocoder.logic.service.ProjectService;
 import com.moekr.moocoder.logic.vo.CallGraphEdgeVO;
 import com.moekr.moocoder.logic.vo.CallGraphNodeVO;
 import com.moekr.moocoder.logic.vo.CallGraphVO;
 import com.moekr.moocoder.logic.vo.OOPMetricVO;
 import com.moekr.moocoder.util.enums.Granularity;
 import com.moekr.moocoder.util.enums.Source;
+import lombok.extern.apachecommons.CommonsLog;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.util.Pair;
 import org.springframework.stereotype.Component;
 
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.*;
 
+import static com.moekr.moocoder.logic.service.impl.ProjectPath.getBytecodeFullPath;
 import static com.moekr.moocoder.logic.service.impl.ProjectPath.getOutputFullPath;
-import static com.moekr.moocoder.logic.service.impl.ProjectPath.getProjectFullPath;
-import static java.lang.Double.NaN;
-import static java.lang.Double.parseDouble;
 
 /**
  * @author miaomuzhi
  * @since 2020/4/23
  */
 @Component
+@CommonsLog
 public class OOPMetricAnalyzer {
     private CallGraphVisualizer visualizer;
     private WalaController walaController;
-    private ProjectService projectService;
 
     @Autowired
-    public OOPMetricAnalyzer(CallGraphVisualizer visualizer, WalaController walaController, ProjectService projectService){
+    public OOPMetricAnalyzer(CallGraphVisualizer visualizer, WalaController walaController){
         this.visualizer = visualizer;
         this.walaController = walaController;
-        this.projectService = projectService;
     }
 
 
@@ -47,27 +45,28 @@ public class OOPMetricAnalyzer {
      * @return OOP metrics, null if project doesn't exist
      */
     OOPMetricVO analyzeMetric(String project, String group) {
-        if (!projectService.containsProject(project)){
-            return null;
-        }
-
         try {
-            String srcDir = getProjectFullPath(project);
+            String srcDir = getBytecodeFullPath(project);
             String dstDir = getOutputFullPath(project);
 
+            File srcDirFile = new File(srcDir);
+            if (!srcDirFile.exists()) {
+                throw new FileNotFoundException(project + " doesn't exist!");
+            }
+
             Pair<Integer, Integer> pair = walaController.countAbstractness(srcDir, group, Source.CLASS_FILE);
             double abstractness = pair.getFirst() / (double)pair.getSecond();
             List<String> walaOutput = walaController.getWalaOutput(srcDir, dstDir, Source.CLASS_FILE);
             return analyzeMetricExceptAbstractness(walaOutput, group, abstractness);
         } catch (IOException|CallGraphBuilderCancelException e){
-            e.printStackTrace();
+            log.warn(e);
             return null;
         }
     }
 
     List<String> getProjectGroups(String project, Granularity g) {
         try {
-            String srcDir = getProjectFullPath(project);
+            String srcDir = getBytecodeFullPath(project);
             String dstDir = getOutputFullPath(project);
             List<String> walaOutput = walaController.getWalaOutput(srcDir, dstDir, Source.CLASS_FILE);
             CallGraphVO graphVO = visualizer.walaToForceDirected(walaOutput);
@@ -92,7 +91,7 @@ public class OOPMetricAnalyzer {
                 throw new IllegalArgumentException("granularity of groups can't be " + g);
             }
         } catch (IOException|CallGraphBuilderCancelException e) {
-            e.printStackTrace();
+            log.warn(e);
             return new ArrayList<>();
         }
     }

+ 26 - 9
src/main/java/com/moekr/moocoder/logic/service/impl/ProjectPath.java

@@ -7,32 +7,49 @@ import java.io.File;
  * @since 2020/5/8
  */
 public class ProjectPath {
-    static String projectDir;
-    static String walaOutputDir;
-    static String tempZipDir;
-    static String srcCodeDir;
+    private static String projectDir;
+    private static String walaOutputDir;
     static String pmdOutputDir;
 
+    //deprecated paths
+    private static String projectBytecodeDir;
+    private static String srcCodeDir;
+    static String tempZipDir;
+
     static {
-        projectDir = ProjectPath.class.getResource("/wala/projects").getFile();
+        projectDir = ProjectPath.class.getResource("/data").getFile();
         walaOutputDir = ProjectPath.class.getResource("/wala/outputs").getFile();
+        pmdOutputDir = ProjectPath.class.getResource("/wala/pmd").getFile();
+
+        //deprecated paths
         tempZipDir = ProjectPath.class.getResource("/wala/zips").getFile();
+        projectBytecodeDir = ProjectPath.class.getResource("/wala/projects").getFile();
         srcCodeDir = ProjectPath.class.getResource("/wala/src_code").getFile();
-        pmdOutputDir = ProjectPath.class.getResource("/wala/pmd").getFile();
     }
 
     private ProjectPath() {}
 
-    static String getProjectFullPath(String project){
+    static String getProjectFullPath(String project) {
         return projectDir + File.separator + project;
     }
 
+    static String getBytecodeFullPath(String project){
+        return projectDir + File.separator + project + "/target/classes";
+    }
+
+    static String getSrcCodeFullPath(String project) {
+        return projectDir + File.separator + project + "/src/main/java" ;
+    }
+
     static String getOutputFullPath(String project){
         return walaOutputDir + File.separator + project;
     }
 
-    static String getSrcCodeDir(String project) {
-        return srcCodeDir + File.separator + project;
+
+    static String getProjectBytecodeStubFullPath(String project) {
+        return projectBytecodeDir + File.separator + project;
     }
 
+    static String getSrcCodeStubDir(String project) {return srcCodeDir + File.separator + project; }
+
 }

+ 59 - 0
src/main/java/com/moekr/moocoder/logic/service/impl/ProjectServiceImpl.java

@@ -1,7 +1,16 @@
 package com.moekr.moocoder.logic.service.impl;
 
+import com.moekr.moocoder.logic.AsyncWrapper;
+import com.moekr.moocoder.logic.service.CodeMetricService;
 import com.moekr.moocoder.logic.service.ProjectService;
+import com.moekr.moocoder.logic.service.VisualizationService;
+import com.moekr.moocoder.util.FileUtil;
+import com.moekr.moocoder.util.MavenUtil;
+import com.moekr.moocoder.util.ToolKit;
+import com.moekr.moocoder.util.enums.Granularity;
+import com.moekr.moocoder.web.dto.CodeDTO;
 import org.apache.tomcat.util.http.fileupload.FileUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -9,6 +18,8 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.Base64;
+import java.util.Map;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
@@ -21,6 +32,18 @@ import static com.moekr.moocoder.logic.service.impl.ProjectPath.tempZipDir;
  */
 @Component
 public class ProjectServiceImpl implements ProjectService {
+    private AsyncWrapper wrapper;
+    private VisualizationService visualizationService;
+    private CodeMetricService codeMetricService;
+
+    @Autowired
+    public ProjectServiceImpl(AsyncWrapper wrapper, VisualizationService visualizationService, CodeMetricService codeMetricService) {
+        this.wrapper = wrapper;
+        this.visualizationService = visualizationService;
+        this.codeMetricService = codeMetricService;
+    }
+
+
     @Override
     public boolean addProject(String project, MultipartFile projectFile) {
         if (projectFile == null || project == null){
@@ -35,6 +58,42 @@ public class ProjectServiceImpl implements ProjectService {
         }
     }
 
+    @Override
+    public String addProject(CodeDTO codeDTO) {
+        String uuid = ToolKit.randomUUID();
+        String rootDirPath = ProjectPath.getProjectFullPath(uuid);
+        File rootDir = new File(rootDirPath);
+
+        if (rootDir.exists()) {  // reassign if uuid duplicates
+            return addProject(codeDTO);
+        }
+
+        if (!rootDir.mkdir()) {
+            return null;
+        }
+
+        for (Map.Entry<String, String> entry : codeDTO.getFileList().entrySet()) {
+            String fileFullPath = rootDirPath + entry.getKey();
+            String fileContent = entry.getValue();
+
+            byte[] decodedBytes = Base64.getDecoder().decode(fileContent);
+            String decodedString = new String(decodedBytes);
+
+            if (!FileUtil.writeFile(fileFullPath, decodedString)){
+                return null;
+            }
+        }
+
+        wrapper.asyncInvoke(() -> {
+            //build, analyze, cache
+            MavenUtil.buildMavenProject(rootDirPath);
+            visualizationService.callGraphAsForceDirected(uuid, Granularity.METHOD);
+            codeMetricService.getPMDRelatedMetric(uuid);
+        });
+
+        return uuid;
+    }
+
     @Override
     public boolean containsProject(String project) {
         String projectPath = getProjectFullPath(project);

+ 0 - 65
src/main/java/com/moekr/moocoder/logic/service/impl/VisualizationServiceStub.java

@@ -1,65 +0,0 @@
-package com.moekr.moocoder.logic.service.impl;
-
-import com.moekr.moocoder.logic.service.VisualizationService;
-import com.moekr.moocoder.logic.vo.CallGraphVO;
-import com.moekr.moocoder.logic.vo.OOPMetricVO;
-import com.moekr.moocoder.util.enums.Granularity;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.util.Pair;
-import org.springframework.stereotype.Component;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author miaomuzhi
- * @since 2020/4/13
- */
-@Deprecated
-public class VisualizationServiceStub implements VisualizationService {
-    private CallGraphVisualizer visualizer;
-
-    @Autowired
-    public VisualizationServiceStub(CallGraphVisualizer visualizer) {
-        this.visualizer = visualizer;
-    }
-
-    @Override
-    public CallGraphVO callGraphAsForceDirected(String id, Granularity g) {
-        File f = new File(getClass().getResource("/mock/jackson-datatype-guava.txt").getFile());
-        List<String> lines = new ArrayList<>();
-        try (FileReader fr = new FileReader(f)){
-            try (BufferedReader br = new BufferedReader(fr)){
-                String line;
-                while ((line = br.readLine()) != null){
-                    lines.add(line);
-                }
-            }
-            return visualizer.walaToForceDirected(lines);
-        } catch (IOException e){
-            e.printStackTrace();
-            return null;
-        }
-    }
-
-    @Override
-    public Pair<String, String> diffForCommit(String firstId, String secondId) {
-        String first = "The new id \nThe new life\n The new challenge";
-        String second = "The new id\nThe new challenge";
-        return Pair.of(first, second);
-    }
-
-    @Override
-    public OOPMetricVO analyzeOOPMetrics(String project, String group) {
-        return new OOPMetricVO(1,1,0.5,0.5,0);
-    }
-
-    @Override
-    public List<String> getProjectGroups(String project, Granularity g) {
-        return null;
-    }
-}

+ 2 - 2
src/main/java/com/moekr/moocoder/logic/vo/CallGraphVO.java

@@ -14,6 +14,6 @@ import java.util.List;
 @EqualsAndHashCode
 @ToString
 public class CallGraphVO {
-    List<CallGraphNodeVO> nodes;
-    List<CallGraphEdgeVO> links;
+    private List<CallGraphNodeVO> nodes;
+    private List<CallGraphEdgeVO> links;
 }

+ 0 - 46
src/main/java/com/moekr/moocoder/logic/vo/CodeVO.java

@@ -1,46 +0,0 @@
-package com.moekr.moocoder.logic.vo;
-
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.moekr.moocoder.util.serializer.TimestampLocalDateTimeSerializer;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Map;
-
-@Data
-public class CodeVO {
-    private Integer examId;
-    @JsonSerialize(using = TimestampLocalDateTimeSerializer.class)
-    private LocalDateTime fetchAt;
-    @JsonSerialize(using = TimestampLocalDateTimeSerializer.class)
-    private LocalDateTime startAt;
-    @JsonSerialize(using = TimestampLocalDateTimeSerializer.class)
-    private LocalDateTime endAt;
-    private List<NestedCodeItemVO> codeList;
-
-    public CodeVO(Integer examId, LocalDateTime fetchAt, LocalDateTime startAt, LocalDateTime endAt, List<NestedCodeItemVO> codeList){
-        this.examId = examId;
-        this.fetchAt = fetchAt;
-        this.startAt = startAt;
-        this.endAt = endAt;
-        this.codeList = codeList;
-    }
-
-    @Data
-    public static class NestedCodeItemVO {
-        private Integer questionId;
-        private String language;
-        private String projectName;
-        private Map<String, String> editable;
-        private Map<String, String> uneditable;
-
-        public NestedCodeItemVO(Integer questionId, String language, String projectName, Map<String, String> editable, Map<String, String> uneditable) {
-            this.questionId = questionId;
-            this.language = language;
-            this.projectName = projectName;
-            this.editable = editable;
-            this.uneditable = uneditable;
-        }
-    }
-}

+ 1 - 0
src/main/java/com/moekr/moocoder/util/FileUtil.java

@@ -12,6 +12,7 @@ import java.io.*;
 public class FileUtil {
     private FileUtil() {}
 
+
     public static boolean writeFile(String path, String content) {
         File file = new File(path);
         if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {//if parent file doesn't exist and fail to make dir

+ 5 - 0
src/main/java/com/moekr/moocoder/util/MavenUtil.java

@@ -18,6 +18,11 @@ public class MavenUtil {
 
     private MavenUtil() {}
 
+    /**
+     * execute 'mvn compile' command
+     * @param srcDir absolute path of the maven project's root directory
+     * @return if it builds successfully
+     */
     public static boolean buildMavenProject(String srcDir){
         try {
             InvocationRequest request = new DefaultInvocationRequest();

+ 39 - 0
src/main/java/com/moekr/moocoder/util/ToolKit.java

@@ -0,0 +1,39 @@
+package com.moekr.moocoder.util;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.springframework.http.HttpStatus;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.UUID;
+
+public abstract class ToolKit {
+	public static final String VERSION = "0.4.5";
+
+	private ToolKit() {
+		throw new IllegalStateException("Utility class");
+	}
+
+	public static String format(Exception exception) {
+		return "[" + exception.getClass().getName() + "]:" + exception.getMessage();
+	}
+
+	public static HttpStatus httpStatus(HttpServletRequest request) {
+		Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
+		if (statusCode == null) {
+			return HttpStatus.INTERNAL_SERVER_ERROR;
+		}
+		try {
+			return HttpStatus.valueOf(statusCode);
+		} catch (Exception e) {
+			return HttpStatus.INTERNAL_SERVER_ERROR;
+		}
+	}
+
+	public static String randomUUID() {
+		return UUID.randomUUID().toString().replace("-", "");
+	}
+
+	public static String randomPassword() {
+		return RandomStringUtils.randomAlphanumeric(12);
+	}
+}

+ 16 - 0
src/main/java/com/moekr/moocoder/util/enums/ProblemType.java

@@ -0,0 +1,16 @@
+package com.moekr.moocoder.util.enums;
+
+/**
+ * @author miaomuzhi
+ * @since 2020/5/31
+ */
+public enum ProblemType {
+    JAVA,
+    JAVA_DATABASE,
+    JAVA_DATA_STRUCTURE,
+    JAVA_COMPUTER_ORGANIZATION,
+    JAVA_SOFTWARE_DESIGN,
+    PYTHON, JAVA_COVERAGE,
+    PYTHON_COVERAGE,
+    JAVA_MUTATION
+}

+ 10 - 1
src/main/java/com/moekr/moocoder/web/ProjectController.java

@@ -1,9 +1,13 @@
 package com.moekr.moocoder.web;
 
 import com.moekr.moocoder.logic.service.ProjectService;
+import com.moekr.moocoder.web.dto.CodeDTO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.multipart.MultipartFile;
 
 /**
@@ -24,4 +28,9 @@ public class ProjectController {
     public @ResponseBody boolean uploadProject(@RequestParam MultipartFile myfile){
         return projectService.addProject(myfile.getOriginalFilename(), myfile);
     }
+
+    @PostMapping("/upload_file")
+    public @ResponseBody String uploadProject(@RequestParam CodeDTO codeDTO) {
+        return projectService.addProject(codeDTO);
+    }
 }

+ 38 - 1
src/main/java/com/moekr/moocoder/web/VisualizationController.java

@@ -1,6 +1,8 @@
 package com.moekr.moocoder.web;
 
 import com.moekr.moocoder.logic.service.VisualizationService;
+import com.moekr.moocoder.logic.vo.CallGraphEdgeVO;
+import com.moekr.moocoder.logic.vo.CallGraphNodeVO;
 import com.moekr.moocoder.logic.vo.CallGraphVO;
 import com.moekr.moocoder.logic.vo.OOPMetricVO;
 import com.moekr.moocoder.util.enums.Granularity;
@@ -30,7 +32,10 @@ public class VisualizationController {
 
     @GetMapping("/call_graph")
     public CallGraphVO callGraph(@RequestParam String id, @RequestParam String granularity){
-        return visualizationService.callGraphAsForceDirected(id, Granularity.valueOf(granularity));
+        Granularity g = Granularity.valueOf(granularity);
+        CallGraphVO ret =  visualizationService.callGraphAsForceDirected(id, g);
+        processCallGraphVO(ret, g);
+        return ret;
     }
 
     @GetMapping("/commit_diff")
@@ -50,4 +55,36 @@ public class VisualizationController {
         }
         return visualizationService.getProjectGroups(projectId, Granularity.PACKAGE);
     }
+
+
+    private void processCallGraphVO(final CallGraphVO callGraphVO, Granularity g) {
+        if (g != Granularity.METHOD) {  // remove the package name which every class belongs to
+            for (CallGraphNodeVO nodeVO : callGraphVO.getNodes()) {
+                String[] notations = nodeVO.getId().split("/");
+                nodeVO.setId(notations[notations.length - 1]);
+            }
+
+            for (CallGraphEdgeVO edgeVO : callGraphVO.getLinks()) {
+                String[] targetNotations = edgeVO.getTarget().split("/");
+                edgeVO.setTarget(targetNotations[targetNotations.length - 1]);
+                String[] sourceNotations = edgeVO.getSource().split("/");
+                edgeVO.setSource(sourceNotations[sourceNotations.length - 1]);
+            }
+        }
+
+        for (CallGraphNodeVO nodeVO : callGraphVO.getNodes()) {
+            if (nodeVO.getId().startsWith("L")){
+                nodeVO.setId(nodeVO.getId().substring(1));
+            }
+        }
+
+        for (CallGraphEdgeVO edgeVO : callGraphVO.getLinks()) {
+            if (edgeVO.getTarget().startsWith("L")){
+                edgeVO.setTarget(edgeVO.getTarget().substring(1));
+            }
+            if (edgeVO.getSource().startsWith("L")){
+                edgeVO.setSource(edgeVO.getSource().substring(1));
+            }
+        }
+    }
 }

+ 19 - 0
src/main/java/com/moekr/moocoder/web/dto/CodeDTO.java

@@ -0,0 +1,19 @@
+package com.moekr.moocoder.web.dto;
+
+import com.moekr.moocoder.util.enums.ProblemType;
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * @author miaomuzhi
+ * @since 2020/5/31
+ */
+@Data
+public class CodeDTO {
+    //问题类型
+    ProblemType problemType;
+
+    //文件名-文件内容
+    Map<String,String> fileList;
+}

+ 0 - 0
src/main/resources/data/.gitkeep


+ 9 - 6
src/main/resources/templates/views/callgraph.html

@@ -39,7 +39,7 @@
     <input type="button" value="提交" onclick="drawCallGraph(document.getElementById('project_id').value, document.getElementById('granularity').value)"/>
 </form>
 
-<svg width="960" height="800"></svg>
+<svg width="1024" height="640"></svg>
 </body>
 <script type="text/javascript" th:src="@{../../lib/vue/vue.js}"></script>
 <script type="text/javascript" th:src="@{../../lib/vue/vue-resource.js}"></script>
@@ -53,8 +53,9 @@
         svg = d3.selectAll("body").append("svg");
         //reconfigure the width and height
         svg = document.getElementsByTagName('svg')[0];
-        svg.setAttribute('width', 960);
-        svg.setAttribute('height', 800);
+
+        svg.setAttribute('width', 1024);
+        svg.setAttribute('height', 640);
 
         svg = d3.select("svg");
         var width = +svg.attr("width"),
@@ -67,10 +68,12 @@
         var simulation = d3.forceSimulation()
             .force("link", d3.forceLink().id(function(d) { return d.id; }))
             .force("charge", d3.forceManyBody())
-            .force("center", d3.forceCenter(width / 2, height / 2));
+            .force("center", d3.forceCenter(width/2, height/2));
+
 
         function dragstarted(d) {
-            if (!d3.event.active) simulation.alphaTarget(0.3).restart();
+            if (!d3.event.active)
+                simulation.alphaTarget(0.3).restart();
             d.fx = d.x;
             d.fy = d.y;
         }
@@ -108,7 +111,7 @@
                 .enter().append("g");
 
             var circles = node.append("circle")
-                .attr("r", 5)
+                .attr("r", 7)
                 .attr("fill", function(d) { return color(d.group); })
                 .call(d3.drag()
                     .on("start", dragstarted)

+ 0 - 24
src/test/java/com/moekr/moocoder/logic/service/VisualizationServiceTest.java

@@ -1,24 +0,0 @@
-package com.moekr.moocoder.logic.service;
-
-import com.alibaba.fastjson.JSON;
-import com.moekr.moocoder.logic.service.impl.CallGraphVisualizer;
-import com.moekr.moocoder.logic.service.impl.VisualizationServiceStub;
-import org.junit.jupiter.api.Test;
-import org.springframework.data.util.Pair;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-/**
- * @author miaomuzhi
- * @since 2020/4/24
- */
-class VisualizationServiceTest {
-
-    @Test
-    void diffForCommit() {
-        VisualizationService service = new VisualizationServiceStub(new CallGraphVisualizer(null));
-        Pair<String, String> pair = service.diffForCommit(null, null);
-        String json = JSON.toJSONString(pair);
-        System.out.println(json);
-    }
-}

+ 7 - 3
src/test/java/com/moekr/moocoder/logic/service/impl/CallGraphVisualizerTest.java

@@ -5,11 +5,15 @@ import com.moekr.moocoder.logic.vo.CallGraphNodeVO;
 import com.moekr.moocoder.logic.vo.CallGraphVO;
 import org.junit.jupiter.api.Test;
 
-import java.io.*;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
 import java.lang.reflect.Method;
 import java.util.*;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * @author miaomuzhi
@@ -20,7 +24,7 @@ class CallGraphVisualizerTest {
     @Test
     void walaToForceDirected() throws Exception{
         List<String> lines = new ArrayList<>();
-        File f = new File(getClass().getResource("/jackson-datatype-guava.txt").getFile());
+        File f = new File(getClass().getResource("/mock/jackson-datatype-guava.txt").getFile());
         try (FileReader fr = new FileReader(f)){
             try (BufferedReader br = new BufferedReader(fr)){
                 String line;

+ 3 - 3
src/test/java/com/moekr/moocoder/logic/service/impl/OOPMetricAnalyzerTest.java

@@ -12,7 +12,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import static java.lang.Float.NaN;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
  * @author miaomuzhi
@@ -23,7 +23,7 @@ class OOPMetricAnalyzerTest {
     @Test
     void analyzeMetric() throws Exception{
         List<String> lines = new ArrayList<>();
-        File f = new File(getClass().getResource("/jackson-datatype-guava.txt").getFile());
+        File f = new File(getClass().getResource("/mock/jackson-datatype-guava.txt").getFile());
         try (FileReader fr = new FileReader(f)){
             try (BufferedReader br = new BufferedReader(fr)){
                 String line;
@@ -32,7 +32,7 @@ class OOPMetricAnalyzerTest {
                 }
 
                 CallGraphVisualizer visualizer = new CallGraphVisualizer(null);
-                OOPMetricAnalyzer analyzer = new OOPMetricAnalyzer(visualizer, null, null);
+                OOPMetricAnalyzer analyzer = new OOPMetricAnalyzer(visualizer, null);
 
                 Method method = OOPMetricAnalyzer.class.getDeclaredMethod("analyzeMetricExceptAbstractness", List.class, String.class, double.class);
                 method.setAccessible(true);

+ 29 - 7
src/test/java/com/moekr/moocoder/logic/service/impl/ProjectServiceImplTest.java

@@ -1,29 +1,51 @@
 package com.moekr.moocoder.logic.service.impl;
 
+import com.alibaba.fastjson.JSON;
 import com.moekr.moocoder.logic.service.ProjectService;
-import org.apache.tomcat.util.http.fileupload.FileItem;
-import org.apache.tomcat.util.http.fileupload.disk.DiskFileItem;
+import com.moekr.moocoder.util.FileUtil;
+import com.moekr.moocoder.web.dto.CodeDTO;
 import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.mock.web.MockMultipartFile;
 import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.multipart.commons.CommonsMultipartFile;
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * @author miaomuzhi
  * @since 2020/5/8
  */
+@SpringBootTest
 class ProjectServiceImplTest {
+    @Autowired
+    ProjectService projectService;
+
+    @Test
+    void uploadProject() {
+        String jsonPath = getClass().getResource("/mock/a.json").getPath();
+        String jsonContent = FileUtil.readFile(jsonPath);
+        CodeDTO codeDTO = JSON.parseObject(jsonContent, CodeDTO.class);
+        String uuid = projectService.addProject(codeDTO);
+        System.out.println(uuid);
+        assertTrue(uuid != null);
+
+        try {
+            Thread.sleep(30000); // wait for the async function call
+        } catch (InterruptedException e){
+            Thread.currentThread().interrupt();
+        }
+    }
+
 
     @Test
     void addProject() throws IOException {
-        ProjectService projectService = new ProjectServiceImpl();
+        ProjectService projectService = new ProjectServiceImpl(null, null, null);
         File f = new File(getClass().getResource("/wala/jar/walawork.zip").getFile());
         try (FileInputStream inputStream = new FileInputStream(f)) {
             MultipartFile zipFile = new MockMultipartFile("walawork", "walawork.zip", "binary", inputStream);
@@ -39,7 +61,7 @@ class ProjectServiceImplTest {
 
     @Test
     void containsProject() {
-        ProjectService projectService = new ProjectServiceImpl();
+        ProjectService projectService = new ProjectServiceImpl(null, null, null);
         assertFalse(projectService.containsProject("The watcher"));
         assertTrue(projectService.containsProject(".gitkeep"));
     }

+ 36 - 0
src/test/java/com/moekr/moocoder/util/ToolKitTest.java

@@ -0,0 +1,36 @@
+package com.moekr.moocoder.util;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Base64;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author miaomuzhi
+ * @since 2020/5/31
+ */
+class ToolKitTest {
+
+    @Test
+    void randomUUID() {
+        for (int i = 0; i < 10000; i++) {
+            String randomID = ToolKit.randomUUID();
+            String randomID2 = ToolKit.randomUUID();
+            assertTrue(randomID.length() == randomID2.length());
+            assertFalse(randomID.equals(randomID2));
+        }
+    }
+
+    @Test
+    void encodingTest() {
+        String originalInput = "test input";
+        String encodedString = Base64.getEncoder().encodeToString(originalInput.getBytes());
+        assertEquals("dGVzdCBpbnB1dA==", encodedString);
+        assertNotEquals("test input", encodedString);
+
+        byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
+        String decodedString = new String(decodedBytes);
+        assertEquals("test input", decodedString);
+    }
+}

+ 9 - 0
src/test/resources/mock/a.json

@@ -0,0 +1,9 @@
+{
+  "problemType":"JAVA",
+  "fileList":{
+    "/src/main/java/edu/nju/HelloWorld.java":"cGFja2FnZSBlZHUubmp1OwoKLyoqCiAqIENyZWF0ZWQgYnkgTW9la3Igb24gMjAxNy8xMC8zMC4KICovCnB1YmxpYyBjbGFzcyBIZWxsb1dvcmxkIHsKICAgIHB1YmxpYyBTdHJpbmcgaGVsbG9Xb3JsZCgpewogICAgICAgIHJldHVybiAiSGVsbG8gV29ybGQhIjsKICAgIH0KfQo=",
+    "/src/test/java/edu/nju/HelloWorldTest.java":"cGFja2FnZSBlZHUubmp1OwoKaW1wb3J0IG9yZy5qdW5pdC5Bc3NlcnQ7CmltcG9ydCBvcmcuanVuaXQuVGVzdDsKCi8qKgogKiBDcmVhdGVkIGJ5IE1vZWtyIG9uIDIwMTcvMTAvMzAuCiAqLwpwdWJsaWMgY2xhc3MgSGVsbG9Xb3JsZFRlc3QgewogICAgQFRlc3QKICAgIHB1YmxpYyB2b2lkIHRlc3RIZWxsb1dvcmxkKCl7CiAgICAgICAgQXNzZXJ0LmFzc2VydEVxdWFscygiSGVsbG8gV29ybGQhIiwgbmV3IEhlbGxvV29ybGQoKS5oZWxsb1dvcmxkKCkpOwogICAgfQp9Cg==",
+    "/pom.xml":"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHByb2plY3QgeG1sbnM9Imh0dHA6Ly9tYXZlbi5hcGFjaGUub3JnL1BPTS80LjAuMCIKICAgICAgICAgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIKICAgICAgICAgeHNpOnNjaGVtYUxvY2F0aW9uPSJodHRwOi8vbWF2ZW4uYXBhY2hlLm9yZy9QT00vNC4wLjAgaHR0cDovL21hdmVuLmFwYWNoZS5vcmcveHNkL21hdmVuLTQuMC4wLnhzZCI+CiAgICA8bW9kZWxWZXJzaW9uPjQuMC4wPC9tb2RlbFZlcnNpb24+CgogICAgPGdyb3VwSWQ+ZWR1Lm5qdTwvZ3JvdXBJZD4KICAgIDxhcnRpZmFjdElkPmhlbGxvLXdvcmxkPC9hcnRpZmFjdElkPgogICAgPHZlcnNpb24+MS4wPC92ZXJzaW9uPgoKICAgIDxkZXBlbmRlbmNpZXM+CiAgICAgICAgPGRlcGVuZGVuY3k+CiAgICAgICAgICAgIDxncm91cElkPmp1bml0PC9ncm91cElkPgogICAgICAgICAgICA8YXJ0aWZhY3RJZD5qdW5pdDwvYXJ0aWZhY3RJZD4KICAgICAgICAgICAgPHZlcnNpb24+NC4xMjwvdmVyc2lvbj4KICAgICAgICA8L2RlcGVuZGVuY3k+CiAgICA8L2RlcGVuZGVuY2llcz4KPC9wcm9qZWN0Pg==",
+    "/README.md":"SmF2YQpIZWxsbyBXb3JsZAoK6KaB5rGCSGVsbG9Xb3JsZOexu+eahGhlbGxvV29ybGTmlrnms5Xov5Tlm54iSGVsbG8gV29ybGQhIuWtl+espuS4suOAggo="
+  }
+}

+ 0 - 0
src/main/resources/mock/jackson-datatype-guava.txt → src/test/resources/mock/jackson-datatype-guava.txt