<commit_before>import PropTypes from 'prop-types';
import React from 'react';
import { defaults, filter, map } from 'lodash';
import classnames from 'classnames';
import Exercise from '../../models/exercises/exercise';
import { observer } from 'mobx-react';
import { computed, action, observable } from 'mobx';
import TagModel from 'shared/model/exercise/tag';
import { Icon } from 'shared';
import Error from './error';
import Wrapper from './wrapper';

const TYPE = 'aplo';
import BookSelection from './book-selection';

const BOOKS = {
  'stax-apbio': {
    r: /^\d{1}\.\d{1,2}$/,
    pattern: '#.##',
    disallowed: /[^0-9.]/g,
  },
  'stax-apphys': {
    r: /^\d\.[A-Z]\.\d\.\d$/,
    pattern: '#.[A-Z].#.#',
    disallowed: /[^0-9A-Z.]/g,
  },
};

@observer
class Input extends React.Component {
  static propTypes = {
    exercise: PropTypes.instanceOf(Exercise).isRequired,
    tag: PropTypes.instanceOf(TagModel).isRequired,
  };

  @observable errorMsg;
  @observable value = this.props.tag.value;

  @computed get book() {
    const { specifier } =  this.props.tag;
    if (specifier && this.availableBooks.includes(specifier)) {
      return specifier;
    }
    return null;
  }

  @computed get lo() {
    return this.props.tag.value;
  }

  @computed get validation() {
    return this.book && BOOKS[this.book];
  }

  @action.bound onTextChange(ev) {
    this.value = ev.target.value.replace(this.validation.disallowed, '');
    this.errorMsg = null;
  }

  isLoValid(book, lo) {
    if (!lo) { return true; } // LO is not required
    return lo.match( this.validation.r );
  }

  @action.bound validateAndSave(attrs) {
    if (attrs == null) { attrs = {}; }<commit_msg>return true if there's no validation, such as when book is blank<commit_after>import PropTypes from 'prop-types';
import React from 'react';
import { defaults, filter, map } from 'lodash';
import classnames from 'classnames';
import Exercise from '../../models/exercises/exercise';
import { observer } from 'mobx-react';
import { computed, action, observable } from 'mobx';
import TagModel from 'shared/model/exercise/tag';
import { Icon } from 'shared';
import Error from './error';
import Wrapper from './wrapper';

const TYPE = 'aplo';
import BookSelection from './book-selection';

const BOOKS = {
  'stax-apbio': {
    r: /^\d{1}\.\d{1,2}$/,
    pattern: '#.##',
    disallowed: /[^0-9.]/g,
  },
  'stax-apphys': {
    r: /^\d\.[A-Z]\.\d\.\d$/,
    pattern: '#.[A-Z].#.#',
    disallowed: /[^0-9A-Z.]/g,
  },
};

@observer
class Input extends React.Component {
  static propTypes = {
    exercise: PropTypes.instanceOf(Exercise).isRequired,
    tag: PropTypes.instanceOf(TagModel).isRequired,
  };

  @observable errorMsg;
  @observable value = this.props.tag.value;

  @computed get book() {
    const { specifier } =  this.props.tag;
    if (specifier && this.availableBooks.includes(specifier)) {
      return specifier;
    }
    return null;
  }

  @computed get lo() {
    return this.props.tag.value;
  }

  @computed get validation() {
    return this.book && BOOKS[this.book];
  }

  @action.bound onTextChange(ev) {
    this.value = ev.target.value.replace(this.validation.disallowed, '');
    this.errorMsg = null;
  }

  isLoValid(book, lo) {
    if (!lo || !this.validation) { return true; } // LO is not required
    return lo.match( this.validation.r );
  }

  @action.bound validateAndSave(attrs) {
    if (attrs == null) { attrs = {}; }
{'fraction_non_alphanumeric': 0.1718432510885341, 'fraction_numerical': 0.0040638606676342524, 'mean_word_length': 3.2075702075702077, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3295797', 'n_tokens_mistral': 1192, 'n_tokens_neox': 1166, 'n_words': 282}