mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -60,14 +60,6 @@
|
||||
<text resource-bundle="messages/CompilerBundle" key="label.option.clear.output.directory.on.rebuild"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="366e" class="javax.swing.JCheckBox" binding="myCbAssertNotNull">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/CompilerBundle" key="add.notnull.assertions"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="348ee" class="javax.swing.JCheckBox" binding="myCbAutoShowFirstError">
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
@@ -172,6 +164,37 @@
|
||||
<text value="Shared build process VM options:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<grid id="6ee49" layout-manager="GridLayoutManager" row-count="1" 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>
|
||||
<grid row="2" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="366e" class="javax.swing.JCheckBox" binding="myCbAssertNotNull">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/CompilerBundle" key="add.notnull.assertions"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="73479" class="javax.swing.JButton" binding="myConfigureAnnotations">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="&Configure annotations..."/>
|
||||
</properties>
|
||||
</component>
|
||||
<hspacer id="4b9fc">
|
||||
<constraints>
|
||||
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
|
||||
@@ -15,20 +15,23 @@
|
||||
*/
|
||||
package com.intellij.compiler.options;
|
||||
|
||||
import com.intellij.codeInsight.NullableNotNullDialog;
|
||||
import com.intellij.compiler.CompilerConfiguration;
|
||||
import com.intellij.compiler.CompilerConfigurationImpl;
|
||||
import com.intellij.compiler.CompilerWorkspaceConfiguration;
|
||||
import com.intellij.compiler.MalformedPatternException;
|
||||
import com.intellij.compiler.impl.javaCompiler.javac.JavacConfiguration;
|
||||
import com.intellij.compiler.server.BuildManager;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.ide.PowerSaveMode;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.compiler.CompilerBundle;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.options.SearchableConfigurable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.DocumentAdapter;
|
||||
import com.intellij.ui.Gray;
|
||||
@@ -44,6 +47,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
@@ -80,6 +85,7 @@ public class CompilerUIConfigurable implements SearchableConfigurable, Configura
|
||||
private JLabel myResourcePatternsLabel;
|
||||
private JLabel myEnableAutomakeLegendLabel;
|
||||
private JLabel myParallelCompilationLegendLabel;
|
||||
private JButton myConfigureAnnotations;
|
||||
|
||||
public CompilerUIConfigurable(@NotNull final Project project) {
|
||||
myProject = project;
|
||||
@@ -99,6 +105,14 @@ public class CompilerUIConfigurable implements SearchableConfigurable, Configura
|
||||
s -> StringUtil.startsWithIgnoreCase(s, "-Xmx")) == null);
|
||||
}
|
||||
});
|
||||
myConfigureAnnotations.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(myPanel));
|
||||
if (project == null) project = ProjectManager.getInstance().getDefaultProject();
|
||||
new NullableNotNullDialog(project).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void tweakControls(@NotNull Project project) {
|
||||
|
||||
@@ -65,7 +65,7 @@ import java.util.zip.ZipFile;
|
||||
public class PluginManagerCore {
|
||||
private static final Logger LOG = Logger.getInstance(PluginManagerCore.class);
|
||||
|
||||
private static final String DISABLED_PLUGINS_FILENAME = "disabled_plugins.txt";
|
||||
public static final String DISABLED_PLUGINS_FILENAME = "disabled_plugins.txt";
|
||||
public static final String CORE_PLUGIN_ID = "com.intellij";
|
||||
private static final String META_INF = "META-INF";
|
||||
public static final String PLUGIN_XML = "plugin.xml";
|
||||
@@ -101,6 +101,8 @@ public class PluginManagerCore {
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Runnable> myDisablePluginListeners;
|
||||
|
||||
/**
|
||||
* do not call this method during bootstrap, should be called in a copy of PluginManager, loaded by IdeaClassLoader
|
||||
*/
|
||||
@@ -140,6 +142,7 @@ public class PluginManagerCore {
|
||||
reader.close();
|
||||
if (!requiredPlugins.isEmpty()) {
|
||||
savePluginsList(disabledPlugins, false, new File(PathManager.getConfigPath(), DISABLED_PLUGINS_FILENAME));
|
||||
fireEditDisablePlugins();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,6 +207,27 @@ public class PluginManagerCore {
|
||||
return app != null && app.isUnitTestMode();
|
||||
}
|
||||
|
||||
public static void addDisablePluginListener(@NotNull Runnable listener) {
|
||||
if (myDisablePluginListeners == null) {
|
||||
myDisablePluginListeners = new ArrayList<Runnable>();
|
||||
}
|
||||
myDisablePluginListeners.add(listener);
|
||||
}
|
||||
|
||||
public static void removeDisablePluginListener(@NotNull Runnable listener) {
|
||||
if (myDisablePluginListeners != null) {
|
||||
myDisablePluginListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private static void fireEditDisablePlugins() {
|
||||
if (myDisablePluginListeners != null) {
|
||||
for (Runnable listener : myDisablePluginListeners) {
|
||||
listener.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void savePluginsList(@NotNull Collection<String> ids, boolean append, @NotNull File plugins) throws IOException {
|
||||
if (!plugins.isFile()) {
|
||||
FileUtil.ensureCanCreateFile(plugins);
|
||||
@@ -256,6 +280,7 @@ public class PluginManagerCore {
|
||||
File plugins = new File(PathManager.getConfigPath(), DISABLED_PLUGINS_FILENAME);
|
||||
savePluginsList(ids, append, plugins);
|
||||
ourDisabledPlugins = null;
|
||||
fireEditDisablePlugins();
|
||||
}
|
||||
|
||||
public static Logger getLogger() {
|
||||
|
||||
@@ -63,7 +63,12 @@ public class HelpManagerImpl extends HelpManager {
|
||||
|
||||
if (broker == null) {
|
||||
ApplicationInfoEx info = ApplicationInfoEx.getInstanceEx();
|
||||
String productVersion = info.getMajorVersion() + "." + info.getMinorVersion();
|
||||
String minorVersion = info.getMinorVersion();
|
||||
int dot = minorVersion.indexOf('.');
|
||||
if (dot != -1) {
|
||||
minorVersion = minorVersion.substring(0, dot);
|
||||
}
|
||||
String productVersion = info.getMajorVersion() + "." + minorVersion;
|
||||
String productCode = info.getPackageCode();
|
||||
|
||||
String url = info.getWebHelpUrl() + "/" + productVersion + "/?" + id;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.ide.cloudConfig;
|
||||
|
||||
import com.intellij.ide.customize.AbstractCustomizeWizardStep;
|
||||
import com.intellij.openapi.application.ConfigImportSettings;
|
||||
import com.intellij.openapi.application.ImportOldConfigsPanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Alexander Lobas
|
||||
*/
|
||||
public abstract class CloudConfigProvider {
|
||||
private static CloudConfigProvider myProvider;
|
||||
|
||||
@Nullable
|
||||
public static CloudConfigProvider getProvider() {
|
||||
return myProvider;
|
||||
}
|
||||
|
||||
public static void setProvider(@Nullable CloudConfigProvider provider) {
|
||||
myProvider = provider;
|
||||
}
|
||||
|
||||
public abstract void initConfigsPanel(@NotNull ImportOldConfigsPanel dialog,
|
||||
@NotNull JPanel parentPanel,
|
||||
@NotNull ConfigImportSettings settings);
|
||||
|
||||
public abstract void importFinished(@NotNull File newConfigDir);
|
||||
|
||||
public abstract void beforeStartupWizard();
|
||||
|
||||
@Nullable
|
||||
public abstract String getLafClassName();
|
||||
|
||||
@NotNull
|
||||
public abstract List<String> getInstalledPlugins();
|
||||
|
||||
public abstract int initSteps(@NotNull List<AbstractCustomizeWizardStep> steps);
|
||||
|
||||
public abstract void startupWizardFinished();
|
||||
}
|
||||
+18
-6
@@ -23,6 +23,7 @@ import com.intellij.openapi.progress.util.AbstractProgressIndicatorExBase;
|
||||
import com.intellij.openapi.ui.VerticalFlowLayout;
|
||||
import com.intellij.openapi.updateSettings.impl.PluginDownloader;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.wm.ex.ProgressIndicatorEx;
|
||||
import com.intellij.ui.ColorUtil;
|
||||
import com.intellij.ui.JBColor;
|
||||
@@ -84,7 +85,7 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt
|
||||
int i = s.indexOf(':');
|
||||
String topic = s.substring(0, i);
|
||||
int j = s.indexOf(':', i + 1);
|
||||
final String description = s.substring(i + 1, j);
|
||||
String description = s.substring(i + 1, j);
|
||||
final String pluginId = s.substring(j + 1);
|
||||
IdeaPluginDescriptor foundDescriptor = null;
|
||||
for (IdeaPluginDescriptor descriptor : pluginsFromRepository) {
|
||||
@@ -96,18 +97,29 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt
|
||||
if (foundDescriptor == null) continue;
|
||||
final IdeaPluginDescriptor descriptor = foundDescriptor;
|
||||
|
||||
|
||||
|
||||
final boolean isVIM = PluginGroups.IDEA_VIM_PLUGIN_ID.equals(descriptor.getPluginId().getIdString());
|
||||
boolean isCloud = "#Cloud".equals(topic);
|
||||
|
||||
if (isCloud) {
|
||||
title = descriptor.getName();
|
||||
description = StringUtil.defaultIfEmpty(descriptor.getDescription(), "No description available");
|
||||
topic = StringUtil.defaultIfEmpty(descriptor.getCategory(), "Unknown");
|
||||
}
|
||||
|
||||
JLabel titleLabel = new JLabel("<html><body><h2 style=\"text-align:left;\">" + title + "</h2></body></html>");
|
||||
JLabel topicLabel = new JLabel("<html><body><h4 style=\"text-align:left;color:#808080;font-weight:bold;\">" + topic + "</h4></body></html>");
|
||||
|
||||
JLabel descriptionLabel = createHTMLLabel(description);
|
||||
JLabel warningLabel = null;
|
||||
if (isVIM) {
|
||||
warningLabel = createHTMLLabel("Recommended only if you are<br> familiar with Vim.");
|
||||
warningLabel.setIcon(AllIcons.General.BalloonWarning);
|
||||
if (isVIM || isCloud) {
|
||||
if (isCloud) {
|
||||
warningLabel = createHTMLLabel("JBA account");
|
||||
warningLabel.setIcon(AllIcons.General.BalloonInformation);
|
||||
}
|
||||
else {
|
||||
warningLabel = createHTMLLabel("Recommended only if you are<br> familiar with Vim.");
|
||||
warningLabel.setIcon(AllIcons.General.BalloonWarning);
|
||||
}
|
||||
|
||||
if (!SystemInfo.isWindows) UIUtil.applyStyle(UIUtil.ComponentStyle.SMALL, warningLabel);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.ide.customize;
|
||||
|
||||
import com.intellij.ide.cloudConfig.CloudConfigProvider;
|
||||
import com.intellij.ide.startup.StartupActionScriptManager;
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
@@ -58,6 +59,12 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis
|
||||
getPeer().setAppIcons();
|
||||
|
||||
stepsProvider.initSteps(this, mySteps);
|
||||
|
||||
CloudConfigProvider configProvider = CloudConfigProvider.getProvider();
|
||||
if (configProvider != null) {
|
||||
myIndex = configProvider.initSteps(mySteps);
|
||||
}
|
||||
|
||||
if (mySteps.isEmpty()) {
|
||||
throw new IllegalArgumentException(stepsProvider + " provided no steps");
|
||||
}
|
||||
|
||||
+19
-1
@@ -17,6 +17,7 @@ package com.intellij.ide.customize;
|
||||
|
||||
import com.intellij.CommonBundle;
|
||||
import com.intellij.ide.WelcomeWizardUtil;
|
||||
import com.intellij.ide.cloudConfig.CloudConfigProvider;
|
||||
import com.intellij.ide.ui.LafManager;
|
||||
import com.intellij.ide.ui.laf.IntelliJLaf;
|
||||
import com.intellij.ide.ui.laf.LafManagerImpl;
|
||||
@@ -26,7 +27,9 @@ import com.intellij.openapi.options.OptionsBundle;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.util.IconUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -89,7 +92,7 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep {
|
||||
myColumnMode = myThemes.size() > 2;
|
||||
JPanel buttonsPanel = new JPanel(new GridLayout(myColumnMode ? myThemes.size() : 1, myColumnMode ? 1 : myThemes.size(), 5, 5));
|
||||
ButtonGroup group = new ButtonGroup();
|
||||
final ThemeInfo myDefaultTheme = myThemes.iterator().next();
|
||||
final ThemeInfo myDefaultTheme = getDefaultTheme();
|
||||
|
||||
for (final ThemeInfo theme : myThemes) {
|
||||
final JRadioButton radioButton = new JRadioButton(theme.name, myDefaultTheme == theme);
|
||||
@@ -146,6 +149,21 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ThemeInfo getDefaultTheme() {
|
||||
CloudConfigProvider provider = CloudConfigProvider.getProvider();
|
||||
if (provider != null) {
|
||||
String lafClassName = provider.getLafClassName();
|
||||
if (lafClassName != null) {
|
||||
ThemeInfo result = ContainerUtil.find(myThemes, theme -> lafClassName.equals(theme.laf));
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return myThemes.iterator().next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension size = super.getPreferredSize();
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.ide.customize;
|
||||
|
||||
import com.intellij.ide.WelcomeWizardUtil;
|
||||
import com.intellij.ide.cloudConfig.CloudConfigProvider;
|
||||
import com.intellij.ide.plugins.IdeaPluginDescriptor;
|
||||
import com.intellij.ide.plugins.PluginManagerCore;
|
||||
import com.intellij.ide.plugins.RepositoryHelper;
|
||||
@@ -32,6 +33,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class PluginGroups {
|
||||
@@ -84,6 +86,7 @@ public class PluginGroups {
|
||||
PluginManagerCore.loadDisabledPlugins(new File(PathManager.getConfigPath()).getPath(), myDisabledPluginIds);
|
||||
|
||||
initGroups(myTree, myFeaturedPlugins);
|
||||
initCloudPlugins();
|
||||
}
|
||||
|
||||
public void setLoadingCallback(Runnable loadingCallback) {
|
||||
@@ -93,6 +96,29 @@ public class PluginGroups {
|
||||
}
|
||||
}
|
||||
|
||||
private void initCloudPlugins() {
|
||||
CloudConfigProvider provider = CloudConfigProvider.getProvider();
|
||||
if (provider == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> plugins = provider.getInstalledPlugins();
|
||||
if (plugins.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Iterator<Entry<String, String>> I = myFeaturedPlugins.entrySet().iterator(); I.hasNext(); ) {
|
||||
String value = I.next().getValue();
|
||||
if (ContainerUtil.find(plugins, plugin -> value.endsWith(":" + plugin)) != null) {
|
||||
I.remove();
|
||||
}
|
||||
}
|
||||
|
||||
for (String plugin : plugins) {
|
||||
myFeaturedPlugins.put(plugin, "#Cloud:#Cloud:" + plugin);
|
||||
}
|
||||
}
|
||||
|
||||
protected void
|
||||
initGroups(Map<String, Pair<Icon, List<String>>> tree, Map<String, String> featuredPlugins) {
|
||||
tree.put(CORE, Pair.create((Icon)null, Arrays.asList(
|
||||
@@ -263,7 +289,7 @@ public class PluginGroups {
|
||||
private void initIfNeed() {
|
||||
if (myInitialized) return;
|
||||
myInitialized = true;
|
||||
for (Map.Entry<String, Pair<Icon, List<String>>> entry : myTree.entrySet()) {
|
||||
for (Entry<String, Pair<Icon, List<String>>> entry : myTree.entrySet()) {
|
||||
final String group = entry.getKey();
|
||||
if (CORE.equals(group)) continue;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.ide.DataManager;
|
||||
import com.intellij.ide.IdeEventQueue;
|
||||
import com.intellij.ide.impl.dataRules.*;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
@@ -51,6 +52,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class DataManagerImpl extends DataManager {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.impl.DataManagerImpl");
|
||||
@@ -209,13 +211,24 @@ public class DataManagerImpl extends DataManager {
|
||||
|
||||
@Override
|
||||
public AsyncResult<DataContext> getDataContextFromFocus() {
|
||||
final AsyncResult<DataContext> context = new AsyncResult<>();
|
||||
|
||||
IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> context.setDone(getDataContext()));
|
||||
|
||||
AsyncResult<DataContext> context = new AsyncResult<>();
|
||||
doWhenFocusSettlesDownInModality(context, ModalityState.current());
|
||||
return context;
|
||||
}
|
||||
|
||||
private void doWhenFocusSettlesDownInModality(AsyncResult<DataContext> context, ModalityState modality) {
|
||||
AtomicBoolean immediate = new AtomicBoolean(true);
|
||||
IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> {
|
||||
if (immediate.get()) {
|
||||
context.setDone(getDataContext());
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationManager.getApplication().invokeLater(() -> doWhenFocusSettlesDownInModality(context, modality), modality);
|
||||
});
|
||||
immediate.set(false);
|
||||
}
|
||||
|
||||
public DataContext getDataContextTest(Component component) {
|
||||
DataContext dataContext = getDataContext(component);
|
||||
if (myWindowManager == null) {
|
||||
|
||||
+1
-2
@@ -45,7 +45,6 @@ import com.intellij.ui.ScrollPaneFactory;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.ui.StatusText;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -151,7 +150,7 @@ public class InstalledPluginsManagerMain extends PluginManagerMain {
|
||||
}
|
||||
}
|
||||
|
||||
PluginInstaller.install(file, file.getName(), false);
|
||||
PluginInstaller.install(file, file.getName(), false, pluginDescriptor);
|
||||
ourState.onPluginInstall(pluginDescriptor);
|
||||
checkInstalledPluginDependencies(model, pluginDescriptor, parent);
|
||||
callback.consume(pair(file, (IdeaPluginDescriptor)pluginDescriptor));
|
||||
|
||||
@@ -30,10 +30,10 @@ import com.intellij.openapi.updateSettings.impl.UpdateSettings;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.GuiUtils;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.io.ZipUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -232,6 +232,8 @@ public class PluginInstaller {
|
||||
if (pluginDescriptor != null) {
|
||||
StartupActionScriptManager.ActionCommand deleteOld = new StartupActionScriptManager.DeleteCommand(pluginDescriptor.getPath());
|
||||
StartupActionScriptManager.addActionCommand(deleteOld);
|
||||
|
||||
fireState(pluginDescriptor, false);
|
||||
}
|
||||
else {
|
||||
PluginManagerMain.LOG.error("Plugin not found: " + pluginId);
|
||||
@@ -240,7 +242,10 @@ public class PluginInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
public static void install(final File fromFile, final String pluginName, boolean deleteFromFile) throws IOException {
|
||||
public static void install(@NotNull File fromFile,
|
||||
@NotNull String pluginName,
|
||||
boolean deleteFromFile,
|
||||
@NotNull IdeaPluginDescriptor descriptor) throws IOException {
|
||||
//noinspection HardCodedStringLiteral
|
||||
if (fromFile.getName().endsWith(".jar")) {
|
||||
// add command to copy file to the IDEA/plugins path
|
||||
@@ -267,5 +272,37 @@ public class PluginInstaller {
|
||||
StartupActionScriptManager.ActionCommand deleteTemp = new StartupActionScriptManager.DeleteCommand(fromFile);
|
||||
StartupActionScriptManager.addActionCommand(deleteTemp);
|
||||
}
|
||||
|
||||
fireState(descriptor, true);
|
||||
}
|
||||
|
||||
private static List<PluginStateListener> myStateListeners;
|
||||
|
||||
public static void addStateListener(@NotNull PluginStateListener listener) {
|
||||
if (myStateListeners == null) {
|
||||
myStateListeners = new ArrayList<>();
|
||||
}
|
||||
myStateListeners.add(listener);
|
||||
}
|
||||
|
||||
public static void removeStateListener(@NotNull PluginStateListener listener) {
|
||||
if (myStateListeners != null) {
|
||||
myStateListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private static void fireState(@NotNull IdeaPluginDescriptor descriptor, boolean install) {
|
||||
if (myStateListeners != null) {
|
||||
UIUtil.invokeLaterIfNeeded(() -> {
|
||||
for (PluginStateListener listener : myStateListeners) {
|
||||
if (install) {
|
||||
listener.install(descriptor);
|
||||
}
|
||||
else {
|
||||
listener.uninstall(descriptor);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.ide.plugins;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Alexander Lobas
|
||||
*/
|
||||
public interface PluginStateListener {
|
||||
void install(@NotNull IdeaPluginDescriptor descriptor);
|
||||
|
||||
void uninstall(@NotNull IdeaPluginDescriptor descriptor);
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.idea;
|
||||
|
||||
import com.intellij.ide.cloudConfig.CloudConfigProvider;
|
||||
import com.intellij.ide.customize.CustomizeIDEWizardDialog;
|
||||
import com.intellij.ide.customize.CustomizeIDEWizardStepsProvider;
|
||||
import com.intellij.ide.plugins.PluginManagerCore;
|
||||
@@ -83,6 +84,8 @@ public class StartupUtil {
|
||||
|
||||
interface AppStarter {
|
||||
void start(boolean newConfigFolder);
|
||||
|
||||
default void beforeImportConfigs() {}
|
||||
}
|
||||
|
||||
static void prepareAndStart(String[] args, AppStarter appStarter) {
|
||||
@@ -125,6 +128,7 @@ public class StartupUtil {
|
||||
}
|
||||
|
||||
if (newConfigFolder) {
|
||||
appStarter.beforeImportConfigs();
|
||||
ConfigImportHelper.importConfigsTo(PathManager.getConfigPath());
|
||||
}
|
||||
|
||||
@@ -422,8 +426,18 @@ public class StartupUtil {
|
||||
return;
|
||||
}
|
||||
|
||||
CloudConfigProvider configProvider = CloudConfigProvider.getProvider();
|
||||
if (configProvider != null) {
|
||||
configProvider.beforeStartupWizard();
|
||||
}
|
||||
|
||||
new CustomizeIDEWizardDialog(provider).show();
|
||||
|
||||
PluginManagerCore.invalidatePlugins();
|
||||
if (configProvider != null) {
|
||||
configProvider.startupWizardFinished();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.openapi.application;
|
||||
|
||||
import com.intellij.ide.cloudConfig.CloudConfigProvider;
|
||||
import com.intellij.ide.plugins.IdeaPluginDescriptorImpl;
|
||||
import com.intellij.ide.plugins.PluginManager;
|
||||
import com.intellij.ide.plugins.PluginManagerCore;
|
||||
@@ -79,6 +80,12 @@ public class ConfigImportHelper {
|
||||
doImport(newConfigDir, oldConfigDir, settings, installationHome);
|
||||
settings.importFinished(newConfigPath);
|
||||
System.setProperty(CONFIG_IMPORTED_IN_CURRENT_SESSION_KEY, Boolean.TRUE.toString());
|
||||
|
||||
CloudConfigProvider provider = CloudConfigProvider.getProvider();
|
||||
if (provider != null) {
|
||||
provider.importFinished(newConfigDir);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -230,7 +237,7 @@ public class ConfigImportHelper {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static File getOldConfigDir(@Nullable File oldInstallHome, ConfigImportSettings settings) {
|
||||
public static File getOldConfigDir(@Nullable File oldInstallHome, ConfigImportSettings settings) {
|
||||
if (oldInstallHome == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.application.ImportOldConfigsPanel">
|
||||
<grid id="47e28" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="47e28" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="10" left="10" bottom="10" right="10"/>
|
||||
<constraints>
|
||||
<xy x="28" y="33" width="566" height="272"/>
|
||||
<xy x="28" y="33" width="575" height="286"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
@@ -11,7 +11,7 @@
|
||||
<grid id="1c1db" layout-manager="GridLayoutManager" row-count="1" 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>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
@@ -38,7 +38,7 @@
|
||||
</grid>
|
||||
<vspacer id="ccd5d">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<grid id="865b3" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
@@ -123,6 +123,16 @@
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
<grid id="ece75" binding="myCustomPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<visible value="false"/>
|
||||
</properties>
|
||||
<border type="none"/>
|
||||
<children/>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.openapi.application;
|
||||
|
||||
import com.intellij.ide.cloudConfig.CloudConfigProvider;
|
||||
import com.intellij.openapi.MnemonicHelper;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
@@ -45,6 +46,7 @@ public class ImportOldConfigsPanel extends JDialog {
|
||||
private JLabel mySuggestLabel;
|
||||
private JLabel myHomeLabel;
|
||||
private JRadioButton myRbImportAuto;
|
||||
private JPanel myCustomPanel;
|
||||
|
||||
private final File myGuessedOldConfig;
|
||||
private final ConfigImportSettings mySettings;
|
||||
@@ -120,6 +122,11 @@ public class ImportOldConfigsPanel extends JDialog {
|
||||
}
|
||||
});
|
||||
|
||||
CloudConfigProvider configProvider = CloudConfigProvider.getProvider();
|
||||
if (configProvider != null) {
|
||||
configProvider.initConfigsPanel(this, myCustomPanel, mySettings);
|
||||
}
|
||||
|
||||
getContentPane().setLayout(new BorderLayout());
|
||||
getContentPane().add(myRootPanel);
|
||||
getRootPane().setDefaultButton(myOkButton);
|
||||
|
||||
+1
-1
@@ -218,7 +218,7 @@ public class PluginDownloader {
|
||||
StartupActionScriptManager.ActionCommand deleteOld = new StartupActionScriptManager.DeleteCommand(myOldFile);
|
||||
StartupActionScriptManager.addActionCommand(deleteOld);
|
||||
}
|
||||
PluginInstaller.install(myFile, getPluginName(), true);
|
||||
PluginInstaller.install(myFile, getPluginName(), true, myDescriptor);
|
||||
InstalledPluginsState state = InstalledPluginsState.getInstanceIfLoaded();
|
||||
if (state != null) {
|
||||
state.onPluginInstall(myDescriptor);
|
||||
|
||||
+2
-1
@@ -24,6 +24,7 @@ import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
import com.jetbrains.edu.learning.courseFormat.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import com.jetbrains.edu.learning.courseGeneration.StudyGenerator;
|
||||
import com.jetbrains.edu.learning.stepic.CCStepicConnector;
|
||||
import com.jetbrains.edu.learning.stepic.CourseInfo;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -61,7 +62,7 @@ public class CCGetCourseFromStepic extends DumbAwareAction {
|
||||
|
||||
private static void createCourse(Project project, String courseId) {
|
||||
final VirtualFile baseDir = project.getBaseDir();
|
||||
final CourseInfo info = EduStepicConnector.getCourseInfo(project, courseId);
|
||||
final CourseInfo info = CCStepicConnector.getCourseInfo(project, courseId);
|
||||
if (info == null) return;
|
||||
|
||||
final Course course = EduStepicConnector.getCourse(project, info);
|
||||
|
||||
+5
-5
@@ -15,8 +15,8 @@ import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
|
||||
import com.jetbrains.edu.learning.stepic.CCStepicConnector;
|
||||
import com.jetbrains.edu.learning.stepic.CourseInfo;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
@@ -59,21 +59,21 @@ public class CCPushCourse extends DumbAwareAction {
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
for (Lesson lesson : course.getLessons()) {
|
||||
if (lesson.getId() > 0) {
|
||||
EduStepicConnector.updateLesson(project, lesson, indicator);
|
||||
CCStepicConnector.updateLesson(project, lesson, indicator);
|
||||
}
|
||||
else {
|
||||
final CourseInfo info = CourseInfo.fromCourse(course);
|
||||
final int lessonId = EduStepicConnector.postLesson(project, lesson, indicator);
|
||||
final int lessonId = CCStepicConnector.postLesson(project, lesson, indicator);
|
||||
final List<Integer> sections = info.getSections();
|
||||
final Integer sectionId = sections.get(sections.size() - 1);
|
||||
EduStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId);
|
||||
CCStepicConnector.postUnit(project, lessonId, lesson.getIndex(), sectionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
EduStepicConnector.postCourseWithProgress(project, course);
|
||||
CCStepicConnector.postCourseWithProgress(project, course);
|
||||
}
|
||||
EduUsagesCollector.courseUploaded();
|
||||
}
|
||||
|
||||
+4
-4
@@ -15,8 +15,8 @@ import com.jetbrains.edu.coursecreator.CCUtils;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
import com.jetbrains.edu.learning.stepic.CCStepicConnector;
|
||||
import com.jetbrains.edu.learning.stepic.CourseInfo;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicNames;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -77,14 +77,14 @@ public class CCPushLesson extends DumbAwareAction {
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
indicator.setText("Uploading lesson to " + EduStepicNames.STEPIC_URL);
|
||||
if (lesson.getId() > 0) {
|
||||
EduStepicConnector.updateLesson(project, lesson, indicator);
|
||||
CCStepicConnector.updateLesson(project, lesson, indicator);
|
||||
}
|
||||
else {
|
||||
final CourseInfo info = CourseInfo.fromCourse(course);
|
||||
final int lessonId = EduStepicConnector.postLesson(project, lesson, indicator);
|
||||
final int lessonId = CCStepicConnector.postLesson(project, lesson, indicator);
|
||||
final List<Integer> sections = info.getSections();
|
||||
final Integer sectionId = sections.get(sections.size()-1);
|
||||
EduStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId);
|
||||
CCStepicConnector.postUnit(project, lessonId, lesson.getIndex(), sectionId);
|
||||
}
|
||||
}});
|
||||
}
|
||||
|
||||
+5
-5
@@ -16,7 +16,7 @@ import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
|
||||
import com.jetbrains.edu.learning.stepic.CCStepicConnector;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicNames;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -48,7 +48,7 @@ public class CCPushTask extends DumbAwareAction {
|
||||
if (lesson != null && lesson.getId() > 0 && course.getId() > 0) {
|
||||
e.getPresentation().setEnabledAndVisible(true);
|
||||
final com.jetbrains.edu.learning.courseFormat.Task task = lesson.getTask(taskDir.getName());
|
||||
if (task.getStepicId() <= 0) {
|
||||
if (task.getStepId() <= 0) {
|
||||
e.getPresentation().setText("Upload Task to Stepik");
|
||||
}
|
||||
}
|
||||
@@ -81,11 +81,11 @@ public class CCPushTask extends DumbAwareAction {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
indicator.setText("Uploading task to " + EduStepicNames.STEPIC_URL);
|
||||
if (task.getStepicId() <= 0) {
|
||||
EduStepicConnector.postTask(project, task, lesson.getId());
|
||||
if (task.getStepId() <= 0) {
|
||||
CCStepicConnector.postTask(project, task, lesson.getId());
|
||||
}
|
||||
else {
|
||||
EduStepicConnector.updateTask(project, task);
|
||||
CCStepicConnector.updateTask(project, task);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
+13
-20
@@ -97,26 +97,19 @@ public class StudyProjectComponent implements ProjectComponent {
|
||||
}
|
||||
|
||||
StudyUtils.registerStudyToolWindow(course, myProject);
|
||||
StartupManager.getInstance(myProject).runWhenProjectIsInitialized(() -> ApplicationManager.getApplication().invokeLater(new DumbAwareRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runWriteAction(new DumbAwareRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Course course = StudyTaskManager.getInstance(myProject).getCourse();
|
||||
if (course != null) {
|
||||
final UISettings instance = UISettings.getInstance();
|
||||
if (instance != null) {
|
||||
instance.HIDE_TOOL_STRIPES = false;
|
||||
instance.fireUISettingsChanged();
|
||||
}
|
||||
registerShortcuts();
|
||||
EduUsagesCollector.projectTypeOpened(course.isAdaptive() ? EduNames.ADAPTIVE : EduNames.STUDY);
|
||||
}
|
||||
StartupManager.getInstance(myProject).runWhenProjectIsInitialized(() -> ApplicationManager.getApplication().invokeLater(
|
||||
(DumbAwareRunnable)() -> ApplicationManager.getApplication().runWriteAction((DumbAwareRunnable)() -> {
|
||||
Course course1 = StudyTaskManager.getInstance(myProject).getCourse();
|
||||
if (course1 != null) {
|
||||
final UISettings instance = UISettings.getInstance();
|
||||
if (instance != null) {
|
||||
instance.HIDE_TOOL_STRIPES = false;
|
||||
instance.fireUISettingsChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
registerShortcuts();
|
||||
EduUsagesCollector.projectTypeOpened(course1.isAdaptive() ? EduNames.ADAPTIVE : EduNames.STUDY);
|
||||
}
|
||||
})));
|
||||
}
|
||||
|
||||
private void registerShortcuts() {
|
||||
@@ -197,7 +190,7 @@ public class StudyProjectComponent implements ProjectComponent {
|
||||
final ArrayList<Task> tasks = new ArrayList<>();
|
||||
for (Task task : lesson.getTaskList()) {
|
||||
index += 1;
|
||||
final Task studentTask = studentLesson.getTask(task.getStepicId());
|
||||
final Task studentTask = studentLesson.getTask(task.getStepId());
|
||||
if (studentTask != null && StudyStatus.Solved.equals(studentTask.getStatus())) {
|
||||
studentTask.setIndex(index);
|
||||
tasks.add(studentTask);
|
||||
|
||||
+2
-12
@@ -10,7 +10,6 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.edu.learning.StudyPluginConfigurator;
|
||||
import com.jetbrains.edu.learning.StudyState;
|
||||
@@ -24,7 +23,6 @@ import com.jetbrains.edu.learning.courseFormat.StudyStatus;
|
||||
import com.jetbrains.edu.learning.courseFormat.Task;
|
||||
import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
|
||||
import com.jetbrains.edu.learning.stepic.StepicUser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -40,7 +38,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
private final Ref<Boolean> myCheckInProcess;
|
||||
private final Process myTestProcess;
|
||||
private final String myCommandLine;
|
||||
private final String FAILED_CHECK_LAUNCH = "Failed to launch checking";
|
||||
private static final String FAILED_CHECK_LAUNCH = "Failed to launch checking";
|
||||
|
||||
public StudyCheckTask(Project project, StudyState studyState, Ref<Boolean> checkInProcess, Process testProcess, String commandLine) {
|
||||
super(project, "Checking Task");
|
||||
@@ -100,7 +98,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
runAfterTaskCheckedActions();
|
||||
final Course course = StudyTaskManager.getInstance(myProject).getCourse();
|
||||
if (course != null && EduNames.STUDY.equals(course.getCourseMode())) {
|
||||
postAttemptToStepic(testsOutput);
|
||||
EduStepicConnector.postAttempt(myTask, testsOutput.isSuccess(), myProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,12 +209,4 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
LOG.warn("No configurator is provided for the plugin");
|
||||
}
|
||||
}
|
||||
|
||||
protected void postAttemptToStepic(@NotNull StudyTestsOutputParser.TestsOutput testsOutput) {
|
||||
final StudyTaskManager studySettings = StudyTaskManager.getInstance(myProject);
|
||||
final StepicUser user = studySettings.getUser();
|
||||
final String login = user.getEmail();
|
||||
final String password = StringUtil.isEmptyOrSpaces(login) ? "" : user.getPassword();
|
||||
EduStepicConnector.postAttempt(myTask, testsOutput.isSuccess(), login, password);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -151,7 +151,7 @@ public class Course {
|
||||
for (Lesson lesson : lessons) {
|
||||
lesson.setUpdateDate(EduStepicConnector.getLessonUpdateDate(lesson.getId()));
|
||||
for (Task task : lesson.getTaskList()) {
|
||||
task.setUpdateDate(EduStepicConnector.getTaskUpdateDate(task.getStepicId()));
|
||||
task.setUpdateDate(EduStepicConnector.getTaskUpdateDate(task.getStepId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ public class Lesson implements StudyItem {
|
||||
|
||||
public Task getTask(int id) {
|
||||
for (Task task : taskList) {
|
||||
if (task.getStepicId() == id) {
|
||||
if (task.getStepId() == id) {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -31,7 +31,7 @@ public class Task implements StudyItem {
|
||||
private StudyStatus myStatus = StudyStatus.Unchecked;
|
||||
|
||||
@SerializedName("stepic_id")
|
||||
@Expose private int myStepicId;
|
||||
@Expose private int myStepId;
|
||||
|
||||
@SerializedName("task_files")
|
||||
@Expose public Map<String, TaskFile> taskFiles = new HashMap<>();
|
||||
@@ -204,12 +204,12 @@ public class Task implements StudyItem {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setStepicId(int stepicId) {
|
||||
myStepicId = stepicId;
|
||||
public void setStepId(int stepId) {
|
||||
myStepId = stepId;
|
||||
}
|
||||
|
||||
public int getStepicId() {
|
||||
return myStepicId;
|
||||
public int getStepId() {
|
||||
return myStepId;
|
||||
}
|
||||
|
||||
public StudyStatus getStatus() {
|
||||
@@ -244,8 +244,8 @@ public class Task implements StudyItem {
|
||||
}
|
||||
|
||||
public boolean isUpToDate() {
|
||||
if (getStepicId() == 0) return true;
|
||||
final Date date = EduStepicConnector.getTaskUpdateDate(getStepicId());
|
||||
if (getStepId() == 0) return true;
|
||||
final Date date = EduStepicConnector.getTaskUpdateDate(getStepId());
|
||||
if (date == null) return true;
|
||||
if (myUpdateDate == null) return false;
|
||||
return !date.after(myUpdateDate);
|
||||
|
||||
+362
@@ -0,0 +1,362 @@
|
||||
package com.jetbrains.edu.learning.stepic;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileFilter;
|
||||
import com.jetbrains.edu.learning.StudySerializationUtils;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.core.EduUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
import com.jetbrains.edu.learning.courseFormat.Task;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CCStepicConnector {
|
||||
private static final Logger LOG = Logger.getInstance(CCStepicConnector.class.getName());
|
||||
|
||||
private CCStepicConnector() {
|
||||
}
|
||||
|
||||
public static CourseInfo getCourseInfo(Project project, String courseId) {
|
||||
final String url = EduStepicNames.COURSES + "/" + courseId;
|
||||
try {
|
||||
final StepicWrappers.CoursesContainer coursesContainer =
|
||||
EduStepicAuthorizedClient.getFromStepic(url, StepicWrappers.CoursesContainer.class, project);
|
||||
return coursesContainer.courses.get(0);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void postCourseWithProgress(final Project project, @NotNull final Course course) {
|
||||
ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Modal(project, "Uploading Course", true) {
|
||||
@Override
|
||||
public void run(@NotNull final ProgressIndicator indicator) {
|
||||
postCourse(project, course, indicator);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void postCourse(final Project project, @NotNull Course course, @NotNull final ProgressIndicator indicator) {
|
||||
indicator.setText("Uploading course to " + EduStepicNames.STEPIC_URL);
|
||||
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/courses");
|
||||
|
||||
final StepicUser currentUser = EduStepicAuthorizedClient.getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
final List<StepicUser> courseAuthors = course.getAuthors();
|
||||
for (int i = 0; i < courseAuthors.size(); i++) {
|
||||
if (courseAuthors.size() > i) {
|
||||
final StepicUser courseAuthor = courseAuthors.get(i);
|
||||
currentUser.setFirstName(courseAuthor.getFirstName());
|
||||
currentUser.setLastName(courseAuthor.getLastName());
|
||||
}
|
||||
}
|
||||
course.setAuthors(Collections.singletonList(currentUser));
|
||||
}
|
||||
|
||||
String requestBody = new Gson().toJson(new StepicWrappers.CourseWrapper(course));
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
final CloseableHttpResponse response = client.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine line = response.getStatusLine();
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
return;
|
||||
}
|
||||
final CourseInfo postedCourse = new Gson().fromJson(responseString, StepicWrappers.CoursesContainer.class).courses.get(0);
|
||||
course.setId(postedCourse.id);
|
||||
final int sectionId = postModule(project, postedCourse.id, 1, String.valueOf(postedCourse.getName()));
|
||||
int position = 1;
|
||||
for (Lesson lesson : course.getLessons()) {
|
||||
indicator.checkCanceled();
|
||||
final int lessonId = postLesson(project, lesson, indicator);
|
||||
postUnit(project, lessonId, position, sectionId);
|
||||
position += 1;
|
||||
}
|
||||
ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(project, postedCourse.id, indicator));
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void postAdditionalFiles(@NotNull final Project project, int id, ProgressIndicator indicator) {
|
||||
final VirtualFile baseDir = project.getBaseDir();
|
||||
final List<VirtualFile> files = VfsUtil.getChildren(baseDir, new VirtualFileFilter() {
|
||||
@Override
|
||||
public boolean accept(VirtualFile file) {
|
||||
final String name = file.getName();
|
||||
return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS) &&
|
||||
!"pyc".equals(file.getExtension()) && !file.isDirectory() && !name.equals(EduNames.TEST_HELPER) && !name.startsWith(".");
|
||||
}
|
||||
});
|
||||
|
||||
if (!files.isEmpty()) {
|
||||
final int sectionId = postModule(project, id, 2, EduNames.PYCHARM_ADDITIONAL);
|
||||
final Lesson lesson = new Lesson();
|
||||
lesson.setName(EduNames.PYCHARM_ADDITIONAL);
|
||||
final Task task = new Task();
|
||||
task.setLesson(lesson);
|
||||
task.setName(EduNames.PYCHARM_ADDITIONAL);
|
||||
task.setIndex(1);
|
||||
task.setText(EduNames.PYCHARM_ADDITIONAL);
|
||||
for (VirtualFile file : files) {
|
||||
try {
|
||||
if (file != null) {
|
||||
if (EduUtils.isImage(file.getName())) {
|
||||
task.addTestsTexts(file.getName(), Base64.encodeBase64URLSafeString(FileUtil.loadBytes(file.getInputStream())));
|
||||
}
|
||||
else {
|
||||
task.addTestsTexts(file.getName(), FileUtil.loadTextAndClose(file.getInputStream()));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error("Can't find file " + file.getPath());
|
||||
}
|
||||
}
|
||||
lesson.addTask(task);
|
||||
lesson.setIndex(1);
|
||||
final int lessonId = postLesson(project, lesson, indicator);
|
||||
postUnit(project, lessonId, 1, sectionId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void postUnit(@NotNull final Project project, int lessonId, int position, int sectionId) {
|
||||
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.UNITS);
|
||||
final StepicWrappers.UnitWrapper unitWrapper = new StepicWrappers.UnitWrapper();
|
||||
unitWrapper.unit = new StepicWrappers.Unit();
|
||||
unitWrapper.unit.lesson = lessonId;
|
||||
unitWrapper.unit.position = position;
|
||||
unitWrapper.unit.section = sectionId;
|
||||
|
||||
String requestBody = new Gson().toJson(unitWrapper);
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
final CloseableHttpResponse response = client.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine line = response.getStatusLine();
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static int postModule(@NotNull final Project project, int courseId, int position, @NotNull final String title) {
|
||||
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/sections");
|
||||
final StepicWrappers.Section section = new StepicWrappers.Section();
|
||||
section.course = courseId;
|
||||
section.title = title;
|
||||
section.position = position;
|
||||
final StepicWrappers.SectionWrapper sectionContainer = new StepicWrappers.SectionWrapper();
|
||||
sectionContainer.section = section;
|
||||
String requestBody = new Gson().toJson(sectionContainer);
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
final CloseableHttpResponse response = client.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine line = response.getStatusLine();
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
return -1;
|
||||
}
|
||||
final StepicWrappers.Section
|
||||
postedSection = new Gson().fromJson(responseString, StepicWrappers.SectionContainer.class).sections.get(0);
|
||||
return postedSection.id;
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int updateTask(@NotNull final Project project, @NotNull final Task task) {
|
||||
final Lesson lesson = task.getLesson();
|
||||
final int lessonId = lesson.getId();
|
||||
|
||||
final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + "/step-sources/" + String.valueOf(task.getStepId()));
|
||||
final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().
|
||||
registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create();
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
task.addTestsTexts("tests.py", task.getTestsText(project));
|
||||
final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId));
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
final CloseableHttpResponse response = client.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
EntityUtils.consume(responseEntity);
|
||||
final StatusLine line = response.getStatusLine();
|
||||
if (line.getStatusCode() != HttpStatus.SC_OK) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
});
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int updateLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) {
|
||||
final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + EduStepicNames.LESSONS + String.valueOf(lesson.getId()));
|
||||
|
||||
String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson));
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
final CloseableHttpResponse response = client.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine line = response.getStatusLine();
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() != HttpStatus.SC_OK) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
return -1;
|
||||
}
|
||||
final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0);
|
||||
for (Integer step : postedLesson.steps) {
|
||||
deleteTask(step, project);
|
||||
}
|
||||
|
||||
for (Task task : lesson.getTaskList()) {
|
||||
indicator.checkCanceled();
|
||||
postTask(project, task, lesson.getId());
|
||||
}
|
||||
return lesson.getId();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int postLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) {
|
||||
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/lessons");
|
||||
|
||||
String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson));
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
final CloseableHttpResponse response = client.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine line = response.getStatusLine();
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
return 0;
|
||||
}
|
||||
final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0);
|
||||
lesson.setId(postedLesson.getId());
|
||||
for (Task task : lesson.getTaskList()) {
|
||||
indicator.checkCanceled();
|
||||
postTask(project, task, postedLesson.getId());
|
||||
}
|
||||
return postedLesson.getId();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void deleteTask(@NotNull final Integer task, Project project) {
|
||||
final HttpDelete request = new HttpDelete(EduStepicNames.STEPIC_API_URL + EduStepicNames.STEP_SOURCES + task);
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
try {
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
final CloseableHttpResponse response = client.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
EntityUtils.consume(responseEntity);
|
||||
final StatusLine line = response.getStatusLine();
|
||||
if (line.getStatusCode() != HttpStatus.SC_NO_CONTENT) {
|
||||
LOG.error("Failed to delete task " + responseString);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void postTask(final Project project, @NotNull final Task task, final int lessonId) {
|
||||
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/step-sources");
|
||||
//TODO: register type adapter for task files here?
|
||||
final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().
|
||||
registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create();
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId));
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
final CloseableHttpResponse response = client.execute(request);
|
||||
final StatusLine line = response.getStatusLine();
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
return;
|
||||
}
|
||||
|
||||
final JsonObject postedTask = new Gson().fromJson(responseString, JsonObject.class);
|
||||
final JsonObject stepSource = postedTask.getAsJsonArray("step-sources").get(0).getAsJsonObject();
|
||||
task.setStepId(stepSource.getAsJsonPrimitive("id").getAsInt());
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+34
-29
@@ -50,12 +50,10 @@ import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.jetbrains.edu.learning.stepic.EduStepicConnector.*;
|
||||
import static com.jetbrains.edu.learning.stepic.EduStepicConnector.getStep;
|
||||
|
||||
public class EduAdaptiveStepicConnector {
|
||||
public static final String PYTHON2 = "python2";
|
||||
@@ -67,12 +65,11 @@ public class EduAdaptiveStepicConnector {
|
||||
@Nullable
|
||||
public static Task getNextRecommendation(@NotNull final Project project, @NotNull Course course) {
|
||||
try {
|
||||
final CloseableHttpClient client = getHttpClient(project);
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
final URI uri = new URIBuilder(EduStepicNames.STEPIC_API_URL + EduStepicNames.RECOMMENDATIONS_URL)
|
||||
.addParameter(EduNames.COURSE, String.valueOf(course.getId()))
|
||||
.build();
|
||||
final HttpGet request = new HttpGet(uri);
|
||||
setHeaders(request, EduStepicNames.CONTENT_TYPE_APPL_JSON);
|
||||
setTimeout(request);
|
||||
|
||||
final CloseableHttpResponse response = client.execute(request);
|
||||
@@ -89,7 +86,8 @@ public class EduAdaptiveStepicConnector {
|
||||
final StepicWrappers.Recommendation recommendation = recomWrapper.recommendations[0];
|
||||
final String lessonId = recommendation.lesson;
|
||||
final StepicWrappers.LessonContainer
|
||||
lessonContainer = getFromStepic(EduStepicNames.LESSONS + lessonId, StepicWrappers.LessonContainer.class);
|
||||
lessonContainer = EduStepicAuthorizedClient
|
||||
.getFromStepic(EduStepicNames.LESSONS + lessonId, StepicWrappers.LessonContainer.class, project);
|
||||
if (lessonContainer.lessons.size() == 1) {
|
||||
final Lesson realLesson = lessonContainer.lessons.get(0);
|
||||
course.getLessons().get(0).setId(Integer.parseInt(lessonId));
|
||||
@@ -112,9 +110,6 @@ public class EduAdaptiveStepicConnector {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((statusCode == HttpStatus.SC_BAD_REQUEST || statusCode == HttpStatus.SC_UNAUTHORIZED) && login(project)) {
|
||||
return getNextRecommendation(project, course);
|
||||
}
|
||||
throw new IOException("Stepic returned non 200 status code: " + responseString);
|
||||
}
|
||||
}
|
||||
@@ -161,8 +156,7 @@ public class EduAdaptiveStepicConnector {
|
||||
final String json = new Gson()
|
||||
.toJson(new StepicWrappers.RecommendationReactionWrapper(new StepicWrappers.RecommendationReaction(reaction, user, lessonId)));
|
||||
post.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
|
||||
final CloseableHttpClient client = getHttpClient(project);
|
||||
setHeaders(post, EduStepicNames.CONTENT_TYPE_APPL_JSON);
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
setTimeout(post);
|
||||
try {
|
||||
final CloseableHttpResponse execute = client.execute(post);
|
||||
@@ -174,10 +168,6 @@ public class EduAdaptiveStepicConnector {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if ((statusCode == HttpStatus.SC_BAD_REQUEST || statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN)
|
||||
&& login(project)) {
|
||||
return postRecommendationReaction(project, lessonId, user, reaction);
|
||||
}
|
||||
LOG.warn("Stepic returned non-201 status code: " + statusCode + " " + entityString);
|
||||
return false;
|
||||
}
|
||||
@@ -208,7 +198,7 @@ public class EduAdaptiveStepicConnector {
|
||||
final String lessonName = EduNames.LESSON + String.valueOf(adaptive.getIndex());
|
||||
if (reaction == 0 || reaction == -1) {
|
||||
unsolvedTask.setName(task.getName());
|
||||
unsolvedTask.setStepicId(task.getStepicId());
|
||||
unsolvedTask.setStepId(task.getStepId());
|
||||
unsolvedTask.setText(task.getText());
|
||||
unsolvedTask.getTestsText().clear();
|
||||
unsolvedTask.setStatus(StudyStatus.Unchecked);
|
||||
@@ -318,7 +308,7 @@ public class EduAdaptiveStepicConnector {
|
||||
private static Task getTaskFromStep(Project project, int lessonID, @NotNull final StepicWrappers.Step step, @NotNull String name) {
|
||||
final Task task = new Task();
|
||||
task.setName(name);
|
||||
task.setStepicId(lessonID);
|
||||
task.setStepId(lessonID);
|
||||
task.setText(step.text);
|
||||
task.setStatus(StudyStatus.Unchecked);
|
||||
if (step.options.samples != null) {
|
||||
@@ -398,7 +388,7 @@ public class EduAdaptiveStepicConnector {
|
||||
final Editor editor = StudyUtils.getSelectedEditor(project);
|
||||
String language = getLanguageString(task, project);
|
||||
if (editor != null && language != null) {
|
||||
final CloseableHttpClient client = getHttpClient(project);
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
StepicWrappers.ResultSubmissionWrapper wrapper = postResultsForCheck(client, attemptId, language, editor.getDocument().getText());
|
||||
|
||||
final StepicUser user = StudyTaskManager.getInstance(project).getUser();
|
||||
@@ -429,7 +419,6 @@ public class EduAdaptiveStepicConnector {
|
||||
final StepicWrappers.SubmissionToPostWrapper submissionToPostWrapper =
|
||||
new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId), language, PYCHARM_COMMENT + text);
|
||||
final HttpPost httpPost = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.SUBMISSIONS);
|
||||
setHeaders(httpPost, EduStepicNames.CONTENT_TYPE_APPL_JSON);
|
||||
setTimeout(httpPost);
|
||||
try {
|
||||
httpPost.setEntity(new StringEntity(new Gson().toJson(submissionToPostWrapper)));
|
||||
@@ -463,7 +452,6 @@ public class EduAdaptiveStepicConnector {
|
||||
.addParameter("user", String.valueOf(id))
|
||||
.build();
|
||||
final HttpGet httpGet = new HttpGet(submissionURI);
|
||||
setHeaders(httpGet, EduStepicNames.CONTENT_TYPE_APPL_JSON);
|
||||
setTimeout(httpGet);
|
||||
final CloseableHttpResponse httpResponse = client.execute(httpGet);
|
||||
final HttpEntity entity = httpResponse.getEntity();
|
||||
@@ -506,13 +494,12 @@ public class EduAdaptiveStepicConnector {
|
||||
}
|
||||
|
||||
private static int getAttemptId(@NotNull final Project project, @NotNull Task task) throws IOException {
|
||||
final StepicWrappers.AttemptToPostWrapper attemptWrapper = new StepicWrappers.AttemptToPostWrapper(task.getStepicId());
|
||||
final StepicWrappers.AttemptToPostWrapper attemptWrapper = new StepicWrappers.AttemptToPostWrapper(task.getStepId());
|
||||
|
||||
final HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS);
|
||||
post.setEntity(new StringEntity(new Gson().toJson(attemptWrapper)));
|
||||
|
||||
final CloseableHttpClient client = getHttpClient(project);
|
||||
setHeaders(post, EduStepicNames.CONTENT_TYPE_APPL_JSON);
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
setTimeout(post);
|
||||
final CloseableHttpResponse httpResponse = client.execute(post);
|
||||
final int statusCode = httpResponse.getStatusLine().getStatusCode();
|
||||
@@ -524,11 +511,6 @@ public class EduAdaptiveStepicConnector {
|
||||
new Gson().fromJson(entityString, StepicWrappers.AttemptContainer.class);
|
||||
return (container.attempts != null && !container.attempts.isEmpty()) ? container.attempts.get(0).id : -1;
|
||||
}
|
||||
else {
|
||||
if ((statusCode == HttpStatus.SC_BAD_REQUEST || statusCode == HttpStatus.SC_UNAUTHORIZED) && login(project)) {
|
||||
return getAttemptId(project, task);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -540,4 +522,27 @@ public class EduAdaptiveStepicConnector {
|
||||
" check_samples(samples=" + new GsonBuilder().create().toJson(samples) + ")";
|
||||
task.addTestsTexts("tests.py", testText);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Integer> getEnrolledCoursesIds(StepicUser stepicUser) {
|
||||
try {
|
||||
final URI enrolledCoursesUri = new URIBuilder(EduStepicNames.COURSES).addParameter("enrolled", "true").build();
|
||||
final List<CourseInfo> courses = EduStepicAuthorizedClient.getFromStepic(enrolledCoursesUri.toString(),
|
||||
StepicWrappers.CoursesContainer.class,
|
||||
stepicUser).courses;
|
||||
final ArrayList<Integer> ids = new ArrayList<>();
|
||||
for (CourseInfo course : courses) {
|
||||
ids.add(course.getId());
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn(e.getMessage());
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
LOG.warn(e.getMessage());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
package com.jetbrains.edu.learning.stepic;
|
||||
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.net.HttpConfigurable;
|
||||
import com.intellij.util.net.ssl.CertificateManager;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import org.apache.http.*;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.DefaultConnectionReuseStrategy;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URI;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class EduStepicAuthorizedClient {
|
||||
private static final Logger LOG = Logger.getInstance(EduStepicAuthorizedClient.class.getName());
|
||||
private static final String ourClientId = "hUCWcq3hZHCmz0DKrDtwOWITLcYutzot7p4n59vU";
|
||||
private static CloseableHttpClient ourClient;
|
||||
|
||||
private EduStepicAuthorizedClient() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CloseableHttpClient getHttpClient(@NotNull final Project project) {
|
||||
if (ourClient != null) {
|
||||
return ourClient;
|
||||
}
|
||||
final StepicUser stepicUser = StudyTaskManager.getInstance(project).getUser();
|
||||
ourClient = initializeClient(stepicUser);
|
||||
if (ourClient == null) {
|
||||
final StepicUser user = login(stepicUser);
|
||||
if (user != null) {
|
||||
StudyTaskManager.getInstance(project).setUser(user);
|
||||
ourClient = initializeClient(stepicUser);
|
||||
}
|
||||
}
|
||||
return ourClient;
|
||||
}
|
||||
|
||||
public static <T> T getFromStepic(String link, final Class<T> container, @NotNull final Project project) throws IOException {
|
||||
return EduStepicClient.getFromStepic(link, container, getHttpClient(project));
|
||||
}
|
||||
|
||||
/*
|
||||
* This method should be used only in project generation while project is not available.
|
||||
* Make sure you saved stepic user in task manager after using this method.
|
||||
*/
|
||||
@NotNull
|
||||
public static CloseableHttpClient getHttpClient(@NotNull final StepicUser stepicUser) {
|
||||
if (ourClient != null) {
|
||||
return ourClient;
|
||||
}
|
||||
ourClient = initializeClient(stepicUser);
|
||||
if (ourClient == null) {
|
||||
final StepicUser user = login(stepicUser);
|
||||
if (user != null) {
|
||||
ourClient = initializeClient(stepicUser);
|
||||
}
|
||||
}
|
||||
return ourClient;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method should be used only in project generation while project is not available.
|
||||
* Make sure you saved stepic user in task manager after using this method.
|
||||
*/
|
||||
public static <T> T getFromStepic(String link, final Class<T> container, @NotNull final StepicUser stepicUser) throws IOException {
|
||||
return EduStepicClient.getFromStepic(link, container, getHttpClient(stepicUser));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static CloseableHttpClient initializeClient(@NotNull final StepicUser stepicUser) {
|
||||
final List<BasicHeader> headers = new ArrayList<>();
|
||||
final String accessToken = stepicUser.getAccessToken();
|
||||
if (accessToken != null && !accessToken.isEmpty()) {
|
||||
headers.add(new BasicHeader("Authorization", "Bearer " + accessToken));
|
||||
headers.add(new BasicHeader("Content-type", EduStepicNames.CONTENT_TYPE_APP_JSON));
|
||||
return getBuilder().setDefaultHeaders(headers).build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static TrustManager[] trustAllCerts() {
|
||||
// Create a trust manager that does not validate certificate for this connection
|
||||
return new TrustManager[]{new X509TrustManager() {
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] certs, String authType) {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] certs, String authType) {
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static HttpClientBuilder getBuilder() {
|
||||
final HttpClientBuilder builder = HttpClients.custom().setSslcontext(CertificateManager.getInstance().getSslContext()).
|
||||
setMaxConnPerRoute(100000).setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE);
|
||||
|
||||
final HttpConfigurable proxyConfigurable = HttpConfigurable.getInstance();
|
||||
final List<Proxy> proxies = proxyConfigurable.getOnlyBySettingsSelector().select(URI.create(EduStepicNames.STEPIC_URL));
|
||||
final InetSocketAddress address = proxies.size() > 0 ? (InetSocketAddress)proxies.get(0).address() : null;
|
||||
if (address != null) {
|
||||
builder.setProxy(new HttpHost(address.getHostName(), address.getPort()));
|
||||
}
|
||||
final TrustManager[] trustAllCerts = trustAllCerts();
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, trustAllCerts, new SecureRandom());
|
||||
builder.setSslcontext(sslContext);
|
||||
}
|
||||
catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static StepicUser login(@NotNull final StepicUser user) {
|
||||
final String login = user.getEmail();
|
||||
final String refreshToken = user.getRefreshToken();
|
||||
if (StringUtil.isEmptyOrSpaces(login)) {
|
||||
return showLoginDialog();
|
||||
}
|
||||
else {
|
||||
if (StringUtil.isNotEmpty(refreshToken)) {
|
||||
final StepicWrappers.TokenInfo tokenInfo = login(refreshToken);
|
||||
if (tokenInfo != null) {
|
||||
user.setupTokenInfo(tokenInfo);
|
||||
}
|
||||
}
|
||||
else {
|
||||
final StepicUser stepicUser = login(login, user.getPassword());
|
||||
if (stepicUser == null) {
|
||||
return showLoginDialog();
|
||||
}
|
||||
return stepicUser;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static StepicUser showLoginDialog() {
|
||||
final StepicUser[] stepicUser = new StepicUser[1];
|
||||
ApplicationManager.getApplication().invokeAndWait(() -> {
|
||||
final LoginDialog dialog = new LoginDialog();
|
||||
dialog.show();
|
||||
stepicUser[0] = dialog.getStepicUser();
|
||||
});
|
||||
return stepicUser[0];
|
||||
}
|
||||
|
||||
public static StepicUser login(@NotNull final String email, @NotNull final String password) {
|
||||
final List<NameValuePair> parameters = new ArrayList<>();
|
||||
if (password.isEmpty()) return null;
|
||||
parameters.add(new BasicNameValuePair("client_id", ourClientId));
|
||||
parameters.add(new BasicNameValuePair("grant_type", "password"));
|
||||
parameters.add(new BasicNameValuePair("username", email));
|
||||
parameters.add(new BasicNameValuePair("password", password));
|
||||
|
||||
final StepicWrappers.TokenInfo tokenInfo = postCredentials(parameters);
|
||||
|
||||
final StepicUser user = new StepicUser(email, password);
|
||||
final StepicUser currentUser = getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
user.setId(currentUser.getId());
|
||||
}
|
||||
if (tokenInfo != null) {
|
||||
user.setupTokenInfo(tokenInfo);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private static StepicWrappers.TokenInfo login(@NotNull final String refreshToken) {
|
||||
final List<NameValuePair> parameters = new ArrayList<>();
|
||||
|
||||
if (refreshToken.isEmpty()) return null;
|
||||
parameters.add(new BasicNameValuePair("client_id", ourClientId));
|
||||
parameters.add(new BasicNameValuePair("content-type", "application/json"));
|
||||
parameters.add(new BasicNameValuePair("grant_type", "refresh_token"));
|
||||
parameters.add(new BasicNameValuePair("refresh_token", refreshToken));
|
||||
|
||||
return postCredentials(parameters);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static StepicUser getCurrentUser() {
|
||||
try {
|
||||
final StepicWrappers.AuthorWrapper wrapper = EduStepicClient.getFromStepic(EduStepicNames.CURRENT_USER,
|
||||
StepicWrappers.AuthorWrapper.class);
|
||||
if (wrapper != null && !wrapper.users.isEmpty()) {
|
||||
return wrapper.users.get(0);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn("Couldn't get author info");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static StepicWrappers.TokenInfo postCredentials(@NotNull final List<NameValuePair> parameters) {
|
||||
final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
||||
|
||||
final HttpPost request = new HttpPost(EduStepicNames.TOKEN_URL);
|
||||
request.setEntity(new UrlEncodedFormEntity(parameters, Consts.UTF_8));
|
||||
|
||||
try {
|
||||
final CloseableHttpClient client = EduStepicClient.getHttpClient();
|
||||
final CloseableHttpResponse response = client.execute(request);
|
||||
final StatusLine statusLine = response.getStatusLine();
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
|
||||
return gson.fromJson(responseString, StepicWrappers.TokenInfo.class);
|
||||
}
|
||||
else {
|
||||
LOG.warn("Failed to Login: " + statusLine.getStatusCode() + statusLine.getReasonPhrase());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package com.jetbrains.edu.learning.stepic;
|
||||
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.util.net.HttpConfigurable;
|
||||
import com.intellij.util.net.ssl.CertificateManager;
|
||||
import com.jetbrains.edu.learning.StudySerializationUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.DefaultConnectionReuseStrategy;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URI;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
|
||||
public class EduStepicClient {
|
||||
private static final Logger LOG = Logger.getInstance(EduStepicClient.class.getName());
|
||||
private static CloseableHttpClient ourClient;
|
||||
|
||||
private EduStepicClient() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CloseableHttpClient getHttpClient() {
|
||||
if (ourClient == null) {
|
||||
initializeClient();
|
||||
}
|
||||
return ourClient;
|
||||
}
|
||||
|
||||
public static <T> T getFromStepic(String link, final Class<T> container) throws IOException {
|
||||
return getFromStepic(link, container, getHttpClient());
|
||||
}
|
||||
|
||||
static <T> T getFromStepic(String link, final Class<T> container, @NotNull final CloseableHttpClient client) throws IOException {
|
||||
if (!link.startsWith("/")) link = "/" + link;
|
||||
final HttpGet request = new HttpGet(EduStepicNames.STEPIC_API_URL + link);
|
||||
if (ourClient == null) {
|
||||
initializeClient();
|
||||
}
|
||||
|
||||
final CloseableHttpResponse response = client.execute(request);
|
||||
final StatusLine statusLine = response.getStatusLine();
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
|
||||
throw new IOException("Stepic returned non 200 status code " + responseString);
|
||||
}
|
||||
Gson gson = new GsonBuilder().registerTypeAdapter(TaskFile.class, new StudySerializationUtils.Json.StepicTaskFileAdapter()).
|
||||
registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).
|
||||
setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").
|
||||
setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
||||
return gson.fromJson(responseString, container);
|
||||
}
|
||||
|
||||
private static void initializeClient() {
|
||||
if (ourClient == null) {
|
||||
final HttpClientBuilder builder = getBuilder();
|
||||
ourClient = builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static TrustManager[] trustAllCerts() {
|
||||
// Create a trust manager that does not validate certificate for this connection
|
||||
return new TrustManager[]{new X509TrustManager() {
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] certs, String authType) {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] certs, String authType) {
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static HttpClientBuilder getBuilder() {
|
||||
final HttpClientBuilder builder = HttpClients.custom().setSslcontext(CertificateManager.getInstance().getSslContext()).
|
||||
setMaxConnPerRoute(100000).setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE);
|
||||
|
||||
final HttpConfigurable proxyConfigurable = HttpConfigurable.getInstance();
|
||||
final List<Proxy> proxies = proxyConfigurable.getOnlyBySettingsSelector().select(URI.create(EduStepicNames.STEPIC_URL));
|
||||
final InetSocketAddress address = proxies.size() > 0 ? (InetSocketAddress)proxies.get(0).address() : null;
|
||||
if (address != null) {
|
||||
builder.setProxy(new HttpHost(address.getHostName(), address.getPort()));
|
||||
}
|
||||
final TrustManager[] trustAllCerts = trustAllCerts();
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, trustAllCerts, new SecureRandom());
|
||||
builder.setSslcontext(sslContext);
|
||||
}
|
||||
catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
+32
-682
@@ -1,280 +1,49 @@
|
||||
package com.jetbrains.edu.learning.stepic;
|
||||
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileFilter;
|
||||
import com.intellij.util.net.HttpConfigurable;
|
||||
import com.intellij.util.net.ssl.CertificateManager;
|
||||
import com.jetbrains.edu.learning.StudySerializationUtils;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.core.EduUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.*;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.http.*;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.*;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
import com.jetbrains.edu.learning.courseFormat.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.cookie.Cookie;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.DefaultConnectionReuseStrategy;
|
||||
import org.apache.http.impl.client.BasicCookieStore;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.*;
|
||||
|
||||
import static com.jetbrains.edu.learning.stepic.EduStepicNames.CONTENT_TYPE_APPL_JSON;
|
||||
|
||||
public class EduStepicConnector {
|
||||
private static final Logger LOG = Logger.getInstance(EduStepicConnector.class.getName());
|
||||
private static String ourCSRFToken = "";
|
||||
private static CloseableHttpClient ourClient;
|
||||
|
||||
//this prefix indicates that course can be opened by educational plugin
|
||||
public static final String PYCHARM_PREFIX = "pycharm";
|
||||
private static BasicCookieStore ourCookieStore;
|
||||
|
||||
private static final String ADAPTIVE_NOTE =
|
||||
"\n\nInitially, the adaptive system may behave somewhat randomly, but the more problems you solve, the smarter it become!";
|
||||
|
||||
private EduStepicConnector() {
|
||||
}
|
||||
|
||||
public static StepicUser login(@NotNull final String username, @NotNull final String password) {
|
||||
initializeClient();
|
||||
if (postCredentials(username, password)) {
|
||||
final StepicWrappers.AuthorWrapper stepicUserWrapper = getCurrentUser();
|
||||
if (stepicUserWrapper != null && stepicUserWrapper.users.size() == 1) {
|
||||
return stepicUserWrapper.users.get(0);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Integer> getEnrolledCoursesIds() {
|
||||
try {
|
||||
final URI enrolledCoursesUri = new URIBuilder(EduStepicNames.COURSES).addParameter("enrolled", "true").build();
|
||||
final List<CourseInfo> courses = getFromStepic(enrolledCoursesUri.toString(), StepicWrappers.CoursesContainer.class).courses;
|
||||
final ArrayList<Integer> ids = new ArrayList<>();
|
||||
for (CourseInfo course : courses) {
|
||||
ids.add(course.getId());
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn(e.getMessage());
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
LOG.warn(e.getMessage());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static StepicWrappers.AuthorWrapper getCurrentUser() {
|
||||
try {
|
||||
return getFromStepic(EduStepicNames.CURRENT_USER, StepicWrappers.AuthorWrapper.class);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn("Couldn't get author info");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean createUser(@NotNull final String user, @NotNull final String password) {
|
||||
final HttpPost userRequest = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.USERS);
|
||||
initializeClient();
|
||||
setHeaders(userRequest, CONTENT_TYPE_APPL_JSON);
|
||||
String requestBody = new Gson().toJson(new StepicWrappers.UserWrapper(user, password));
|
||||
userRequest.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpResponse response = ourClient.execute(userRequest);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine statusLine = response.getStatusLine();
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (statusLine.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
LOG.error("Failed to create user " + responseString);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void initializeClient() {
|
||||
if (ourClient == null) {
|
||||
final HttpGet request = new HttpGet(EduStepicNames.STEPIC_URL);
|
||||
setHeaders(request, CONTENT_TYPE_APPL_JSON);
|
||||
|
||||
HttpClientBuilder builder =
|
||||
HttpClients.custom().setSslcontext(CertificateManager.getInstance().getSslContext()).setMaxConnPerRoute(100000).
|
||||
setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE);
|
||||
|
||||
final HttpConfigurable proxyConfigurable = HttpConfigurable.getInstance();
|
||||
final List<Proxy> proxies = proxyConfigurable.getOnlyBySettingsSelector().select(URI.create(EduStepicNames.STEPIC_URL));
|
||||
final InetSocketAddress address = proxies.size() > 0 ? (InetSocketAddress)proxies.get(0).address() : null;
|
||||
if (address != null) {
|
||||
builder.setProxy(new HttpHost(address.getHostName(), address.getPort()));
|
||||
}
|
||||
ourCookieStore = new BasicCookieStore();
|
||||
|
||||
try {
|
||||
// Create a trust manager that does not validate certificate for this connection
|
||||
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] certs, String authType) {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] certs, String authType) {
|
||||
}
|
||||
}};
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, trustAllCerts, new SecureRandom());
|
||||
ourClient = builder.setDefaultCookieStore(ourCookieStore).setSslcontext(sslContext).build();
|
||||
|
||||
ourClient.execute(request);
|
||||
saveCSRFToken();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
catch (KeyManagementException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void saveCSRFToken() {
|
||||
if (ourCookieStore == null) return;
|
||||
final List<Cookie> cookies = ourCookieStore.getCookies();
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie.getName().equals("csrftoken")) {
|
||||
ourCSRFToken = cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean postCredentials(String user, String password) {
|
||||
String url = EduStepicNames.STEPIC_URL + EduStepicNames.LOGIN;
|
||||
final HttpPost request = new HttpPost(url);
|
||||
List <NameValuePair> nvps = new ArrayList<>();
|
||||
nvps.add(new BasicNameValuePair("csrfmiddlewaretoken", ourCSRFToken));
|
||||
nvps.add(new BasicNameValuePair("login", user));
|
||||
nvps.add(new BasicNameValuePair("next", "/"));
|
||||
nvps.add(new BasicNameValuePair("password", password));
|
||||
nvps.add(new BasicNameValuePair("remember", "on"));
|
||||
|
||||
request.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8));
|
||||
|
||||
setHeaders(request, "application/x-www-form-urlencoded");
|
||||
|
||||
try {
|
||||
final CloseableHttpResponse response = ourClient.execute(request);
|
||||
saveCSRFToken();
|
||||
final StatusLine line = response.getStatusLine();
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() != HttpStatus.SC_MOVED_TEMPORARILY) {
|
||||
LOG.warn("Failed to login: " + line.getStatusCode() + line.getReasonPhrase());
|
||||
LOG.debug("Failed to login " + responseString);
|
||||
|
||||
ourClient = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn(e.getMessage());
|
||||
ourClient = null;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static <T> T getFromStepic(String link, final Class<T> container) throws IOException {
|
||||
if (!link.startsWith("/")) link = "/" + link;
|
||||
final HttpGet request = new HttpGet(EduStepicNames.STEPIC_API_URL + link);
|
||||
if (ourClient == null) {
|
||||
initializeClient();
|
||||
}
|
||||
setHeaders(request, CONTENT_TYPE_APPL_JSON);
|
||||
|
||||
final CloseableHttpResponse response = ourClient.execute(request);
|
||||
final StatusLine statusLine = response.getStatusLine();
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
|
||||
throw new IOException("Stepic returned non 200 status code " + responseString);
|
||||
}
|
||||
Gson gson = new GsonBuilder().registerTypeAdapter(TaskFile.class, new StudySerializationUtils.Json.StepicTaskFileAdapter()).
|
||||
registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).
|
||||
setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").
|
||||
setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
||||
return gson.fromJson(responseString, container);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CloseableHttpClient getHttpClient(@NotNull final Project project) {
|
||||
if (ourClient == null) {
|
||||
login(project);
|
||||
}
|
||||
return ourClient;
|
||||
}
|
||||
|
||||
public static boolean enrollToCourse(final int courseId) {
|
||||
public static boolean enrollToCourse(final int courseId, final StepicUser stepicUser) {
|
||||
HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ENROLLMENTS);
|
||||
try {
|
||||
final StepicWrappers.EnrollmentWrapper enrollment = new StepicWrappers.EnrollmentWrapper(String.valueOf(courseId));
|
||||
post.setEntity(new StringEntity(new GsonBuilder().create().toJson(enrollment)));
|
||||
setHeaders(post, CONTENT_TYPE_APPL_JSON);
|
||||
if (ourClient == null) {
|
||||
initializeClient();
|
||||
}
|
||||
CloseableHttpResponse response = ourClient.execute(post);
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(stepicUser);
|
||||
CloseableHttpResponse response = client.execute(post);
|
||||
StatusLine line = response.getStatusLine();
|
||||
return line.getStatusCode() == HttpStatus.SC_CREATED;
|
||||
}
|
||||
@@ -300,23 +69,10 @@ public class EduStepicConnector {
|
||||
return Collections.singletonList(CourseInfo.INVALID_COURSE);
|
||||
}
|
||||
|
||||
public static CourseInfo getCourseInfo(Project project, String courseId) {
|
||||
final String url = EduStepicNames.COURSES + "/" + courseId;
|
||||
try {
|
||||
login(project);
|
||||
final StepicWrappers.CoursesContainer coursesContainer = getFromStepic(url, StepicWrappers.CoursesContainer.class);
|
||||
return coursesContainer.courses.get(0);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Date getCourseUpdateDate(final int courseId) {
|
||||
final String url = EduStepicNames.COURSES + "/" + courseId;
|
||||
try {
|
||||
final List<CourseInfo> courses = getFromStepic(url, StepicWrappers.CoursesContainer.class).courses;
|
||||
final List<CourseInfo> courses = EduStepicClient.getFromStepic(url, StepicWrappers.CoursesContainer.class).courses;
|
||||
if (!courses.isEmpty()) {
|
||||
return courses.get(0).getUpdateDate();
|
||||
}
|
||||
@@ -331,7 +87,7 @@ public class EduStepicConnector {
|
||||
public static Date getLessonUpdateDate(final int lessonId) {
|
||||
final String url = EduStepicNames.LESSONS + "/" + lessonId;
|
||||
try {
|
||||
List<Lesson> lessons = getFromStepic(url, StepicWrappers.LessonContainer.class).lessons;
|
||||
List<Lesson> lessons = EduStepicClient.getFromStepic(url, StepicWrappers.LessonContainer.class).lessons;
|
||||
if (!lessons.isEmpty()) {
|
||||
return lessons.get(0).getUpdateDate();
|
||||
}
|
||||
@@ -346,7 +102,7 @@ public class EduStepicConnector {
|
||||
public static Date getTaskUpdateDate(final int taskId) {
|
||||
final String url = EduStepicNames.STEPS + "/" + String.valueOf(taskId);
|
||||
try {
|
||||
List<StepicWrappers.StepSource> steps = getFromStepic(url, StepicWrappers.StepContainer.class).steps;
|
||||
List<StepicWrappers.StepSource> steps = EduStepicClient.getFromStepic(url, StepicWrappers.StepContainer.class).steps;
|
||||
if (!steps.isEmpty()) {
|
||||
return steps.get(0).update_date;
|
||||
}
|
||||
@@ -368,7 +124,7 @@ public class EduStepicConnector {
|
||||
LOG.error(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
final StepicWrappers.CoursesContainer coursesContainer = getFromStepic(url.toString(), StepicWrappers.CoursesContainer.class);
|
||||
final StepicWrappers.CoursesContainer coursesContainer = EduStepicClient.getFromStepic(url.toString(), StepicWrappers.CoursesContainer.class);
|
||||
final List<CourseInfo> courseInfos = coursesContainer.courses;
|
||||
for (CourseInfo info : courseInfos) {
|
||||
final String courseType = info.getType();
|
||||
@@ -376,7 +132,7 @@ public class EduStepicConnector {
|
||||
final List<String> typeLanguage = StringUtil.split(courseType, " ");
|
||||
if (info.isAdaptive() || (typeLanguage.size() == 2 && PYCHARM_PREFIX.equals(typeLanguage.get(0)))) {
|
||||
for (Integer instructor : info.instructors) {
|
||||
final StepicUser author = getFromStepic(EduStepicNames.USERS + "/" + String.valueOf(instructor),
|
||||
final StepicUser author = EduStepicClient.getFromStepic(EduStepicNames.USERS + "/" + String.valueOf(instructor),
|
||||
StepicWrappers.AuthorWrapper.class).users.get(0);
|
||||
info.addAuthor(author);
|
||||
}
|
||||
@@ -433,15 +189,15 @@ public class EduStepicConnector {
|
||||
|
||||
public static List<Lesson> getLessons(int sectionId) throws IOException {
|
||||
final StepicWrappers.SectionContainer
|
||||
sectionContainer = getFromStepic(EduStepicNames.SECTIONS + String.valueOf(sectionId), StepicWrappers.SectionContainer.class);
|
||||
sectionContainer = EduStepicClient.getFromStepic(EduStepicNames.SECTIONS + String.valueOf(sectionId), StepicWrappers.SectionContainer.class);
|
||||
List<Integer> unitIds = sectionContainer.sections.get(0).units;
|
||||
final List<Lesson> lessons = new ArrayList<>();
|
||||
for (Integer unitId : unitIds) {
|
||||
StepicWrappers.UnitContainer
|
||||
unit = getFromStepic(EduStepicNames.UNITS + "/" + String.valueOf(unitId), StepicWrappers.UnitContainer.class);
|
||||
unit = EduStepicClient.getFromStepic(EduStepicNames.UNITS + "/" + String.valueOf(unitId), StepicWrappers.UnitContainer.class);
|
||||
int lessonID = unit.units.get(0).lesson;
|
||||
StepicWrappers.LessonContainer
|
||||
lessonContainer = getFromStepic(EduStepicNames.LESSONS + String.valueOf(lessonID), StepicWrappers.LessonContainer.class);
|
||||
lessonContainer = EduStepicClient.getFromStepic(EduStepicNames.LESSONS + String.valueOf(lessonID), StepicWrappers.LessonContainer.class);
|
||||
Lesson lesson = lessonContainer.lessons.get(0);
|
||||
lesson.taskList = new ArrayList<>();
|
||||
for (Integer s : lesson.steps) {
|
||||
@@ -459,7 +215,7 @@ public class EduStepicConnector {
|
||||
final StepicWrappers.Step block = step.block;
|
||||
if (!block.name.equals(PYCHARM_PREFIX)) return;
|
||||
final Task task = new Task();
|
||||
task.setStepicId(stepicId);
|
||||
task.setStepId(stepicId);
|
||||
task.setUpdateDate(step.update_date);
|
||||
task.setName(block.options != null ? block.options.title : PYCHARM_PREFIX);
|
||||
task.setText(block.text);
|
||||
@@ -477,53 +233,25 @@ public class EduStepicConnector {
|
||||
}
|
||||
|
||||
public static StepicWrappers.StepSource getStep(Integer step) throws IOException {
|
||||
return getFromStepic(EduStepicNames.STEPS + "/" + String.valueOf(step), StepicWrappers.StepContainer.class).steps.get(0);
|
||||
return EduStepicClient.getFromStepic(EduStepicNames.STEPS + "/" + String.valueOf(step), StepicWrappers.StepContainer.class).steps.get(0);
|
||||
}
|
||||
|
||||
|
||||
public static boolean showLoginDialog() {
|
||||
final boolean[] logged = {false};
|
||||
ApplicationManager.getApplication().invokeAndWait(() -> {
|
||||
final LoginDialog dialog = new LoginDialog();
|
||||
dialog.show();
|
||||
logged[0] = dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE;
|
||||
});
|
||||
return logged[0];
|
||||
}
|
||||
|
||||
public static void postAttempt(@NotNull final Task task, boolean passed, @Nullable String login, @Nullable String password) {
|
||||
if (task.getStepicId() <= 0) {
|
||||
public static void postAttempt(@NotNull final Task task, boolean passed, @NotNull final Project project) {
|
||||
if (task.getStepId() <= 0) {
|
||||
return;
|
||||
}
|
||||
if (ourClient == null) {
|
||||
if (StringUtil.isEmptyOrSpaces(login) || StringUtil.isEmptyOrSpaces(password)) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (login(login, password) == null) return;
|
||||
}
|
||||
}
|
||||
|
||||
final HttpPost attemptRequest = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS);
|
||||
setHeaders(attemptRequest, CONTENT_TYPE_APPL_JSON);
|
||||
String attemptRequestBody = new Gson().toJson(new StepicWrappers.AttemptWrapper(task.getStepicId()));
|
||||
String attemptRequestBody = new Gson().toJson(new StepicWrappers.AttemptWrapper(task.getStepId()));
|
||||
attemptRequest.setEntity(new StringEntity(attemptRequestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpResponse attemptResponse = ourClient.execute(attemptRequest);
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
final CloseableHttpResponse attemptResponse = client.execute(attemptRequest);
|
||||
final HttpEntity responseEntity = attemptResponse.getEntity();
|
||||
final String attemptResponseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine statusLine = attemptResponse.getStatusLine();
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (statusLine.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
|
||||
if (StringUtil.isEmptyOrSpaces(login) || StringUtil.isEmptyOrSpaces(password)) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
login(login, password);
|
||||
postAttempt(task, passed, login, password);
|
||||
}
|
||||
}
|
||||
if (statusLine.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
LOG.warn("Failed to make attempt " + attemptResponseString);
|
||||
}
|
||||
@@ -534,20 +262,21 @@ public class EduStepicConnector {
|
||||
for (TaskFile fileEntry : taskFiles.values()) {
|
||||
files.add(new StepicWrappers.SolutionFile(fileEntry.name, fileEntry.text));
|
||||
}
|
||||
postSubmission(passed, attempt, files);
|
||||
postSubmission(passed, attempt, project, files);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void postSubmission(boolean passed, StepicWrappers.AttemptWrapper.Attempt attempt, ArrayList<StepicWrappers.SolutionFile> files) throws IOException {
|
||||
private static void postSubmission(boolean passed, StepicWrappers.AttemptWrapper.Attempt attempt,
|
||||
Project project, ArrayList<StepicWrappers.SolutionFile> files) throws IOException {
|
||||
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.SUBMISSIONS);
|
||||
setHeaders(request, CONTENT_TYPE_APPL_JSON);
|
||||
|
||||
String requestBody = new Gson().toJson(new StepicWrappers.SubmissionWrapper(attempt.id, passed ? "1" : "0", files));
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
final CloseableHttpResponse response = ourClient.execute(request);
|
||||
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
|
||||
final CloseableHttpResponse response = client.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine line = response.getStatusLine();
|
||||
@@ -556,383 +285,4 @@ public class EduStepicConnector {
|
||||
LOG.error("Failed to make submission " + responseString);
|
||||
}
|
||||
}
|
||||
|
||||
public static void postCourseWithProgress(final Project project, @NotNull final Course course) {
|
||||
postCourseWithProgress(project, course, false);
|
||||
}
|
||||
|
||||
public static void postCourseWithProgress(final Project project, @NotNull final Course course, final boolean relogin) {
|
||||
ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Modal(project, "Uploading Course", true) {
|
||||
@Override
|
||||
public void run(@NotNull final ProgressIndicator indicator) {
|
||||
postCourse(project, course, relogin, indicator);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void postCourse(final Project project, @NotNull Course course, boolean relogin, @NotNull final ProgressIndicator indicator) {
|
||||
indicator.setText("Uploading course to " + EduStepicNames.STEPIC_URL);
|
||||
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/courses");
|
||||
if (ourClient == null || !relogin) {
|
||||
if (!login(project)) return;
|
||||
}
|
||||
final StepicWrappers.AuthorWrapper authors = getCurrentUser();
|
||||
if (authors != null) {
|
||||
final List<StepicUser> courseAuthors = course.getAuthors();
|
||||
for (int i = 0; i < courseAuthors.size(); i++) {
|
||||
final StepicUser user = authors.users.get(i);
|
||||
if (courseAuthors.size() > i) {
|
||||
final StepicUser courseAuthor = courseAuthors.get(i);
|
||||
user.setFirstName(courseAuthor.getFirstName());
|
||||
user.setLastName(courseAuthor.getLastName());
|
||||
}
|
||||
}
|
||||
|
||||
course.setAuthors(authors.users);
|
||||
}
|
||||
|
||||
setHeaders(request, CONTENT_TYPE_APPL_JSON);
|
||||
String requestBody = new Gson().toJson(new StepicWrappers.CourseWrapper(course));
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpResponse response = ourClient.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine line = response.getStatusLine();
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
if (!relogin) {
|
||||
login(project);
|
||||
postCourse(project, course, true, indicator);
|
||||
}
|
||||
LOG.error("Failed to push " + responseString);
|
||||
return;
|
||||
}
|
||||
final CourseInfo postedCourse = new Gson().fromJson(responseString, StepicWrappers.CoursesContainer.class).courses.get(0);
|
||||
course.setId(postedCourse.id);
|
||||
final int sectionId = postModule(postedCourse.id, 1, String.valueOf(postedCourse.getName()));
|
||||
int position = 1;
|
||||
for (Lesson lesson : course.getLessons()) {
|
||||
indicator.checkCanceled();
|
||||
final int lessonId = postLesson(project, lesson, indicator);
|
||||
postUnit(lessonId, position, sectionId);
|
||||
position += 1;
|
||||
}
|
||||
ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(project, postedCourse.id, indicator));
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
static boolean login(@NotNull final Project project) {
|
||||
final StepicUser user = StudyTaskManager.getInstance(project).getUser();
|
||||
final String login = user.getEmail();
|
||||
if (StringUtil.isEmptyOrSpaces(login)) {
|
||||
return showLoginDialog();
|
||||
}
|
||||
else {
|
||||
if (login(login, user.getPassword()) == null) {
|
||||
return showLoginDialog();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void postAdditionalFiles(@NotNull final Project project, int id, ProgressIndicator indicator) {
|
||||
final VirtualFile baseDir = project.getBaseDir();
|
||||
final List<VirtualFile> files = VfsUtil.getChildren(baseDir, new VirtualFileFilter() {
|
||||
@Override
|
||||
public boolean accept(VirtualFile file) {
|
||||
final String name = file.getName();
|
||||
return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS) &&
|
||||
!"pyc".equals(file.getExtension()) && !file.isDirectory() && !name.equals(EduNames.TEST_HELPER) && !name.startsWith("");
|
||||
}
|
||||
});
|
||||
|
||||
if (!files.isEmpty()) {
|
||||
final int sectionId = postModule(id, 2, EduNames.PYCHARM_ADDITIONAL);
|
||||
final Lesson lesson = new Lesson();
|
||||
lesson.setName(EduNames.PYCHARM_ADDITIONAL);
|
||||
final Task task = new Task();
|
||||
task.setLesson(lesson);
|
||||
task.setName(EduNames.PYCHARM_ADDITIONAL);
|
||||
task.setIndex(1);
|
||||
task.setText(EduNames.PYCHARM_ADDITIONAL);
|
||||
for (VirtualFile file : files) {
|
||||
try {
|
||||
if (file != null) {
|
||||
if (EduUtils.isImage(file.getName())) {
|
||||
task.addTestsTexts(file.getName(), Base64.encodeBase64URLSafeString(FileUtil.loadBytes(file.getInputStream())));
|
||||
}
|
||||
else {
|
||||
task.addTestsTexts(file.getName(), FileUtil.loadTextAndClose(file.getInputStream()));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error("Can't find file " + file.getPath());
|
||||
}
|
||||
}
|
||||
lesson.addTask(task);
|
||||
lesson.setIndex(1);
|
||||
final int lessonId = postLesson(project, lesson, indicator);
|
||||
postUnit(lessonId, 1, sectionId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void postUnit(int lessonId, int position, int sectionId) {
|
||||
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.UNITS);
|
||||
setHeaders(request, CONTENT_TYPE_APPL_JSON);
|
||||
final StepicWrappers.UnitWrapper unitWrapper = new StepicWrappers.UnitWrapper();
|
||||
unitWrapper.unit = new StepicWrappers.Unit();
|
||||
unitWrapper.unit.lesson = lessonId;
|
||||
unitWrapper.unit.position = position;
|
||||
unitWrapper.unit.section = sectionId;
|
||||
|
||||
String requestBody = new Gson().toJson(unitWrapper);
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpResponse response = ourClient.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine line = response.getStatusLine();
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static int postModule(int courseId, int position, @NotNull final String title) {
|
||||
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/sections");
|
||||
setHeaders(request, CONTENT_TYPE_APPL_JSON);
|
||||
final StepicWrappers.Section section = new StepicWrappers.Section();
|
||||
section.course = courseId;
|
||||
section.title = title;
|
||||
section.position = position;
|
||||
final StepicWrappers.SectionWrapper sectionContainer = new StepicWrappers.SectionWrapper();
|
||||
sectionContainer.section = section;
|
||||
String requestBody = new Gson().toJson(sectionContainer);
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpResponse response = ourClient.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine line = response.getStatusLine();
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
return -1;
|
||||
}
|
||||
final StepicWrappers.Section
|
||||
postedSection = new Gson().fromJson(responseString, StepicWrappers.SectionContainer.class).sections.get(0);
|
||||
return postedSection.id;
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int updateTask(@NotNull final Project project, @NotNull final Task task) {
|
||||
final Lesson lesson = task.getLesson();
|
||||
final int lessonId = lesson.getId();
|
||||
|
||||
if (ourClient == null) {
|
||||
if (!login(project)) {
|
||||
LOG.error("Failed to update task");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + "/step-sources/" + String.valueOf(task.getStepicId()));
|
||||
setHeaders(request, CONTENT_TYPE_APPL_JSON);
|
||||
final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().
|
||||
registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create();
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId));
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpResponse response = ourClient.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
EntityUtils.consume(responseEntity);
|
||||
final StatusLine line = response.getStatusLine();
|
||||
if (line.getStatusCode() == HttpStatus.SC_FORBIDDEN) {
|
||||
if (login(project)) {
|
||||
updateTask(project, task);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (line.getStatusCode() != HttpStatus.SC_OK) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
});
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int updateLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) {
|
||||
final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + EduStepicNames.LESSONS + String.valueOf(lesson.getId()));
|
||||
if (ourClient == null) {
|
||||
if (!login(project)) {
|
||||
LOG.error("Failed to push lesson");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
setHeaders(request, CONTENT_TYPE_APPL_JSON);
|
||||
String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson));
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpResponse response = ourClient.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine line = response.getStatusLine();
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() == HttpStatus.SC_FORBIDDEN) {
|
||||
if (login(project)) {
|
||||
return updateLesson(project, lesson, indicator);
|
||||
}
|
||||
}
|
||||
if (line.getStatusCode() != HttpStatus.SC_OK) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
return -1;
|
||||
}
|
||||
final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0);
|
||||
for (Integer step : postedLesson.steps) {
|
||||
deleteTask(step);
|
||||
}
|
||||
|
||||
for (Task task : lesson.getTaskList()) {
|
||||
indicator.checkCanceled();
|
||||
postTask(project, task, lesson.getId());
|
||||
}
|
||||
return lesson.getId();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int postLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) {
|
||||
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/lessons");
|
||||
if (ourClient == null) {
|
||||
login(project);
|
||||
}
|
||||
|
||||
setHeaders(request, CONTENT_TYPE_APPL_JSON);
|
||||
String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson));
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpResponse response = ourClient.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
final StatusLine line = response.getStatusLine();
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() == HttpStatus.SC_FORBIDDEN) {
|
||||
if (login(project)) {
|
||||
return postLesson(project, lesson, indicator);
|
||||
}
|
||||
}
|
||||
if (line.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
return 0;
|
||||
}
|
||||
final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0);
|
||||
lesson.setId(postedLesson.getId());
|
||||
for (Task task : lesson.getTaskList()) {
|
||||
indicator.checkCanceled();
|
||||
postTask(project, task, postedLesson.getId());
|
||||
}
|
||||
return postedLesson.getId();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void deleteTask(@NotNull final Integer task) {
|
||||
final HttpDelete request = new HttpDelete(EduStepicNames.STEPIC_API_URL + EduStepicNames.STEP_SOURCES + task);
|
||||
setHeaders(request, CONTENT_TYPE_APPL_JSON);
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
try {
|
||||
final CloseableHttpResponse response = ourClient.execute(request);
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
EntityUtils.consume(responseEntity);
|
||||
final StatusLine line = response.getStatusLine();
|
||||
if (line.getStatusCode() != HttpStatus.SC_NO_CONTENT) {
|
||||
LOG.error("Failed to delete task " + responseString);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void postTask(final Project project, @NotNull final Task task, final int lessonId) {
|
||||
if (ourClient == null) {
|
||||
if (!login(project)) {
|
||||
LOG.error("Failed to update task");
|
||||
}
|
||||
}
|
||||
|
||||
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/step-sources");
|
||||
setHeaders(request, CONTENT_TYPE_APPL_JSON);
|
||||
//TODO: register type adapter for task files here?
|
||||
final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().
|
||||
registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create();
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId));
|
||||
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
final CloseableHttpResponse response = ourClient.execute(request);
|
||||
final StatusLine line = response.getStatusLine();
|
||||
final HttpEntity responseEntity = response.getEntity();
|
||||
final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
|
||||
EntityUtils.consume(responseEntity);
|
||||
if (line.getStatusCode() == HttpStatus.SC_FORBIDDEN) {
|
||||
if (login(project)) {
|
||||
postTask(project, task, lessonId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (line.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
LOG.error("Failed to push " + responseString);
|
||||
return;
|
||||
}
|
||||
|
||||
final JsonObject postedTask = new Gson().fromJson(responseString, JsonObject.class);
|
||||
final JsonObject stepSource = postedTask.getAsJsonArray("step-sources").get(0).getAsJsonObject();
|
||||
task.setStepicId(stepSource.getAsJsonPrimitive("id").getAsInt());
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void setHeaders(@NotNull final HttpRequestBase request, String contentType) {
|
||||
request.addHeader(new BasicHeader("referer", EduStepicNames.STEPIC_URL));
|
||||
request.addHeader(new BasicHeader("X-CSRFToken", ourCSRFToken));
|
||||
request.addHeader(new BasicHeader("content-type", contentType));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -2,9 +2,11 @@ package com.jetbrains.edu.learning.stepic;
|
||||
|
||||
public class EduStepicNames {
|
||||
public static final String STEPIC_URL = "https://stepic.org";
|
||||
public static final String TOKEN_URL = STEPIC_URL + "/oauth2/token/";
|
||||
public static final String STEPIC_API_URL = STEPIC_URL + "/api";
|
||||
|
||||
public static final String RECOMMENDATIONS_URL = "/recommendations";
|
||||
public static final String CONTENT_TYPE_APPL_JSON = "application/json";
|
||||
public static final String CONTENT_TYPE_APP_JSON = "application/json";
|
||||
public static final String LESSONS = "/lessons/";
|
||||
public static final String RECOMMENDATION_REACTIONS_URL = "/recommendation-reactions";
|
||||
public static final String ATTEMPTS = "/attempts";
|
||||
|
||||
+7
-9
@@ -1,10 +1,7 @@
|
||||
package com.jetbrains.edu.learning.stepic;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.ui.LoginPanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -12,6 +9,7 @@ import javax.swing.*;
|
||||
|
||||
public class LoginDialog extends DialogWrapper {
|
||||
protected final LoginPanel myLoginPanel;
|
||||
private StepicUser myStepicUser;
|
||||
|
||||
public LoginDialog() {
|
||||
super(false);
|
||||
@@ -44,13 +42,9 @@ public class LoginDialog extends DialogWrapper {
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
if (!validateLoginAndPasswordFields()) return;
|
||||
final StepicUser user = EduStepicConnector.login(myLoginPanel.getLogin(), myLoginPanel.getPassword());
|
||||
if (user != null) {
|
||||
myStepicUser = EduStepicAuthorizedClient.login(myLoginPanel.getLogin(), myLoginPanel.getPassword());
|
||||
if (myStepicUser != null) {
|
||||
doJustOkAction();
|
||||
final Project project = ProjectUtil.guessCurrentProject(myLoginPanel.getContentPanel());
|
||||
user.setEmail(myLoginPanel.getLogin());
|
||||
user.setPassword(myLoginPanel.getPassword());
|
||||
StudyTaskManager.getInstance(project).setUser(user);
|
||||
}
|
||||
else {
|
||||
setErrorText("Login failed");
|
||||
@@ -76,4 +70,8 @@ public class LoginDialog extends DialogWrapper {
|
||||
public void clearErrors() {
|
||||
setErrorText(null);
|
||||
}
|
||||
|
||||
public StepicUser getStepicUser() {
|
||||
return myStepicUser;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -158,7 +158,7 @@ public class StepicAdaptiveReactionsPanel extends JPanel {
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
StepicAdaptiveReactionsPanel.this.setEnabledRecursive(false);
|
||||
ApplicationManager.getApplication().invokeLater(()->setBackground(UIUtil.getLabelBackground()));
|
||||
EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, myReaction, indicator);
|
||||
EduAdaptiveStepicConnector.addNextRecommendedTask(StepicAdaptiveReactionsPanel.this.myProject, myReaction, indicator);
|
||||
StepicAdaptiveReactionsPanel.this.setEnabledRecursive(true);
|
||||
}
|
||||
});
|
||||
|
||||
+4
-1
@@ -151,7 +151,10 @@ public class StepicStudyOptions implements StudyOptionsProvider {
|
||||
user.setPassword(password);
|
||||
|
||||
// login to post credentials
|
||||
EduStepicConnector.login(login, password);
|
||||
final StepicUser stepicUser = EduStepicAuthorizedClient.login(login, password);
|
||||
if (stepicUser != null) {
|
||||
taskManager.setUser(stepicUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -12,6 +12,8 @@ public class StepicUser {
|
||||
private String myFirstName;
|
||||
private String myLastName;
|
||||
private String myEmail;
|
||||
private String myAccessToken;
|
||||
private String myRefreshToken;
|
||||
|
||||
public StepicUser() {
|
||||
myFirstName = "";
|
||||
@@ -74,4 +76,25 @@ public class StepicUser {
|
||||
public String getName() {
|
||||
return StringUtil.join(new String[]{myFirstName, myLastName}, " ");
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return myAccessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.myAccessToken = accessToken;
|
||||
}
|
||||
|
||||
public String getRefreshToken() {
|
||||
return myRefreshToken;
|
||||
}
|
||||
|
||||
public void setRefreshToken(String refreshToken) {
|
||||
this.myRefreshToken = refreshToken;
|
||||
}
|
||||
|
||||
public void setupTokenInfo(@NotNull final StepicWrappers.TokenInfo tokenInfo) {
|
||||
myAccessToken = tokenInfo.getAccessToken();
|
||||
myRefreshToken = tokenInfo.getRefreshToken();
|
||||
}
|
||||
}
|
||||
|
||||
+24
-3
@@ -119,12 +119,12 @@ public class StepicWrappers {
|
||||
String python27;
|
||||
|
||||
@Nullable
|
||||
public String getTemplateForLanguage(@NotNull final String langauge) {
|
||||
if (langauge.equals(EduAdaptiveStepicConnector.PYTHON2)) {
|
||||
public String getTemplateForLanguage(@NotNull final String language) {
|
||||
if (language.equals(EduAdaptiveStepicConnector.PYTHON2)) {
|
||||
return python27;
|
||||
}
|
||||
|
||||
if (langauge.equals(EduAdaptiveStepicConnector.PYTHON3)) {
|
||||
if (language.equals(EduAdaptiveStepicConnector.PYTHON3)) {
|
||||
return python3;
|
||||
}
|
||||
|
||||
@@ -435,4 +435,25 @@ public class StepicWrappers {
|
||||
enrollment = new Enrollment(courseId);
|
||||
}
|
||||
}
|
||||
|
||||
static class TokenInfo {
|
||||
@Expose String accessToken;
|
||||
@Expose String refreshToken;
|
||||
@Expose String tokenType;
|
||||
@Expose String scope;
|
||||
@Expose int expiresIn;
|
||||
|
||||
public TokenInfo() {
|
||||
accessToken = "";
|
||||
refreshToken = "";
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public String getRefreshToken() {
|
||||
return refreshToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CCCreateAnswerPlaceholderPanel {
|
||||
private static String ourFirstHintText = "Type here to add hint";
|
||||
private static final String ourFirstHintText = "Type here to add hint";
|
||||
|
||||
private JPanel myPanel;
|
||||
private JTextArea myHintTextArea;
|
||||
|
||||
+4
-8
@@ -29,10 +29,7 @@ import com.intellij.util.ui.UIUtil;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
|
||||
import com.jetbrains.edu.learning.stepic.CourseInfo;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
|
||||
import com.jetbrains.edu.learning.stepic.LoginDialog;
|
||||
import com.jetbrains.edu.learning.stepic.StepicUser;
|
||||
import com.jetbrains.edu.learning.stepic.*;
|
||||
import icons.InteractiveLearningIcons;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -344,14 +341,13 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
|
||||
ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true);
|
||||
|
||||
final StepicUser stepicUser = StudyUtils.execCancelable(() -> EduStepicConnector.login(myLoginPanel.getLogin(),
|
||||
myLoginPanel.getPassword()));
|
||||
final StepicUser stepicUser = StudyUtils.execCancelable(() -> EduStepicAuthorizedClient.login(myLoginPanel.getLogin(),
|
||||
myLoginPanel.getPassword()));
|
||||
if (stepicUser != null) {
|
||||
stepicUser.setEmail(myLoginPanel.getLogin());
|
||||
stepicUser.setPassword(myLoginPanel.getPassword());
|
||||
myGenerator.myUser = stepicUser;
|
||||
myGenerator.setEnrolledCoursesIds(EduStepicConnector.getEnrolledCoursesIds());
|
||||
|
||||
myGenerator.setEnrolledCoursesIds(EduAdaptiveStepicConnector.getEnrolledCoursesIds(stepicUser));
|
||||
final List<CourseInfo> courses = myGenerator.getCourses(true);
|
||||
if (courses != null && myRefreshCourseList) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> refreshCoursesList(courses));
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware {
|
||||
final Course course = taskManager.getCourse();
|
||||
if (course != null) {
|
||||
final StudyToolWindow studyToolWindow;
|
||||
if (StudyUtils.hasJavaFx() && StudyTaskManager.getInstance(project).shouldUseJavaFx()) {
|
||||
if (StudyUtils.hasJavaFx() && taskManager.shouldUseJavaFx()) {
|
||||
studyToolWindow = new StudyJavaFxToolWindow();
|
||||
}
|
||||
else {
|
||||
|
||||
+1
-1
@@ -183,7 +183,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
|
||||
if (course.isAdaptive() && !enrolledCoursesIds.contains(course.getId())) {
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
|
||||
ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true);
|
||||
return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId()));
|
||||
return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId(), myGenerator.myUser));
|
||||
}, "Creating Course", true, ProjectManager.getInstance().getDefaultProject());
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
@@ -115,18 +116,24 @@ public class PyPep8NamingInspection extends PyInspection {
|
||||
final PyFunction function = PsiTreeUtil.getParentOfType(node, PyFunction.class, true, PyClass.class);
|
||||
if (function == null) return;
|
||||
final Scope scope = ControlFlowCache.getScope(function);
|
||||
for (PyExpression expression : node.getTargets()) {
|
||||
final String name = expression.getName();
|
||||
for (Pair<PyExpression, PyExpression> pair : node.getTargetsToValuesMapping()) {
|
||||
final String name = pair.getFirst().getName();
|
||||
if (name == null || scope.isGlobal(name)) continue;
|
||||
if (expression instanceof PyTargetExpression) {
|
||||
final PyExpression qualifier = ((PyTargetExpression)expression).getQualifier();
|
||||
if (pair.getFirst() instanceof PyTargetExpression) {
|
||||
final PyExpression qualifier = ((PyTargetExpression)pair.getFirst()).getQualifier();
|
||||
if (qualifier != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final PyCallExpression assignedValue = PyUtil.as(pair.getSecond(), PyCallExpression.class);
|
||||
if (assignedValue != null
|
||||
&& assignedValue.getCallee() != null && PyNames.NAMEDTUPLE.equals(assignedValue.getCallee().getName())) {
|
||||
return;
|
||||
}
|
||||
final String errorCode = "N806";
|
||||
if (!LOWERCASE_REGEX.matcher(name).matches() && !name.startsWith("_") && !ignoredErrors.contains(errorCode)) {
|
||||
registerAndAddRenameAndIgnoreErrorQuickFixes(expression, errorCode);
|
||||
registerAndAddRenameAndIgnoreErrorQuickFixes(pair.getFirst(), errorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from collections import namedtuple
|
||||
|
||||
def f():
|
||||
Point = namedtuple("Point", ["x1", "x2"], verbose=True)
|
||||
<weak_warning descr="Variable in function should be lowercase">Test</weak_warning> = "foo"
|
||||
@@ -112,6 +112,10 @@ public class PyPep8NamingInspectionTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testNamedTupleInsideFunction() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testOverrideFromModule() {
|
||||
myFixture.configureByFiles("inspections/PyPep8NamingInspection/" + getTestName(true) + ".py",
|
||||
"inspections/PyPep8NamingInspection/tmp1.py");
|
||||
|
||||
@@ -103,7 +103,7 @@ statistics.warnings.count={0} {0,choice, 0#warnings|1#warning|2#warnings}
|
||||
compiler.running.dialog.title=Compiler Running
|
||||
warning.compiler.running.on.project.close=The compiler is running. Proceed with project closing?
|
||||
warning.compiler.running.on.toolwindow.close=The compiler is running. Terminate it?
|
||||
add.notnull.assertions=Add @NotNull &assertions
|
||||
add.notnull.assertions=Add runtime &assertions for not-null-annotated methods and parameters
|
||||
|
||||
compiler.eclipse.name=Eclipse
|
||||
eclipse.options.group.title=Eclipse Options
|
||||
|
||||
Reference in New Issue
Block a user