fix to support subtasks in java
[idea/community.git] / python / educational-core / student / src / com / jetbrains / edu / learning / courseFormat / Task.java
1 package com.jetbrains.edu.learning.courseFormat;
2
3 import com.google.gson.annotations.Expose;
4 import com.google.gson.annotations.SerializedName;
5 import com.intellij.openapi.editor.Document;
6 import com.intellij.openapi.fileEditor.FileDocumentManager;
7 import com.intellij.openapi.project.Project;
8 import com.intellij.openapi.util.text.StringUtil;
9 import com.intellij.openapi.vfs.VirtualFile;
10 import com.intellij.util.xmlb.XmlSerializer;
11 import com.intellij.util.xmlb.annotations.Transient;
12 import com.jetbrains.edu.learning.StudyUtils;
13 import com.jetbrains.edu.learning.core.EduNames;
14 import com.jetbrains.edu.learning.stepic.EduStepicConnector;
15 import org.jdom.Element;
16 import org.jetbrains.annotations.NotNull;
17 import org.jetbrains.annotations.Nullable;
18
19 import java.util.Date;
20 import java.util.HashMap;
21 import java.util.Map;
22
23 /**
24  * Implementation of task which contains task files, tests, input file for tests
25  */
26 public class Task implements StudyItem {
27   @Expose private String name;
28
29   // index is visible to user number of task from 1 to task number
30   private int myIndex;
31   private StudyStatus myStatus = StudyStatus.Unchecked;
32
33   @SerializedName("stepic_id")
34   @Expose private int myStepId;
35   
36   @SerializedName("task_files")
37   @Expose public Map<String, TaskFile> taskFiles = new HashMap<>();
38
39   private String text;
40   private Map<String, String> testsText = new HashMap<>();
41
42   @Transient private Lesson myLesson;
43   @Expose @SerializedName("update_date") private Date myUpdateDate;
44
45   private int myActiveSubtaskIndex = 0;
46   @Expose private int mySubtaskNum = 1;
47
48   public Task() {}
49
50   public Task(@NotNull final String name) {
51     this.name = name;
52   }
53
54   /**
55    * Initializes state of task file
56    *
57    * @param lesson lesson which task belongs to
58    */
59   public void initTask(final Lesson lesson, boolean isRestarted) {
60     setLesson(lesson);
61     if (!isRestarted) myStatus = StudyStatus.Unchecked;
62     for (TaskFile taskFile : getTaskFiles().values()) {
63       taskFile.initTaskFile(this, isRestarted);
64     }
65   }
66
67   public String getName() {
68     return name;
69   }
70
71   public void setName(String name) {
72     this.name = name;
73   }
74
75   public String getText() {
76     return text;
77   }
78
79   public void setText(final String text) {
80     this.text = text;
81   }
82
83   public int getIndex() {
84     return myIndex;
85   }
86
87   public void setIndex(int index) {
88     myIndex = index;
89   }
90
91   public Map<String, String> getTestsText() {
92     return testsText;
93   }
94
95   public void addTestsTexts(String name, String text) {
96     testsText.put(name, text);
97   }
98
99   public Map<String, TaskFile> getTaskFiles() {
100     return taskFiles;
101   }
102
103   @Nullable
104   public TaskFile getTaskFile(final String name) {
105     return name != null ? taskFiles.get(name) : null;
106   }
107
108   public boolean isTaskFile(@NotNull final String fileName) {
109     return taskFiles.get(fileName) != null;
110   }
111
112   public void addTaskFile(@NotNull final String name, int index) {
113     TaskFile taskFile = new TaskFile();
114     taskFile.setIndex(index);
115     taskFile.setTask(this);
116     taskFile.name = name;
117     taskFiles.put(name, taskFile);
118   }
119
120   public void addTaskFile(@NotNull final TaskFile taskFile) {
121     taskFiles.put(taskFile.name, taskFile);
122   }
123
124   @Nullable
125   public TaskFile getFile(@NotNull final String fileName) {
126     return taskFiles.get(fileName);
127   }
128
129   @Transient
130   public Lesson getLesson() {
131     return myLesson;
132   }
133
134   @Transient
135   public void setLesson(Lesson lesson) {
136     myLesson = lesson;
137   }
138
139   @Nullable
140   public VirtualFile getTaskDir(@NotNull final Project project) {
141     String lessonDirName = EduNames.LESSON + String.valueOf(myLesson.getIndex());
142     String taskDirName = EduNames.TASK + String.valueOf(myIndex);
143     VirtualFile courseDir = project.getBaseDir();
144     if (courseDir != null) {
145       VirtualFile lessonDir = courseDir.findChild(lessonDirName);
146       if (lessonDir != null) {
147         VirtualFile taskDir = lessonDir.findChild(taskDirName);
148         if (taskDir == null) {
149           return null;
150         }
151         VirtualFile srcDir = taskDir.findChild(EduNames.SRC);
152         return srcDir != null ? srcDir : taskDir;
153       }
154     }
155     return null;
156   }
157
158   @NotNull
159   public String getTaskText(@NotNull final Project project) {
160     if (!StringUtil.isEmptyOrSpaces(text)) return text;
161     final VirtualFile taskDir = getTaskDir(project);
162     if (taskDir != null) {
163       final VirtualFile file = StudyUtils.findTaskDescriptionVirtualFile(project, taskDir);
164       if (file == null) return "";
165       final Document document = FileDocumentManager.getInstance().getDocument(file);
166       if (document != null) {
167         return document.getImmutableCharSequence().toString();
168       }
169     }
170
171     return "";
172   }
173
174   @NotNull
175   public String getTestsText(@NotNull final Project project) {
176     final VirtualFile taskDir = getTaskDir(project);
177     if (taskDir != null) {
178       final VirtualFile file = taskDir.findChild(EduNames.TESTS_FILE);
179       if (file == null) return "";
180       final Document document = FileDocumentManager.getInstance().getDocument(file);
181       if (document != null) {
182         return document.getImmutableCharSequence().toString();
183       }
184     }
185
186     return "";
187   }
188
189   @Override
190   public boolean equals(Object o) {
191     if (this == o) return true;
192     if (o == null || getClass() != o.getClass()) return false;
193
194     Task task = (Task)o;
195
196     if (myIndex != task.myIndex) return false;
197     if (name != null ? !name.equals(task.name) : task.name != null) return false;
198     if (taskFiles != null ? !taskFiles.equals(task.taskFiles) : task.taskFiles != null) return false;
199     if (text != null ? !text.equals(task.text) : task.text != null) return false;
200     if (testsText != null ? !testsText.equals(task.testsText) : task.testsText != null) return false;
201
202     return true;
203   }
204
205   @Override
206   public int hashCode() {
207     int result = name != null ? name.hashCode() : 0;
208     result = 31 * result + myIndex;
209     result = 31 * result + (taskFiles != null ? taskFiles.hashCode() : 0);
210     result = 31 * result + (text != null ? text.hashCode() : 0);
211     result = 31 * result + (testsText != null ? testsText.hashCode() : 0);
212     return result;
213   }
214
215   public void setStepId(int stepId) {
216     myStepId = stepId;
217   }
218
219   public int getStepId() {
220     return myStepId;
221   }
222
223   public StudyStatus getStatus() {
224     return myStatus;
225   }
226   
227   public void setStatus(StudyStatus status) {
228     for (TaskFile taskFile : taskFiles.values()) {
229       for (AnswerPlaceholder placeholder : taskFile.getActivePlaceholders()) {
230         placeholder.setStatus(status);
231       }
232     }
233     if (status == StudyStatus.Solved && hasSubtasks() && getActiveSubtaskIndex() != getSubtaskNum() - 1) {
234       return;
235     }
236     myStatus = status;
237   }
238
239   public Task copy() {
240     Element element = XmlSerializer.serialize(this);
241     Task copy = XmlSerializer.deserialize(element, Task.class);
242     if (copy == null) {
243       return null;
244     }
245     copy.initTask(null, true);
246     return copy;
247   }
248
249   public void setUpdateDate(Date date) {
250     myUpdateDate = date;
251   }
252
253   public Date getUpdateDate() {
254     return myUpdateDate;
255   }
256
257   public boolean isUpToDate() {
258     if (getStepId() == 0) return true;
259     final Date date = EduStepicConnector.getTaskUpdateDate(getStepId());
260     if (date == null) return true;
261     if (myUpdateDate == null) return false;
262     return !date.after(myUpdateDate);
263   }
264
265   public int getActiveSubtaskIndex() {
266     return myActiveSubtaskIndex;
267   }
268
269   public void setActiveSubtaskIndex(int activeSubtaskIndex) {
270     myActiveSubtaskIndex = activeSubtaskIndex;
271   }
272
273   public int getSubtaskNum() {
274     return mySubtaskNum;
275   }
276
277   public void setSubtaskNum(int subtaskNum) {
278     mySubtaskNum = subtaskNum;
279   }
280
281   public boolean hasSubtasks() {
282     return mySubtaskNum > 1;
283   }
284 }