<commit_before>/* @flow */
import { empty } from "rxjs/observable/empty";
import { of } from "rxjs/observable/of";
import { interval } from "rxjs/observable/interval";

import {
  tap,
  filter,
  map,
  mergeMap,
  mapTo,
  switchMap,
  catchError
} from "rxjs/operators";
import { ofType } from "redux-observable";

import FileSaver from "file-saver";

import * as actions from "../actions";
import * as actionTypes from "../actionTypes";
import * as selectors from "../selectors";
import type { AppState } from "../state";

import type { ActionsObservable } from "redux-observable";

import { contents } from "rx-jupyter";

import { fromJS, toJS, stringifyNotebook } from "@nteract/commutable";
import type { Notebook } from "@nteract/commutable";

export function fetchContentEpic(
  action$: ActionsObservable<*>,
  store: Store<*, *>
) {
  return action$.pipe(
    ofType(actionTypes.FETCH_CONTENT),
    switchMap((action: actionTypes.FetchContent) => {
      if (!action.payload || typeof action.payload.filepath !== "string") {
        return of({
          type: "ERROR",
          error: true,
          payload: { error: new Error("fetching content needs a payload") }
        });
      }

      const state = store.getState();

      const host = selectors.currentHost(state);
      if (host.type !== "jupyter") {
        // Dismiss any usage that isn't targeting a jupyter server
        return empty();
      }
      const serverConfig = selectors.serverConfig(host);

      return contents
        .get(serverConfig, action.payload.filepath, action.payload.params)
        .pipe(
          tap(xhr => {
            if (xhr.status !== 200) {
              throw new Error(xhr.response);
            }
          }),
          map(xhr => {
            return actions.fetchContentFulfilled({
              filepath: action.payload.filepath,
              model: xhr.response,
              kernelRef: action.payload.kernelRef,
              contentRef: action.payload.contentRef
            });
          }),
          catchError((xhrError: any) =>
            of(
              actions.fetchContentFailed({
                filepath: action.payload.filepath,
                error: xhrError,
                kernelRef: action.payload.kernelRef,
                contentRef: action.payload.contentRef
              })
            )
          )
        );
    })
  );
}

export function downloadString(
  fileContents: string,
  filepath: string,
  contentType: string
) {
  const filename = filepath.split("/").pop();
  const blob = new Blob([fileContents], { type: contentType });
  // NOTE: There is no callback for this, we have to rely on the browser
  //       to do this well, so we assume it worked
  FileSaver.saveAs(blob, filename);
}

export function autoSaveCurrentContentEpic(
  action$: ActionsObservable<Action>,
  store: Store<AppState, *>
) {
  // Save every seven seconds, regardless of contentType
  return interval(3000).pipe(
    // TODO: Once we're switched to the coming redux observable 1.0.0 release,
    // we should use the state$ stream to only save when the content has changed
    mergeMap(() => {
      const state = store.getState();
      const contentRef = state.core.currentContentRef;

      const content = selectors.content(state, { contentRef });

      if (
        // Don't bother saving nothing
        content &&
        // Only files and notebooks
        (content.type === "file" || content.type === "notebook") &&
        // Only save if they have a real filepath
        content.filepath !== ""
      ) {
        return of(actions.save({ contentRef }));
      } else {
        return empty();
      }
    })
  );
}

export function saveContentEpic(
  action$: ActionsObservable<Action>,
  store: Store<*, *>
) {
  return action$.pipe(<commit_msg>save every 30 seconds<commit_after>/* @flow */
import { empty } from "rxjs/observable/empty";
import { of } from "rxjs/observable/of";
import { interval } from "rxjs/observable/interval";

import {
  tap,
  filter,
  map,
  mergeMap,
  mapTo,
  switchMap,
  catchError
} from "rxjs/operators";
import { ofType } from "redux-observable";

import FileSaver from "file-saver";

import * as actions from "../actions";
import * as actionTypes from "../actionTypes";
import * as selectors from "../selectors";
import type { AppState } from "../state";

import type { ActionsObservable } from "redux-observable";

import { contents } from "rx-jupyter";

import { fromJS, toJS, stringifyNotebook } from "@nteract/commutable";
import type { Notebook } from "@nteract/commutable";

export function fetchContentEpic(
  action$: ActionsObservable<*>,
  store: Store<*, *>
) {
  return action$.pipe(
    ofType(actionTypes.FETCH_CONTENT),
    switchMap((action: actionTypes.FetchContent) => {
      if (!action.payload || typeof action.payload.filepath !== "string") {
        return of({
          type: "ERROR",
          error: true,
          payload: { error: new Error("fetching content needs a payload") }
        });
      }

      const state = store.getState();

      const host = selectors.currentHost(state);
      if (host.type !== "jupyter") {
        // Dismiss any usage that isn't targeting a jupyter server
        return empty();
      }
      const serverConfig = selectors.serverConfig(host);

      return contents
        .get(serverConfig, action.payload.filepath, action.payload.params)
        .pipe(
          tap(xhr => {
            if (xhr.status !== 200) {
              throw new Error(xhr.response);
            }
          }),
          map(xhr => {
            return actions.fetchContentFulfilled({
              filepath: action.payload.filepath,
              model: xhr.response,
              kernelRef: action.payload.kernelRef,
              contentRef: action.payload.contentRef
            });
          }),
          catchError((xhrError: any) =>
            of(
              actions.fetchContentFailed({
                filepath: action.payload.filepath,
                error: xhrError,
                kernelRef: action.payload.kernelRef,
                contentRef: action.payload.contentRef
              })
            )
          )
        );
    })
  );
}

export function downloadString(
  fileContents: string,
  filepath: string,
  contentType: string
) {
  const filename = filepath.split("/").pop();
  const blob = new Blob([fileContents], { type: contentType });
  // NOTE: There is no callback for this, we have to rely on the browser
  //       to do this well, so we assume it worked
  FileSaver.saveAs(blob, filename);
}

export function autoSaveCurrentContentEpic(
  action$: ActionsObservable<Action>,
  store: Store<AppState, *>
) {
  // Save every 30 seconds, regardless of contentType
  return interval(30 * 1000).pipe(
    // TODO: Once we're switched to the coming redux observable 1.0.0 release,
    // we should use the state$ stream to only save when the content has changed
    mergeMap(() => {
      const state = store.getState();
      const contentRef = state.core.currentContentRef;

      const content = selectors.content(state, { contentRef });

      if (
        // Don't bother saving nothing
        content &&
        // Only files and notebooks
        (content.type === "file" || content.type === "notebook") &&
        // Only save if they have a real filepath
        content.filepath !== ""
      ) {
        return of(actions.save({ contentRef }));
      } else {
        return empty();
      }
    })
  );
}

export function saveContentEpic(
  action$: ActionsObservable<Action>,
  store: Store<*, *>
) {
  return action$.pipe(
{'fraction_non_alphanumeric': 0.12125591171833947, 'fraction_numerical': 0.003415659485023647, 'mean_word_length': 2.3567019400352733, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6869775', 'n_tokens_mistral': 2196, 'n_tokens_neox': 2132, 'n_words': 593}