removed addLocalCourse action, simplified TaskManager, store courses as list not...
authorEkaterina Tuzova <Ekaterina.Tuzova@jetbrains.com>
Wed, 21 Jan 2015 16:33:29 +0000 (19:33 +0300)
committerEkaterina Tuzova <Ekaterina.Tuzova@jetbrains.com>
Wed, 21 Jan 2015 16:33:29 +0000 (19:33 +0300)
incrementally load courses

14 files changed:
python/edu/interactive-learning-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java
python/edu/interactive-learning-python/src/com/jetbrains/edu/learning/PyStudyTestRunner.java
python/edu/interactive-learning-python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyProjectGenerator.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyTaskManager.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyUtils.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/Course.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/Lesson.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/course/TaskFile.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/stepic/StudyStepicConnector.java
python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.form
python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java

index 293282be29e382ccd89bd88beb29a5105e556654..a1252c5aef71411387b3a9ed0274877401c0193f 100644 (file)
@@ -17,8 +17,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
-import java.io.File;
-import java.util.Map;
+import java.util.List;
 
 
 public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator implements DirectoryProjectGenerator {
@@ -85,7 +84,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator imp
     return settingsPanel.getContentPanel();
   }
 
-  public Map<CourseInfo, File> getCourses() {
+  public List<CourseInfo> getCourses() {
     return myGenerator.getCourses();
   }
 
index bc0e563d80c969cea42759e88c5e0d6b64f909ad..22aee16417d56696ae404b6ae86727a7a225c2a4 100644 (file)
@@ -39,12 +39,8 @@ public class PyStudyTestRunner extends StudyTestRunner {
         commandLine.addParameter(testRunner.getPath());
         final Course course = StudyTaskManager.getInstance(project).getCourse();
         assert course != null;
-        File resourceFile = new File(course.getResourcePath());
-        String resourceFolder = resourceFile.getParent();
-        if (resourceFolder == null) {
-          return null;
-        }
-        commandLine.addParameter(resourceFolder);
+        File resourceFile = new File(course.getCourseDirectory());
+        commandLine.addParameter(resourceFile.getPath());
         commandLine.addParameter(FileUtil.toSystemDependentName(executablePath));
         return commandLine.createProcess();
       }
index a7785e16838cdba6d63b28bb9a79ddb882893f14..4a65e37219b548e9b41189dfeb117389718e69a0 100644 (file)
@@ -32,7 +32,6 @@ import org.jetbrains.annotations.NotNull;
 
 import java.io.File;
 import java.util.List;
-import java.util.Map;
 
 public class PyStudyIntroductionCourseAction extends AnAction {
 
@@ -49,15 +48,15 @@ public class PyStudyIntroductionCourseAction extends AnAction {
     else {
       final GenerateProjectCallback callback = new GenerateProjectCallback();
       final PyStudyDirectoryProjectGenerator generator = new PyStudyDirectoryProjectGenerator();
-      final Map<CourseInfo, File> courses = generator.getCourses();
+      final List<CourseInfo> courses = generator.getCourses();
       CourseInfo introCourse = null;
-      for (CourseInfo info : courses.keySet()) {
+      for (CourseInfo info : courses) {
         if ("Introduction to Python".equals(info.getName())) {
           introCourse = info;
         }
       }
       if (introCourse == null) {
-        introCourse = StudyUtils.getFirst(courses.keySet());
+        introCourse = StudyUtils.getFirst(courses);
       }
       generator.setSelectedCourse(introCourse);
       final ProjectSpecificSettingsStep step = new ProjectSpecificSettingsStep(generator, callback);
index 000f8f24d83fb9b55ddd50854747136d0fb9ee2f..c9d5a3bae749a24e3c3285cd372b2ae99f2cd6fb 100644 (file)
@@ -1,18 +1,19 @@
 package com.jetbrains.edu.learning;
 
-import com.google.gson.*;
-import com.google.gson.stream.JsonReader;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
 import com.intellij.facet.ui.ValidationResult;
 import com.intellij.ide.projectView.ProjectView;
 import com.intellij.ide.projectView.impl.AbstractProjectViewPane;
-import com.intellij.lang.javascript.boilerplate.GithubDownloadUtil;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.application.PathManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.fileEditor.FileEditorManager;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.VirtualFileManager;
@@ -20,292 +21,180 @@ import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
 import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl;
 import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter;
 import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
-import com.intellij.platform.templates.github.GeneratorException;
-import com.intellij.platform.templates.github.ZipUtil;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.PsiManager;
 import com.intellij.util.containers.ContainerUtil;
 import com.jetbrains.edu.learning.course.*;
 import com.jetbrains.edu.learning.stepic.StudyStepicConnector;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import java.io.*;
-import java.util.Collections;
-import java.util.HashMap;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 public class StudyProjectGenerator {
   private static final Logger LOG = Logger.getInstance(StudyProjectGenerator.class.getName());
   private final List<SettingsListener> myListeners = ContainerUtil.newArrayList();
-  private static final String REPO_URL = "https://github.com/JetBrains/pycharm-courses/archive/master.zip";
-  private static final String USER_NAME = "PyCharm";
-  private static final String COURSE_META_FILE = "course.json";
-  private static final String COURSE_NAME_ATTRIBUTE = "name";
-  private static final Pattern CACHE_PATTERN = Pattern.compile("(name=(.*)) (path=(.*course.json)) (author=(.*)) (description=(.*))");
-  private static final String REPOSITORY_NAME = "pycharm-courses";
-  public static final String AUTHOR_ATTRIBUTE = "author";
   private final File myCoursesDir = new File(PathManager.getConfigPath(), "courses");
   private static final String CACHE_NAME = "courseNames.txt";
-  private Map<CourseInfo, File> myCourses = new HashMap<CourseInfo, File>();   // TODO: replace with list
-  private File mySelectedCourseFile;
-  private Project myProject;
+  private List<CourseInfo> myCourses = new ArrayList<CourseInfo>();   // TODO: replace with list
+  private CourseInfo mySelectedCourseInfo;
 
-  public void setCourses(Map<CourseInfo, File> courses) {
+  public void setCourses(List<CourseInfo> courses) {
     myCourses = courses;
   }
 
-  /**
-   * Finds selected course in courses by name.
-   *
-   * @param courseName name of selected course
-   */
   public void setSelectedCourse(@NotNull CourseInfo courseName) {
-    File courseFile = myCourses.get(courseName);
-    if (courseFile == null) {
-      LOG.error("invalid course in list");
-    }
-    mySelectedCourseFile = courseFile;
-  }
-
-  /**
-   * Adds course to courses specified in params
-   *
-   * @param courseDir must be directory containing course file
-   * @return added course name or null if course is invalid
-   */
-  @Nullable
-  private static CourseInfo addCourse(Map<CourseInfo, File> courses, File courseDir) {
-    if (courseDir.isDirectory()) {
-      File[] courseFiles = courseDir.listFiles(new FilenameFilter() {
-        @Override
-        public boolean accept(File dir, String name) {
-          return name.equals(COURSE_META_FILE);
-        }
-      });
-      if (courseFiles.length != 1) {
-        LOG.info("User tried to add course with more than one or without course files");
-        return null;
-      }
-      File courseFile = courseFiles[0];
-      CourseInfo courseInfo = getCourseInfo(courseFile);
-      if (courseInfo != null) {
-        courses.put(courseInfo, courseFile);
-      }
-      return courseInfo;
-    }
-    return null;
-  }
-
-
-  /**
-   * Adds course from zip archive to courses
-   *
-   * @return added course name or null if course is invalid
-   */
-  @Nullable
-  public CourseInfo addLocalCourse(String zipFilePath) {
-    File file = new File(zipFilePath);
-    try {
-      String fileName = file.getName();
-      String unzippedName = fileName.substring(0, fileName.indexOf("."));
-      File courseDir = new File(myCoursesDir, unzippedName);
-      ZipUtil.unzip(null, courseDir, file, null, null, true);
-      CourseInfo courseName = addCourse(myCourses, courseDir);
-      flushCache();
-      return courseName;
-    }
-    catch (IOException e) {
-      LOG.error("Failed to unzip course archive");
-      LOG.error(e);
-    }
-    return null;
+    mySelectedCourseInfo = courseName;
   }
 
   public void generateProject(@NotNull final Project project, @NotNull final VirtualFile baseDir) {
+    final Course course = StudyStepicConnector.getCourse(mySelectedCourseInfo);
+    if (course == null) return;
+    flushCourse(course);
+    course.init(false);
+    final File courseDirectory = new File(myCoursesDir, course.getName());
+    course.create(baseDir, courseDirectory, project);
+    course.setCourseDirectory(new File(myCoursesDir, mySelectedCourseInfo.getName()).getAbsolutePath());
+    VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
+    StudyTaskManager.getInstance(project).setCourse(course);
+    final boolean[] initialized = {false};
+    ToolWindowManagerEx.getInstanceEx(project).addToolWindowManagerListener(new ToolWindowManagerAdapter() {
+      @Override
+      public void stateChanged() {
+        final AbstractProjectViewPane projectViewPane = ProjectView.getInstance(project).getCurrentProjectViewPane();
+        if (projectViewPane == null || initialized[0]) return;
+        JTree tree = projectViewPane.getTree();
+        if (tree == null) {
+          return;
+        }
+        tree.updateUI();
 
-    myProject = project;
-    Reader reader = null;
-    try {
-      reader = new InputStreamReader(new FileInputStream(mySelectedCourseFile), "UTF-8");
-      Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
-      final Course course = gson.fromJson(reader, Course.class);
-      course.init(false);
-      course.create(baseDir, new File(mySelectedCourseFile.getParent()), project);
-      course.setResourcePath(mySelectedCourseFile.getAbsolutePath());
-      VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
-      StudyTaskManager.getInstance(project).setCourse(course);
-      final boolean[] initialized = {false};
-      ToolWindowManagerEx.getInstanceEx(myProject).addToolWindowManagerListener(new ToolWindowManagerAdapter() {
-        @Override
-        public void stateChanged() {
-          final AbstractProjectViewPane projectViewPane = ProjectView.getInstance(myProject).getCurrentProjectViewPane();
-          if (projectViewPane == null || initialized[0]) return;
-          JTree tree = projectViewPane.getTree();
-          if (tree == null) {
-            return;
-          }
-          tree.updateUI();
-
-          ApplicationManager.getApplication().invokeLater(new Runnable() {
-            @Override
-            public void run() {
-              LocalFileSystem.getInstance().refresh(false);
-              final Lesson firstLesson = StudyUtils.getFirst(course.getLessons());
-              final Task firstTask = StudyUtils.getFirst(firstLesson.getTaskList());
-              final VirtualFile taskDir = firstTask.getTaskDir(myProject);
-              if (taskDir == null) return;
-              final Map<String, TaskFile> taskFiles = firstTask.getTaskFiles();
-              VirtualFile activeVirtualFile = null;
-              for (Map.Entry<String, TaskFile> entry : taskFiles.entrySet()) {
-                final String name = entry.getKey();
-                final TaskFile taskFile = entry.getValue();
-                final VirtualFile virtualFile = ((VirtualDirectoryImpl)taskDir).refreshAndFindChild(name);
-                if (virtualFile != null) {
-                  FileEditorManager.getInstance(project).openFile(virtualFile, true);
-                  if (!taskFile.getTaskWindows().isEmpty()) {
-                    activeVirtualFile = virtualFile;
-                  }
+        ApplicationManager.getApplication().invokeLater(new Runnable() {
+          @Override
+          public void run() {
+            LocalFileSystem.getInstance().refresh(false);
+            final Lesson firstLesson = StudyUtils.getFirst(course.getLessons());
+            final Task firstTask = StudyUtils.getFirst(firstLesson.getTaskList());
+            final VirtualFile taskDir = firstTask.getTaskDir(project);
+            if (taskDir == null) return;
+            final Map<String, TaskFile> taskFiles = firstTask.getTaskFiles();
+            VirtualFile activeVirtualFile = null;
+            for (Map.Entry<String, TaskFile> entry : taskFiles.entrySet()) {
+              final String name = entry.getKey();
+              final TaskFile taskFile = entry.getValue();
+              final VirtualFile virtualFile = ((VirtualDirectoryImpl)taskDir).refreshAndFindChild(name);
+              if (virtualFile != null) {
+                FileEditorManager.getInstance(project).openFile(virtualFile, true);
+                if (!taskFile.getTaskWindows().isEmpty()) {
+                  activeVirtualFile = virtualFile;
                 }
               }
-              if (activeVirtualFile != null) {
-                final PsiFile file = PsiManager.getInstance(myProject).findFile(activeVirtualFile);
-                ProjectView.getInstance(project).select(file, activeVirtualFile, true);
-              } else {
-                String first = StudyUtils.getFirst(taskFiles.keySet());
-                if (first != null) {
-                  NewVirtualFile firstFile = ((VirtualDirectoryImpl)taskDir).refreshAndFindChild(first);
-                  if (firstFile != null) {
-                    FileEditorManager.getInstance(project).openFile(firstFile, true);
-                  }
+            }
+            if (activeVirtualFile != null) {
+              final PsiFile file = PsiManager.getInstance(project).findFile(activeVirtualFile);
+              ProjectView.getInstance(project).select(file, activeVirtualFile, true);
+            } else {
+              String first = StudyUtils.getFirst(taskFiles.keySet());
+              if (first != null) {
+                NewVirtualFile firstFile = ((VirtualDirectoryImpl)taskDir).refreshAndFindChild(first);
+                if (firstFile != null) {
+                  FileEditorManager.getInstance(project).openFile(firstFile, true);
                 }
               }
-              initialized[0] = true;
             }
-          }, ModalityState.current(), new Condition() {
-            @Override
-            public boolean value(Object o) {
-              return project.isDisposed();
-            }
-          });
-        }
-      });
-    }
-    catch (FileNotFoundException e) {
-      LOG.error(e);
-    }
-    catch (UnsupportedEncodingException e) {
-      LOG.error(e);
-    }
-    finally {
-      StudyUtils.closeSilently(reader);
-    }
+            initialized[0] = true;
+          }
+        }, ModalityState.current(), new Condition() {
+          @Override
+          public boolean value(Object o) {
+            return project.isDisposed();
+          }
+        });
+      }
+    });
   }
 
-  /**
-   * Downloads courses from {@link com.jetbrains.python.edu.StudyDirectoryProjectGenerator#REPO_URL}
-   * and unzips them into {@link com.jetbrains.python.edu.StudyDirectoryProjectGenerator#myCoursesDir}
-   */
+  private void flushCourse(@NotNull final Course course) {
+    final File courseDirectory = new File(myCoursesDir, course.getName());
+    FileUtil.createDirectory(courseDirectory);
+    flushCourseJson(course, courseDirectory);
 
-  public void downloadAndUnzip(boolean needProgressBar) {
-    File outputFile = new File(PathManager.getConfigPath(), "courses.zip");
-    try {
-      if (!needProgressBar) {
-        GithubDownloadUtil.downloadAtomically(null, REPO_URL,
-                                              outputFile, USER_NAME, REPOSITORY_NAME);
-      }
-      else {
-        GithubDownloadUtil.downloadContentToFileWithProgressSynchronously(myProject, REPO_URL, "downloading courses", outputFile, USER_NAME,
-                                                                          REPOSITORY_NAME, false);
-      }
-      if (outputFile.exists()) {
-        ZipUtil.unzip(null, myCoursesDir, outputFile, null, null, true);
-        if (!outputFile.delete()) {
-          LOG.error("Failed to delete", outputFile.getName());
-        }
-        File[] files = myCoursesDir.listFiles();
-        if (files != null) {
-          for (File file : files) {
-            String fileName = file.getName();
-            if (StudyUtils.isZip(fileName)) {
-              ZipUtil.unzip(null, new File(myCoursesDir, fileName.substring(0, fileName.indexOf("."))), file, null, null, true);
-              if (!file.delete()) {
-                LOG.error("Failed to delete", fileName);
-              }
-            }
+    int lessonIndex = 1;
+    for (Lesson lesson : course.lessons) {
+      final File lessonDirectory = new File(courseDirectory, "lesson"+String.valueOf(lessonIndex));
+      FileUtil.createDirectory(lessonDirectory);
+      int taskIndex = 1;
+      for (Task task : lesson.taskList) {
+        final File taskDirectory = new File(lessonDirectory, "task" + String.valueOf(taskIndex));
+        FileUtil.createDirectory(taskDirectory);
+        for (Map.Entry<String, TaskFile> taskFileEntry : task.taskFiles.entrySet()) {
+          final String name = taskFileEntry.getKey();
+          final TaskFile taskFile = taskFileEntry.getValue();
+          final File file = new File(taskDirectory, name);
+          FileUtil.createIfDoesntExist(file);
+
+          try {
+            FileUtil.writeToFile(file, taskFile.text);
+          }
+          catch (IOException e) {
+            LOG.error("ERROR copying file " + name);
           }
         }
-      } else {
-        LOG.debug("failed to download course");
+        final File testsFile = new File(taskDirectory, "tests.py");
+        FileUtil.createIfDoesntExist(testsFile);
+        try {
+          FileUtil.writeToFile(testsFile, task.testsText);
+        }
+        catch (IOException e) {
+          LOG.error("ERROR copying tests file");
+        }
+        final File taskText = new File(taskDirectory, "task.html");
+        FileUtil.createIfDoesntExist(taskText);
+        try {
+          FileUtil.writeToFile(taskText, task.text);
+        }
+        catch (IOException e) {
+          LOG.error("ERROR copying tests file");
+        }
+        taskIndex += 1;
       }
-    }
-    catch (IOException e) {
-      LOG.error(e);
-    }
-    catch (GeneratorException e) {
-      LOG.error(e);
+      lessonIndex += 1;
     }
   }
 
-  public Map<CourseInfo, File> getLoadedCourses() {
-    return myCourses;
-  }
-
-  /**
-   * Parses courses located in {@link com.jetbrains.python.edu.StudyDirectoryProjectGenerator#myCoursesDir}
-   * to {@link com.jetbrains.python.edu.StudyDirectoryProjectGenerator#myCourses}
-   *
-   * @return map with course names and course files location
-   */
-  public Map<CourseInfo, File> loadCourses() {
-    final List<CourseInfo> courses = StudyStepicConnector.getCourses();
-    //Map<CourseInfo, File> courses = new HashMap<CourseInfo, File>();
-    //if (myCoursesDir.exists()) {
-    //  File[] courseDirs = myCoursesDir.listFiles(new FileFilter() {
-    //    @Override
-    //    public boolean accept(File pathname) {
-    //      return pathname.isDirectory();
-    //    }
-    //  });
-    //  for (File courseDir : courseDirs) {
-    //    addCourse(courses, courseDir);
-    //  }
-    //}
-    return Collections.singletonMap(courses.get(0), null);
-  }
-
-  /**
-   * Parses course json meta file and finds course name
-   *
-   * @return information about course or null if course file is invalid
-   */
-  @Nullable
-  private static CourseInfo getCourseInfo(File courseFile) {
-    CourseInfo courseInfo = null;
-    BufferedReader reader = null;
+  private static void flushCourseJson(@NotNull final Course course, @NotNull final File courseDirectory) {
+    final Gson gson = new GsonBuilder().setPrettyPrinting().create();
+    final String json = gson.toJson(course);
+    final File courseJson = new File(courseDirectory, "course.json");
+    final FileOutputStream fileOutputStream;
     try {
-      if (courseFile.getName().equals(COURSE_META_FILE)) {
-        reader = new BufferedReader(new InputStreamReader(new FileInputStream(courseFile), "UTF-8"));
-        JsonReader r = new JsonReader(reader);
-        JsonParser parser = new JsonParser();
-        JsonElement el = parser.parse(r);
-        String courseName = el.getAsJsonObject().get(COURSE_NAME_ATTRIBUTE).getAsString();
-        String courseAuthor = el.getAsJsonObject().get(AUTHOR_ATTRIBUTE).getAsString();
-        String courseDescription = el.getAsJsonObject().get("description").getAsString();
-        courseInfo = new CourseInfo(courseName, courseAuthor, courseDescription);
+      fileOutputStream = new FileOutputStream(courseJson);
+      OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");
+      try {
+        outputStreamWriter.write(json);
+      }
+      catch (IOException e) {
+        Messages.showErrorDialog(e.getMessage(), "Failed to Generate Json");
+        LOG.info(e);
+      }
+      finally {
+        try {
+          outputStreamWriter.close();
+        }
+        catch (IOException e) {
+          LOG.info(e);
+        }
       }
     }
-    catch (Exception e) {
-      //error will be shown in UI
+    catch (FileNotFoundException e) {
+      LOG.info(e);
     }
-    finally {
-      StudyUtils.closeSilently(reader);
+    catch (UnsupportedEncodingException e) {
+      LOG.info(e);
     }
-    return courseInfo;
   }
 
   /**
@@ -324,10 +213,9 @@ public class StudyProjectGenerator {
         }
       }
       writer = new PrintWriter(cashFile);
-      for (Map.Entry<CourseInfo, File> course : myCourses.entrySet()) {
-        CourseInfo courseInfo = course.getKey();
+      for (CourseInfo courseInfo : myCourses) {
         String line = String
-          .format("name=%s path=%s author=%s description=%s", courseInfo.getName(), course.getValue(), courseInfo.getAuthor(),
+          .format("name=%s author=%s description=%s", courseInfo.getName(), courseInfo.getAuthor(),
                   courseInfo.getDescription());
         writer.println(line);
       }
@@ -343,69 +231,17 @@ public class StudyProjectGenerator {
     }
   }
 
-  /**
-   * Loads courses from {@link com.jetbrains.python.edu.StudyDirectoryProjectGenerator#CACHE_NAME} file
-   *
-   * @return map of course names and course files
-   */
-  @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
-  private static Map<CourseInfo, File> getCoursesFromCache(File cashFile) {
-    Map<CourseInfo, File> coursesFromCash = new HashMap<CourseInfo, File>();
-    BufferedReader reader =  null;
-    try {
-      reader = new BufferedReader(new InputStreamReader(new FileInputStream(cashFile)));
-      String line;
-
-      while ((line = reader.readLine()) != null) {
-        Matcher matcher = CACHE_PATTERN.matcher(line);
-        if (matcher.matches()) {
-          String courseName = matcher.group(2);
-          File file = new File(matcher.group(4));
-          String author = matcher.group(6);
-          String description = matcher.group(8);
-          CourseInfo courseInfo = new CourseInfo(courseName, author, description);
-          if (file.exists()) {
-            coursesFromCash.put(courseInfo, file);
-          }
-        }
-      }
-    }
-    catch (FileNotFoundException e) {
-      LOG.error(e);
-    }
-    catch (IOException e) {
-      LOG.error(e);
-    }
-    finally {
-      StudyUtils.closeSilently(reader);
-    }
-    return coursesFromCash;
-  }
-
   /**
    * @return courses from memory or from cash file or parses course directory
    */
-  public Map<CourseInfo, File> getCourses() {
+  public List<CourseInfo> getCourses() {
     if (!myCourses.isEmpty()) {
       return myCourses;
     }
-    //if (myCoursesDir.exists()) {
-    //  File cacheFile = new File(myCoursesDir, CACHE_NAME);
-    //  if (cacheFile.exists()) {
-    //    myCourses = getCoursesFromCache(cacheFile);
-    //    if (!myCourses.isEmpty()) {
-    //      return myCourses;
-    //    }
-    //  }
-    //  myCourses = loadCourses();
-    //  if (!myCourses.isEmpty()) {
-    //    return myCourses;
-    //  }
-    //}
-    //downloadAndUnzip(false);
-    myCourses = loadCourses();
-    flushCache();
-    return myCourses;
+    else {
+      myCourses = StudyStepicConnector.getCourses();
+      return myCourses;
+    }
   }
 
   public void addSettingsStateListener(@NotNull SettingsListener listener) {
index 16996c0fb3315a04f7e857a2d2005e09261f2b94..f1b9144c79cae24c4f533dd2a6cfead0dba201ed 100644 (file)
@@ -17,6 +17,9 @@ import com.intellij.openapi.fileEditor.FileEditor;
 import com.intellij.openapi.fileEditor.FileEditorManager;
 import com.intellij.openapi.keymap.Keymap;
 import com.intellij.openapi.keymap.KeymapManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.module.ModuleServiceManager;
 import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.project.DumbAwareRunnable;
 import com.intellij.openapi.project.Project;
@@ -70,10 +73,9 @@ import java.util.Map;
 public class StudyTaskManager implements ProjectComponent, PersistentStateComponent<Element>, DumbAware {
   private static final Logger LOG = Logger.getInstance(StudyTaskManager.class.getName());
   public static final String COURSE_ELEMENT = "courseElement";
-  private static Map<String, StudyTaskManager> myTaskManagers = new HashMap<String, StudyTaskManager>();
   private static Map<String, String> myDeletedShortcuts = new HashMap<String, String>();
   private final Project myProject;
-  private Course myCourse;
+  public Course myCourse;
   private FileCreatedListener myListener;
 
 
@@ -82,11 +84,9 @@ public class StudyTaskManager implements ProjectComponent, PersistentStateCompon
   }
 
   private StudyTaskManager(@NotNull final Project project) {
-    myTaskManagers.put(project.getBasePath(), this);
     myProject = project;
   }
 
-
   @Nullable
   public Course getCourse() {
     return myCourse;
@@ -243,15 +243,11 @@ public class StudyTaskManager implements ProjectComponent, PersistentStateCompon
     if (myCourse == null) {
       return;
     }
-    File resourceFile = new File(myCourse.getResourcePath());
-    if (!resourceFile.exists()) {
-      return;
-    }
-    final File courseDir = resourceFile.getParentFile();
-    if (!courseDir.exists()) {
+    File resourceDirectory = new File(myCourse.getCourseDirectory());
+    if (!resourceDirectory.exists()) {
       return;
     }
-    final File[] files = courseDir.listFiles();
+    final File[] files = resourceDirectory.listFiles();
     if (files == null) return;
     for (File file : files) {
       if (file.getName().equals(StudyNames.TEST_HELPER)) {
@@ -360,11 +356,10 @@ public class StudyTaskManager implements ProjectComponent, PersistentStateCompon
   }
 
   public static StudyTaskManager getInstance(@NotNull final Project project) {
-    StudyTaskManager item = myTaskManagers.get(project.getBasePath());
-    return item != null ? item : new StudyTaskManager(project);
+    final Module module = ModuleManager.getInstance(project).getModules()[0];
+    return ModuleServiceManager.getService(module, StudyTaskManager.class);
   }
 
-
   @Nullable
   public TaskFile getTaskFile(@NotNull final VirtualFile file) {
     if (myCourse == null) {
index 6f5f211f5ae1ada543c44fee0b303c97c4e0d073..784d27ac99e5eca8123d46b279f555316127b450 100644 (file)
@@ -196,7 +196,7 @@ public class StudyUtils {
     int lessonNum = task.getLesson().getIndex() + 1;
     assert course != null;
     String pathToResource =
-      FileUtil.join(new File(course.getResourcePath()).getParent(), Lesson.LESSON_DIR + lessonNum, Task.TASK_DIR + taskNum);
+      FileUtil.join(course.getCourseDirectory(), Lesson.LESSON_DIR + lessonNum, Task.TASK_DIR + taskNum);
     File resourceFile = new File(pathToResource, copyName);
     FileUtil.copy(new File(pathToResource, sourceName), resourceFile);
     return resourceFile;
index c2ef5ab211a557a0db32993d1c4e5a17042781f5..7d2fb81d924cba70b60fbf0c1988091f17259961 100644 (file)
@@ -119,13 +119,12 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction {
     String lessonDir = Lesson.LESSON_DIR + String.valueOf(task.getLesson().getIndex() + 1);
     String taskDir = Task.TASK_DIR + String.valueOf(task.getIndex() + 1);
     Course course = task.getLesson().getCourse();
-    File resourceFile = new File(course.getResourcePath());
-    File resourceRoot = resourceFile.getParentFile();
-    if (!resourceFile.exists() || resourceRoot == null) {
+    File resourceFile = new File(course.getCourseDirectory());
+    if (!resourceFile.exists()) {
       showBalloon(project, "Course was deleted", MessageType.ERROR);
       return false;
     }
-    String patternPath = FileUtil.join(resourceRoot.getPath(), lessonDir, taskDir, fileName);
+    String patternPath = FileUtil.join(resourceFile.getPath(), lessonDir, taskDir, fileName);
     VirtualFile patternFile = VfsUtil.findFileByIoFile(new File(patternPath), true);
     if (patternFile == null) {
       return false;
index 94e3cc4bd5efaf0c716b4d53dfce3069aa208314..cf2f960ac95ddf60ed23742e7e2cf2f004923d63 100644 (file)
@@ -21,9 +21,6 @@ import com.jetbrains.edu.learning.course.TaskWindow;
 import com.jetbrains.edu.learning.editor.StudyEditor;
 import icons.InteractiveLearningIcons;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.io.File;
 
 public class StudyShowHintAction extends DumbAwareAction {
   public static final String ACTION_ID = "ShowHintAction";
@@ -61,7 +58,7 @@ public class StudyShowHintAction extends DumbAwareAction {
     }
     String hintText = OUTSIDE_TASK_WINDOW_MESSAGE;
     if (taskWindow != null) {
-      hintText = getHintText(taskWindow, course);
+      hintText = taskWindow.getHint();
     }
     int offset = editor.getDocument().getLineStartOffset(pos.line) + pos.column;
     PsiElement element = file.findElementAt(offset);
@@ -71,23 +68,6 @@ public class StudyShowHintAction extends DumbAwareAction {
     showHintPopUp(project, editor, component);
   }
 
-  @Nullable
-  private static String getHintText(@NotNull final TaskWindow taskWindow, @NotNull final Course course) {
-    String hintFileName = taskWindow.getHint();
-    String hintText = HINT_NOT_AVAILABLE;
-    if (hintFileName != null && !hintFileName.isEmpty()) {
-      File resourceFile = new File(course.getResourcePath());
-      File resourceRoot = resourceFile.getParentFile();
-      if (resourceRoot != null && resourceRoot.exists()) {
-        File hintsDir = new File(resourceRoot, Course.HINTS_DIR);
-        if (hintsDir.exists()) {
-          hintText = StudyUtils.getFileText(hintsDir.getAbsolutePath(), hintFileName, true, "UTF-8");
-        }
-      }
-    }
-    return  hintText != null ? hintText : OUTSIDE_TASK_WINDOW_MESSAGE;
-  }
-
   private static void showHintPopUp(Project project, Editor editor, DocumentationComponent component) {
     final JBPopup popup =
       JBPopupFactory.getInstance().createComponentPopupBuilder(component, component)
index 94196dd45e26ddcb86eba2ec9540cb2434dc5627..2d42bd699e6fd182843536e20859c6bfd549833d 100644 (file)
@@ -14,16 +14,13 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class Course {
-
   private static final Logger LOG = Logger.getInstance(Course.class.getName());
   public static final String SANDBOX_DIR = "Sandbox";
   public List<Lesson> lessons = new ArrayList<Lesson>();
   public String description;
   public String name;
-  public String myResourcePath = "";
-  public String author;
-  public static final String COURSE_DIR = "course";
-  public static final String HINTS_DIR = "hints";
+  public String myCourseDirectory = "";
+  public String author="";
   public boolean myUpToDate = false;
 
 
@@ -93,12 +90,12 @@ public class Course {
     return name;
   }
 
-  public void setResourcePath(@NotNull final String resourcePath) {
-    myResourcePath = resourcePath;
+  public void setCourseDirectory(@NotNull final String courseDirectory) {
+    myCourseDirectory = courseDirectory;
   }
 
-  public String getResourcePath() {
-    return myResourcePath;
+  public String getCourseDirectory() {
+    return myCourseDirectory;
   }
 
   public String getDescription() {
index b8ce1b2ea08ccf972c496a76fb0dd5be375e8e38..6b421d3cd50f9f22c728e1f7a23ca4afd96928d0 100644 (file)
@@ -12,9 +12,13 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class Lesson implements Stateful {
+  @Transient
   String id;
+  @Transient
   public List<Integer> steps;
+  @Transient
   public List<String> tags;
+  @Transient
   Boolean is_public;
   @SerializedName("title")
   public String name;
index bd38695799464e962136d5229849434d90a86ade..51804f1cf0247d5726876d6d93422f01528cd02a 100644 (file)
@@ -32,7 +32,7 @@ import java.util.List;
 
 public class TaskFile implements Stateful {
   public String name;
-  String text;
+  public String text;
   @SerializedName("placeholders")
   public List<TaskWindow> taskWindows = new ArrayList<TaskWindow>();
 
index 0f5df9a8fced4849c0c95bfd3f5eca6d73b4681d..90e15071800c17eb5c7a4999d776e939ac85111f 100644 (file)
@@ -56,6 +56,7 @@ public class StudyStepicConnector {
       catch (IOException e) {
         LOG.error("IOException " + e.getMessage());
       }
+      return course;
     }
 
     try {
@@ -107,9 +108,12 @@ public class StudyStepicConnector {
         task.testsText = step.options.test;
 
         task.taskFiles = new HashMap<String, TaskFile>();      // TODO: it looks like we don't need taskFiles as map anymore
-        for (TaskFile taskFile : step.options.files) {
-          task.taskFiles.put(taskFile.name, taskFile);
+        if (step.options.files != null) {
+          for (TaskFile taskFile : step.options.files) {
+            task.taskFiles.put(taskFile.name, taskFile);
+          }
         }
+        lesson.taskList.add(task);
       }
     }
     return lessons;
@@ -137,7 +141,6 @@ public class StudyStepicConnector {
   }
 
   private static class Step {
-
     StepOptions options;
     String text;
     String name;
@@ -146,14 +149,12 @@ public class StudyStepicConnector {
   private static class StepOptions {
     String test;
     List<TaskFile> files;
-
   }
 
   private static class StepRaw {
     Step block;
   }
 
-
   private static class CoursesContainer {
     public List<CourseInfo> courses;
   }
index e6d745da6a7b239177991d725d50ec0f8b7f20cb..d2a504027ab355594c94d2271965f238c1110d4f 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.edu.learning.ui.StudyNewProjectPanel">
-  <grid id="27dc6" binding="myContentPanel" layout-manager="GridLayoutManager" row-count="2" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+  <grid id="27dc6" binding="myContentPanel" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
     <margin top="0" left="0" bottom="0" right="0"/>
     <constraints>
       <xy x="20" y="20" width="500" height="400"/>
@@ -11,7 +11,7 @@
       <grid id="54488" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
         <margin top="0" left="0" bottom="0" right="0"/>
         <constraints>
-          <grid row="1" column="1" row-span="1" col-span="3" vsize-policy="2" hsize-policy="4" anchor="0" fill="3" indent="0" use-parent-layout="false">
+          <grid row="1" column="1" row-span="1" col-span="2" vsize-policy="2" hsize-policy="4" anchor="0" fill="3" indent="0" use-parent-layout="false">
             <minimum-size width="-1" height="60"/>
             <preferred-size width="-1" height="60"/>
           </grid>
         </constraints>
         <properties/>
       </component>
-      <component id="5c614" class="com.intellij.openapi.ui.FixedSizeButton" binding="myBrowseButton">
-        <constraints>
-          <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
-            <minimum-size width="30" height="25"/>
-          </grid>
-        </constraints>
-        <properties/>
-      </component>
       <component id="f1e10" class="javax.swing.JButton" binding="myRefreshButton">
         <constraints>
-          <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
+          <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
         </constraints>
         <properties>
           <hideActionText value="false"/>
index b7064784e0bf837d81b9337fee3892ef5c9cd90e..7fd2990d46aed6582231c335eaf541b05ffe287b 100644 (file)
@@ -3,10 +3,6 @@ package com.jetbrains.edu.learning.ui;
 import com.intellij.facet.ui.FacetValidatorsManager;
 import com.intellij.facet.ui.ValidationResult;
 import com.intellij.icons.AllIcons;
-import com.intellij.openapi.fileChooser.FileChooser;
-import com.intellij.openapi.fileChooser.FileChooserDescriptor;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.util.Consumer;
 import com.jetbrains.edu.learning.StudyProjectGenerator;
 import com.jetbrains.edu.learning.StudyUtils;
 import com.jetbrains.edu.learning.course.CourseInfo;
@@ -14,20 +10,16 @@ import com.jetbrains.edu.learning.course.CourseInfo;
 import javax.swing.*;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.io.File;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * author: liana
  * data: 7/31/14.
  */
 public class StudyNewProjectPanel{
-  private Set<CourseInfo> myAvailableCourses = new HashSet<CourseInfo>();
+  private List<CourseInfo> myAvailableCourses = new ArrayList<CourseInfo>();
   private JComboBox myCoursesComboBox;
-  private JButton myBrowseButton;
   private JButton myRefreshButton;
   private JPanel myContentPanel;
   private JLabel myAuthorLabel;
@@ -40,12 +32,11 @@ public class StudyNewProjectPanel{
 
   public StudyNewProjectPanel(StudyProjectGenerator generator) {
     myGenerator = generator;
-    Map<CourseInfo, File> courses = myGenerator.getCourses();
-    if (courses.isEmpty()) {
+    myAvailableCourses = myGenerator.getCourses();
+    if (myAvailableCourses.isEmpty()) {
       setError(CONNECTION_ERROR);
     }
     else {
-      myAvailableCourses = courses.keySet();
       for (CourseInfo courseInfo : myAvailableCourses) {
         myCoursesComboBox.addItem(courseInfo);
       }
@@ -64,47 +55,6 @@ public class StudyNewProjectPanel{
 
   private void initListeners() {
 
-    final FileChooserDescriptor fileChooser = new FileChooserDescriptor(true, false, false, true, false, false) {
-      @Override
-      public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
-        return file.isDirectory() || StudyUtils.isZip(file.getName());
-      }
-
-      @Override
-      public boolean isFileSelectable(VirtualFile file) {
-        return StudyUtils.isZip(file.getName());
-      }
-    };
-    myBrowseButton.addActionListener(new ActionListener() {
-      @Override
-      public void actionPerformed(ActionEvent e) {
-        FileChooser.chooseFile(fileChooser, null, null,
-                               new Consumer<VirtualFile>() {
-                                 @Override
-                                 public void consume(VirtualFile file) {
-                                   String fileName = file.getPath();
-                                   int oldSize = myAvailableCourses.size();
-                                   CourseInfo courseInfo = myGenerator.addLocalCourse(fileName);
-                                   if (courseInfo != null)  {
-                                     if (oldSize != myAvailableCourses.size()) {
-                                       myCoursesComboBox.addItem(courseInfo);
-                                     }
-                                     myCoursesComboBox.setSelectedItem(courseInfo);
-                                     setOK();
-                                   }
-                                   else {
-                                     setError(INVALID_COURSE);
-                                     myCoursesComboBox.removeAllItems();
-                                     myCoursesComboBox.addItem(CourseInfo.INVALID_COURSE);
-                                     for (CourseInfo course : myAvailableCourses) {
-                                       myCoursesComboBox.addItem(course);
-                                     }
-                                     myCoursesComboBox.setSelectedItem(CourseInfo.INVALID_COURSE);
-                                   }
-                                 }
-                               });
-      }
-    });
     myRefreshButton.addActionListener(new RefreshActionListener());
     myCoursesComboBox.addActionListener(new CourseSelectedListener());
   }
@@ -140,35 +90,20 @@ public class StudyNewProjectPanel{
   private class RefreshActionListener implements ActionListener {
     @Override
     public void actionPerformed(ActionEvent e) {
-      myGenerator.downloadAndUnzip(true);
-      Map<CourseInfo, File> downloadedCourses = myGenerator.loadCourses();
-      if (downloadedCourses.isEmpty()) {
+      final List<CourseInfo> courses = myGenerator.getCourses();
+      if (courses.isEmpty()) {
         setError(CONNECTION_ERROR);
         return;
       }
-      Map<CourseInfo, File> oldCourses = myGenerator.getLoadedCourses();
-      Map<CourseInfo, File> newCourses = new HashMap<CourseInfo, File>();
-      for (Map.Entry<CourseInfo, File> course : oldCourses.entrySet()) {
-        File courseFile = course.getValue();
-        if (courseFile.exists()) {
-          newCourses.put(course.getKey(), courseFile);
-        }
-      }
-      for (Map.Entry<CourseInfo, File> course : downloadedCourses.entrySet()) {
-        CourseInfo courseName = course.getKey();
-        if (newCourses.get(courseName) == null) {
-          newCourses.put(courseName, course.getValue());
-        }
-      }
       myCoursesComboBox.removeAllItems();
 
-      for (CourseInfo courseInfo : newCourses.keySet()) {
+      for (CourseInfo courseInfo : courses) {
         myCoursesComboBox.addItem(courseInfo);
       }
-      myGenerator.setSelectedCourse(StudyUtils.getFirst(newCourses.keySet()));
+      myGenerator.setSelectedCourse(StudyUtils.getFirst(courses));
 
-      myGenerator.setCourses(newCourses);
-      myAvailableCourses = newCourses.keySet();
+      myGenerator.setCourses(courses);
+      myAvailableCourses = courses;
       myGenerator.flushCache();
     }
   }