Skip to content

Commit

Permalink
#537 Course news
Browse files Browse the repository at this point in the history
  • Loading branch information
jaakkonakaza committed Oct 28, 2021
1 parent 6752c35 commit 6589f8e
Show file tree
Hide file tree
Showing 31 changed files with 916 additions and 161 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ repositories {
}

dependencies {
implementation 'org.jsoup:jsoup:1.14.3'
compile group: 'commons-io', name: 'commons-io', version: '2.8.0'
compile group: 'net.lingala.zip4j', name: 'zip4j', version: '2.7.0'
compileOnly group: 'org.scala-lang', name: 'scala-library', version: '2.13.4'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import fi.aalto.cs.apluscourses.model.ExerciseGroup;
import fi.aalto.cs.apluscourses.model.Group;
import fi.aalto.cs.apluscourses.model.InvalidAuthenticationException;
import fi.aalto.cs.apluscourses.model.News;
import fi.aalto.cs.apluscourses.model.Points;
import fi.aalto.cs.apluscourses.model.Student;
import fi.aalto.cs.apluscourses.model.Submission;
Expand Down Expand Up @@ -51,6 +52,7 @@ public class APlusExerciseDataSource implements ExerciseDataSource {
private static final String STUDENTS = "students";
public static final String RESULTS = "results";
public static final String ME = "me";
private static final String NEWS = "news";

@NotNull
private final Client client;
Expand Down Expand Up @@ -218,6 +220,15 @@ public ZonedDateTime getEndingTime(@NotNull Course course,
return parser.parseEndingTime(response);
}

@Override
@NotNull
public List<News> getNews(@NotNull Course course,
@NotNull Authentication authentication) throws IOException {
String url = apiUrl + COURSES + "/" + course.getId() + "/" + NEWS + "/";
return getPaginatedResults(url, authentication, CachePreferences.GET_NEW_AND_FORGET,
jsonObject -> News.fromJsonObject(jsonObject, course));
}

/**
* Sends the submission to the server.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

public class ActionGroups {

public static final String NEWS_ACTIONS =
"fi.aalto.cs.apluscourses.intellij.actions.ActionGroups.NEWS_ACTIONS";
public static final String MODULE_ACTIONS =
"fi.aalto.cs.apluscourses.intellij.actions.ActionGroups.MODULE_ACTIONS";
public static final String EXERCISE_ACTIONS =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package fi.aalto.cs.apluscourses.intellij.actions;

import com.intellij.openapi.project.Project;
import fi.aalto.cs.apluscourses.intellij.notifications.Notifier;
import fi.aalto.cs.apluscourses.intellij.services.MainViewModelProvider;
import fi.aalto.cs.apluscourses.model.ExercisesTree;
import fi.aalto.cs.apluscourses.model.UrlRenderer;
import fi.aalto.cs.apluscourses.presentation.base.BaseTreeViewModel;
import org.jetbrains.annotations.NotNull;

public class OpenExerciseItemAction extends OpenItemAction<ExercisesTree> {

public static final String ACTION_ID = OpenExerciseItemAction.class.getCanonicalName();

/**
* Construct an {@link OpenExerciseItemAction} instance with the given parameters. This constructor
* is mainly useful for testing purposes.
*/
public OpenExerciseItemAction(@NotNull MainViewModelProvider mainViewModelProvider,
@NotNull UrlRenderer urlRenderer,
@NotNull Notifier notifier) {
super(mainViewModelProvider, urlRenderer, notifier);
}

/**
* Construct an {@link OpenExerciseItemAction} instance with reasonable defaults.
*/
public OpenExerciseItemAction() {
super();
}

@Override
BaseTreeViewModel<ExercisesTree> getTreeViewModel(@NotNull Project project) {
return mainViewModelProvider.getMainViewModel(project).exercisesViewModel.get();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,25 @@

import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import fi.aalto.cs.apluscourses.intellij.notifications.DefaultNotifier;
import fi.aalto.cs.apluscourses.intellij.notifications.Notifier;
import fi.aalto.cs.apluscourses.intellij.notifications.UrlRenderingErrorNotification;
import fi.aalto.cs.apluscourses.intellij.services.MainViewModelProvider;
import fi.aalto.cs.apluscourses.intellij.services.PluginSettings;
import fi.aalto.cs.apluscourses.model.Browsable;
import fi.aalto.cs.apluscourses.model.UrlRenderer;
import fi.aalto.cs.apluscourses.presentation.base.BaseTreeViewModel;
import fi.aalto.cs.apluscourses.presentation.base.SelectableNodeViewModel;
import fi.aalto.cs.apluscourses.presentation.exercise.ExercisesTreeViewModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class OpenItemAction extends DumbAwareAction {
public class OpenItemAction<T> extends DumbAwareAction {

public static final String ACTION_ID = OpenItemAction.class.getCanonicalName();

@NotNull
private final MainViewModelProvider mainViewModelProvider;
protected final MainViewModelProvider mainViewModelProvider;

@NotNull
private final UrlRenderer urlRenderer;
Expand All @@ -30,9 +32,9 @@ public class OpenItemAction extends DumbAwareAction {
* Construct an {@link OpenItemAction} instance with the given parameters. This constructor
* is mainly useful for testing purposes.
*/
public OpenItemAction(@NotNull MainViewModelProvider mainViewModelProvider,
@NotNull UrlRenderer urlRenderer,
@NotNull Notifier notifier) {
protected OpenItemAction(@NotNull MainViewModelProvider mainViewModelProvider,
@NotNull UrlRenderer urlRenderer,
@NotNull Notifier notifier) {
this.mainViewModelProvider = mainViewModelProvider;
this.urlRenderer = urlRenderer;
this.notifier = notifier;
Expand All @@ -41,7 +43,7 @@ public OpenItemAction(@NotNull MainViewModelProvider mainViewModelProvider,
/**
* Construct an {@link OpenItemAction} instance with reasonable defaults.
*/
public OpenItemAction() {
protected OpenItemAction() {
this(
PluginSettings.getInstance(),
new UrlRenderer(),
Expand All @@ -51,15 +53,15 @@ public OpenItemAction() {

@Override
public void actionPerformed(@NotNull AnActionEvent e) {
ExercisesTreeViewModel exercisesTree = mainViewModelProvider
.getMainViewModel(e.getProject())
.exercisesViewModel
.get();
if (exercisesTree == null) {
if (e.getProject() == null) {
return;
}
BaseTreeViewModel<T> treeViewModel = getTreeViewModel(e.getProject());
if (treeViewModel == null) {
return;
}

SelectableNodeViewModel<?> nodeViewModel = exercisesTree.getSelectedItem();
SelectableNodeViewModel<?> nodeViewModel = treeViewModel.getSelectedItem();
if (nodeViewModel == null) {
return;
}
Expand All @@ -74,9 +76,11 @@ public void actionPerformed(@NotNull AnActionEvent e) {
@Override
public void update(@NotNull AnActionEvent e) {
var project = e.getProject();
var exercisesTreeViewModel =
mainViewModelProvider.getMainViewModel(project).exercisesViewModel.get();
e.getPresentation().setEnabled(exercisesTreeViewModel != null
&& exercisesTreeViewModel.isAuthenticated());
e.getPresentation().setEnabled(project != null && getTreeViewModel(project) != null);
}

@Nullable
BaseTreeViewModel<T> getTreeViewModel(@NotNull Project project) {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package fi.aalto.cs.apluscourses.intellij.actions;

import com.intellij.openapi.project.Project;
import fi.aalto.cs.apluscourses.intellij.notifications.Notifier;
import fi.aalto.cs.apluscourses.intellij.services.MainViewModelProvider;
import fi.aalto.cs.apluscourses.model.NewsTree;
import fi.aalto.cs.apluscourses.model.UrlRenderer;
import fi.aalto.cs.apluscourses.presentation.base.BaseTreeViewModel;
import org.jetbrains.annotations.NotNull;

public class OpenNewsItemAction extends OpenItemAction<NewsTree> {

public static final String ACTION_ID = OpenNewsItemAction.class.getCanonicalName();

/**
* Construct an {@link OpenNewsItemAction} instance with the given parameters. This constructor
* is mainly useful for testing purposes.
*/
public OpenNewsItemAction(@NotNull MainViewModelProvider mainViewModelProvider,
@NotNull UrlRenderer urlRenderer,
@NotNull Notifier notifier) {
super(mainViewModelProvider, urlRenderer, notifier);
}

/**
* Construct an {@link OpenNewsItemAction} instance with reasonable defaults.
*/
public OpenNewsItemAction() {
super();
}

@Override
BaseTreeViewModel<NewsTree> getTreeViewModel(@NotNull Project project) {
return mainViewModelProvider.getMainViewModel(project).newsTreeViewModel.get();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package fi.aalto.cs.apluscourses.intellij.actions;

import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.DumbAwareAction;
import fi.aalto.cs.apluscourses.intellij.services.MainViewModelProvider;
import fi.aalto.cs.apluscourses.intellij.services.PluginSettings;
import fi.aalto.cs.apluscourses.model.News;
import org.jetbrains.annotations.NotNull;

public class ReadAllNewsAction extends DumbAwareAction {
@NotNull
private final MainViewModelProvider mainViewModelProvider;

protected ReadAllNewsAction(@NotNull MainViewModelProvider mainViewModelProvider) {

this.mainViewModelProvider = mainViewModelProvider;
}

/**
* Construct an {@link OpenItemAction} instance with reasonable defaults.
*/
protected ReadAllNewsAction() {
this(
PluginSettings.getInstance()
);
}

@Override
public void actionPerformed(@NotNull AnActionEvent e) {
var project = e.getProject();
var mainViewModel = mainViewModelProvider.getMainViewModel(project);
var newsViewModel = mainViewModel.newsTreeViewModel.get();
if (newsViewModel == null) {
return;
}
newsViewModel.getModel().getNews().forEach(News::setRead);
mainViewModel.newsTreeViewModel.valueChanged();
}

@Override
public void update(@NotNull AnActionEvent e) {
var project = e.getProject();
var mainViewModel = mainViewModelProvider.getMainViewModel(project);
e.getPresentation().setEnabled(mainViewModel.newsTreeViewModel.get() != null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import fi.aalto.cs.apluscourses.model.Course;
import fi.aalto.cs.apluscourses.model.ExercisesLazyLoader;
import fi.aalto.cs.apluscourses.model.ExercisesTree;
import fi.aalto.cs.apluscourses.model.NewsTree;
import fi.aalto.cs.apluscourses.model.Student;
import fi.aalto.cs.apluscourses.model.User;
import fi.aalto.cs.apluscourses.utils.Event;
Expand Down Expand Up @@ -41,6 +42,8 @@ public class CourseProject implements ExercisesLazyLoader {

private volatile ExercisesTree exercisesTree;

private volatile NewsTree newsTree;

private final AtomicBoolean hasTriedToReadAuthenticationFromStorage = new AtomicBoolean(false);

@NotNull
Expand Down Expand Up @@ -78,7 +81,7 @@ public CourseProject(@NotNull Course course,
this.notifier = notifier;
this.course = course;
this.project = project;
this.courseUpdater = new CourseUpdater(course, project, courseUrl, courseUpdated);
this.courseUpdater = new CourseUpdater(this, course, project, courseUrl, courseUpdated);
this.exercisesUpdater = new ExercisesUpdater(this, exercisesUpdated);
}

Expand Down Expand Up @@ -161,6 +164,15 @@ public void setExerciseTree(@NotNull ExercisesTree exercisesTree) {
this.exercisesTree = exercisesTree;
}

@Nullable
public NewsTree getNewsTree() {
return newsTree;
}

public void setNewsTree(@NotNull NewsTree newsTree) {
this.newsTree = newsTree;
}

@Nullable
public Authentication getAuthentication() {
return user.get() == null ? null : Objects.requireNonNull(user.get()).getAuthentication();
Expand All @@ -172,7 +184,7 @@ public Authentication getAuthentication() {
public void setAuthentication(Authentication authentication) {
try {
var newUser = authentication == null
? null : course.getExerciseDataSource().getUser(authentication);
? null : course.getExerciseDataSource().getUser(authentication);
var oldUser = this.user.getAndSet(newUser);
if (oldUser != null) {
oldUser.getAuthentication().clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import fi.aalto.cs.apluscourses.intellij.notifications.Notifier;
import fi.aalto.cs.apluscourses.intellij.services.PluginSettings;
import fi.aalto.cs.apluscourses.model.Course;
import fi.aalto.cs.apluscourses.model.NewsTree;
import fi.aalto.cs.apluscourses.utils.CoursesClient;
import fi.aalto.cs.apluscourses.utils.Event;
import fi.aalto.cs.apluscourses.utils.Version;
Expand Down Expand Up @@ -36,6 +37,7 @@ public interface CourseConfigurationFetcher {
InputStream fetch(URL configurationUrl) throws IOException;
}

private final CourseProject courseProject;
@NotNull
private final Course course;

Expand All @@ -61,14 +63,16 @@ public interface CourseConfigurationFetcher {
/**
* Construct a course updater with the given parameters.
*/
public CourseUpdater(@NotNull Course course,
public CourseUpdater(@NotNull CourseProject courseProject,
@NotNull Course course,
@NotNull Project project,
@NotNull URL courseUrl,
@NotNull CourseConfigurationFetcher configurationFetcher,
@NotNull Event eventToTrigger,
@NotNull Notifier notifier,
long updateInterval) {
super(updateInterval);
this.courseProject = courseProject;
this.course = course;
this.project = project;
this.courseUrl = courseUrl;
Expand All @@ -80,11 +84,12 @@ public CourseUpdater(@NotNull Course course,
/**
* Construct a course updater with reasonable defaults.
*/
public CourseUpdater(@NotNull Course course,
public CourseUpdater(@NotNull CourseProject courseProject,
@NotNull Course course,
@NotNull Project project,
@NotNull URL courseUrl,
@NotNull Event eventToTrigger) {
this(course, project, courseUrl, CoursesClient::fetch, eventToTrigger, new DefaultNotifier(),
this(courseProject, course, project, courseUrl, CoursesClient::fetch, eventToTrigger, new DefaultNotifier(),
PluginSettings.UPDATE_INTERVAL);
}

Expand All @@ -93,7 +98,22 @@ protected void doTask() {
var progressViewModel =
PluginSettings.getInstance().getMainViewModel(project).progressViewModel;
var progress =
progressViewModel.start(1, getText("ui.ProgressBarView.refreshingModules"), false);
progressViewModel.start(2, getText("ui.ProgressBarView.refreshingCourse"), false);

var auth = courseProject.getAuthentication();
try {
if (auth != null) {
var news = course.getExerciseDataSource().getNews(course, auth);
var newsTree = new NewsTree(news);
courseProject.setNewsTree(newsTree);
eventToTrigger.trigger();
}
} catch (IOException e) {
progress.finish();
return;
}
progress.increment();

updateModules(fetchModulesInfo());
if (Thread.interrupted()) {
progress.finish();
Expand Down
Loading

0 comments on commit 6589f8e

Please sign in to comment.