aaa72fe823bee8954693dc59ff3b0cffbd37213e
[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         return lessonDir.findChild(taskDirName);
148       }
149     }
150     return null;
151   }
152
153   @NotNull
154   public String getTaskText(@NotNull final Project project) {
155     if (!StringUtil.isEmptyOrSpaces(text)) return text;
156     final VirtualFile taskDir = getTaskDir(project);
157     if (taskDir != null) {
158       final VirtualFile file = StudyUtils.findTaskDescriptionVirtualFile(project, taskDir);
159       if (file == null) return "";
160       final Document document = FileDocumentManager.getInstance().getDocument(file);
161       if (document != null) {
162         return document.getImmutableCharSequence().toString();
163       }
164     }
165
166     return "";
167   }
168
169   @NotNull
170   public String getTestsText(@NotNull final Project project) {
171     final VirtualFile taskDir = getTaskDir(project);
172     if (taskDir != null) {
173       final VirtualFile file = taskDir.findChild(EduNames.TESTS_FILE);
174       if (file == null) return "";
175       final Document document = FileDocumentManager.getInstance().getDocument(file);
176       if (document != null) {
177         return document.getImmutableCharSequence().toString();
178       }
179     }
180
181     return "";
182   }
183
184   @Override
185   public boolean equals(Object o) {
186     if (this == o) return true;
187     if (o == null || getClass() != o.getClass()) return false;
188
189     Task task = (Task)o;
190
191     if (myIndex != task.myIndex) return false;
192     if (name != null ? !name.equals(task.name) : task.name != null) return false;
193     if (taskFiles != null ? !taskFiles.equals(task.taskFiles) : task.taskFiles != null) return false;
194     if (text != null ? !text.equals(task.text) : task.text != null) return false;
195     if (testsText != null ? !testsText.equals(task.testsText) : task.testsText != null) return false;
196
197     return true;
198   }
199
200   @Override
201   public int hashCode() {
202     int result = name != null ? name.hashCode() : 0;
203     result = 31 * result + myIndex;
204     result = 31 * result + (taskFiles != null ? taskFiles.hashCode() : 0);
205     result = 31 * result + (text != null ? text.hashCode() : 0);
206     result = 31 * result + (testsText != null ? testsText.hashCode() : 0);
207     return result;
208   }
209
210   public void setStepId(int stepId) {
211     myStepId = stepId;
212   }
213
214   public int getStepId() {
215     return myStepId;
216   }
217
218   public StudyStatus getStatus() {
219     return myStatus;
220   }
221   
222   public void setStatus(StudyStatus status) {
223     for (TaskFile taskFile : taskFiles.values()) {
224       for (AnswerPlaceholder placeholder : taskFile.getActivePlaceholders()) {
225         placeholder.setStatus(status);
226       }
227     }
228     if (status == StudyStatus.Solved && hasSubtasks() && getActiveSubtaskIndex() != getSubtaskNum() - 1) {
229       return;
230     }
231     myStatus = status;
232   }
233
234   public Task copy() {
235     Element element = XmlSerializer.serialize(this);
236     Task copy = XmlSerializer.deserialize(element, Task.class);
237     if (copy == null) {
238       return null;
239     }
240     copy.initTask(null, true);
241     return copy;
242   }
243
244   public void setUpdateDate(Date date) {
245     myUpdateDate = date;
246   }
247
248   public Date getUpdateDate() {
249     return myUpdateDate;
250   }
251
252   public boolean isUpToDate() {
253     if (getStepId() == 0) return true;
254     final Date date = EduStepicConnector.getTaskUpdateDate(getStepId());
255     if (date == null) return true;
256     if (myUpdateDate == null) return false;
257     return !date.after(myUpdateDate);
258   }
259
260   public int getActiveSubtaskIndex() {
261     return myActiveSubtaskIndex;
262   }
263
264   public void setActiveSubtaskIndex(int activeSubtaskIndex) {
265     myActiveSubtaskIndex = activeSubtaskIndex;
266   }
267
268   public int getSubtaskNum() {
269     return mySubtaskNum;
270   }
271
272   public void setSubtaskNum(int subtaskNum) {
273     mySubtaskNum = subtaskNum;
274   }
275
276   public boolean hasSubtasks() {
277     return mySubtaskNum > 1;
278   }
279 }