<commit_before>import _ from "lodash";
import React from "react";
import PropTypes from "prop-types";
import { Popover, OverlayTrigger, Panel } from "react-bootstrap";
import SplitTaxon from "../../../shared/components/split_taxon";
import CommunityIDPopover from "./community_id_popover";
import TaxonSummaryPopover from "./taxon_summary_popover";
import ConservationStatusBadge from "../components/conservation_status_badge";
import EstablishmentMeansBadge from "../components/establishment_means_badge";
import util from "../util";

class CommunityIdentification extends React.Component {

  constructor( props ) {
    super( props );
    this.ownerID = null;
    this.setInstanceVars = this.setInstanceVars.bind( this );
    this.communityIDOptIn = this.communityIDOptIn.bind( this );
    this.communityIDOptOut = this.communityIDOptOut.bind( this );
    this.showCommunityIDModal = this.showCommunityIDModal.bind( this );
    this.communityIDOverridePanel = this.communityIDOverridePanel.bind( this );
    this.communityIDOverrideStatement = this.communityIDOverrideStatement.bind( this );
    this.optOutPopoverClose = this.optOutPopoverClose.bind( this );
    const currentUser = props.config && props.config.currentUser;
    this.state = {
      open: currentUser ? !currentUser.prefers_hide_obs_show_expanded_cid : false
    };
  }

  setInstanceVars( ) {
    const { observation, config } = this.props;
    this.loggedIn = config && config.currentUser;
    this.observerOptedOut = ( observation.user.preferences &&
      observation.user.preferences.prefers_community_taxa === false );
    this.observationOptedIn = ( observation.preferences &&
      observation.preferences.prefers_community_taxon === true );
    this.observationOptedOut = ( observation.preferences &&
      observation.preferences.prefers_community_taxon === false );
    this.userIsObserver = this.loggedIn && config.currentUser.id === observation.user.id;
    this.communityIDIsRejected = ( this.observationOptedOut ||
      ( this.observerOptedOut && !this.observationOptedIn ) );
  }

  communityIDOptIn( e ) {
    e.preventDefault( );
    this.props.updateObservation( { prefers_community_taxon: true } );
  }

  communityIDOptOut( e ) {
    e.preventDefault( );
    this.props.updateObservation( { prefers_community_taxon: false } );
    this.optOutPopoverClose( );
  }

  optOutPopoverClose( ) {
    this.refs["popover-trigger"].hide( );
  }

  communityIDInfoPopover( ) {
    return (
      <Popover
        className="CommunityIDInfoOverlay"
        id="popover-community-id-info"
      >
        <div dangerouslySetInnerHTML={ { __html:
          I18n.t( "views.observations.community_id.explanation" ) } }
        />
      </Popover>
    );
  }

  communityIDOverridePanel( ) {
    if ( !( this.userIsObserver && this.ownerID && this.communityIDIsRejected ) ) {
      return ( <div /> );
    }
    return (
      <div className="override out">
        <span className="bold">
          { I18n.t( "views.observations.community_id.you_have_opted_out" ) }.
        </span>
        <a href="#" onClick={ this.communityIDOptIn }>
          { I18n.t( "views.observations.community_id.opt_in_for_this_observation" ) }
        </a>
        <span className="separator">·</span>
        <a href="/users/edit">
          { I18n.t( "edit_your_default_settings" ) }
        </a>
      </div>
    );
  }

  communityIDOverrideStatement( ) {
    let statement;
    if ( this.communityIDIsRejected ) {
      statement = ( <div className="opted_out stacked">
        { I18n.t( "user_has_opted_out_of_community_id" ) }
        <OverlayTrigger
          trigger="click"
          rootClose
          placement="top"
          overlay={ this.communityIDInfoPopover( ) }
          containerPadding={ 20 }
        >
          <i className="fa fa-question-circle" />
        </OverlayTrigger>
      </div> );
    }
    return statement;
  }

  optOutPopover( ) {
    // must be observer, IDer, must not have opted out already
    if ( !( this.userIsObserver && this.ownerID && this.props.observation.taxon && !this.observationOptedOut ) ) {
      return null;
    }
    // the taxa must be different, or the user defaults to opt-out, but opted in here
    if ( this.ownerID.taxon.id === this.props.observation.taxon.id &&
         !( this.observerOptedOut && this.observationOptedIn ) ) {
      return null;
    }
    let dissimilarMessage;
    const idName = this.ownerID.taxon.preferred_common_name || this.ownerID.taxon.name;
    if ( this.ownerID.taxon.id !== this.props.observation.taxon.id ) {
      dissimilarMessage = ( <span className="something" dangerouslySetInnerHTML={ { __html:
        I18n.t( "views.observations.community_id.your_id_does_not_match", {
          taxon_name: idName } ) } }
      /> );
    }
    const popover = (
      <Popover
        className="OptOutPopover"
        id="popover-opt-out"
      >
        <p>
          { I18n.t( "if_for_some_reason_a_user_doesnt_agree" ) }
        </p>
        <p>
          { dissimilarMessage }
        </p>
        <div className="action">
          <button
            className="btn btn-default reject"
            onClick={ this.communityIDOptOut }
          >
            { I18n.t( "yes_reject_it" ) }
          </button>
          <div
            className="cancel linky"
            onClick={ this.optOutPopoverClose }
          >
            { I18n.t( "cancel" ) }
          </div>
        </div>
      </Popover>
    );
    return (
      <OverlayTrigger
        trigger="click"
        rootClose
        placement="top"
        containerPadding={ 20 }
        overlay={ popover }
        ref="popover-trigger"
      >
        <div className="reject linky">
          { I18n.t( "reject?" ) }
        </div>
      </OverlayTrigger>
    );
  }

  showCommunityIDModal( ) {
    this.props.setCommunityIDModalState( { show: true } );
  }

  sortedIdents( ) {
    const { observation } = this.props;
    const currentIdents = _.filter( observation.identifications, i => ( i.current && i.taxon.is_active ) );
    const taxonCounts = _.countBy( currentIdents, i => i.taxon.id );
    // Mavericks last, then sort by counts desc
    return _.sortBy( currentIdents, i =>
      `${i.category === "maverick" ? 1 : 0}-${taxonCounts[i.taxon.id] * -1 + 1000}` );
  }

  dataForTaxon( taxon ) {
    const { observation, config } = this.props;
    const loggedIn = config && config.currentUser;
    const votesFor = [];
    const votesAgainst = [];
    let userAgreedToThis;
    let canAgree = true;
    const taxonImageTag = util.taxonImage( taxon );
    const tid = taxon.rank_level <= 10 ? taxon.ancestor_ids[taxon.ancestor_ids - 2] : taxon.id;
    const compareLink = `/observations/identotron?observation_id=${observation.id}&taxon=${tid}`;
    const currentUserID = loggedIn && _.findLast( observation.identifications, i => (
      i.current && i.user && i.user.id === config.currentUser.id
    ) );
    this.ownerID = _.findLast( observation.identifications, i => (
      i.current && i.user && i.user.id === observation.user.id
    ) );
    if ( currentUserID ) {
      canAgree = util.taxaDissimilar( currentUserID.taxon, taxon );
      userAgreedToThis = currentUserID.agreedTo && currentUserID.agreedTo === "communityID";
    }
    const sortedIdents = this.sortedIdents( );
    let taxonIsMaverick = false;
    if ( sortedIdents.length === 1 ) {
      votesFor.push( sortedIdents[0] );
    } else if ( observation.communityTaxon ) {
      let obsTaxonAncestry = `${observation.communityTaxon.id}`;
      if ( observation.communityTaxon.ancestry ) {
        obsTaxonAncestry = `${observation.communityTaxon.ancestry}/${observation.communityTaxon.id}`;
      }
      const taxonAncestry = `${taxon.ancestry}/${taxon.id}`;
      taxonIsMaverick = (
        !obsTaxonAncestry.includes( taxonAncestry ) && !taxonAncestry.includes( obsTaxonAncestry )
      );
      const first_ident_of_taxon = _.filter( sortedIdents, si => ( si.taxon.id == `${observation.communityTaxon.id}` ) )[0];
      _.each( sortedIdents, i => {
        const idAncestry = `${i.taxon.ancestry}/${i.taxon.id}`;
        if ( obsTaxonAncestry.includes( idAncestry ) || idAncestry.includes( obsTaxonAncestry ) ) {
          if ( obsTaxonAncestry.includes( i.previous_observation_taxon_id  ) && obsTaxonAncestry !== idAncestry && i.disagreement ) {
            if( i.previous_observation_taxon_id == 31 ){
              console.log( obsTaxonAncestry );
            }
            votesAgainst.push( i );
          } else if ( first_ident_of_taxon && obsTaxonAncestry.includes( idAncestry ) && obsTaxonAncestry !== idAncestry && i.disagreement == null && i.id > first_ident_of_taxon.id ) {
            votesAgainst.push( i );
          } else if ( !observation.communityTaxon.ancestry.includes( i.taxon.id ) ) {
            votesFor.push( i );
          }
        } else if ( !observation.communityTaxon.ancestry.includes( i.taxon.id ) ) {
          votesAgainst.push( i );
        }
      } );
    }
    const totalVotes = votesFor.length + votesAgainst.length;
    let voteCells = [];
    const width = `${_.round( 100 / totalVotes, 3 )}%`;
    let taxaSeen = [];
    _.each( votesFor, v => {
      if ( taxaSeen.indexOf( v.taxon.id ) < 0 ) {
        taxaSeen.push( v.taxon.id );
      }
      let voteCellClassName = `for taxon-${taxaSeen.indexOf( v.taxon.id )} ${taxon.id === v.taxon.id ? "exact" : "not-exact"}`;
      voteCells.push( (
        <CommunityIDPopover
          className={ taxon.id === v.taxon.id ? "exact" : "not-exact" }
          key={ `community-id-${v.id}` }
          keyPrefix="ids"
          identification={ v }
          communityIDTaxon={ observation.communityTaxon }<commit_msg>bugfix for stateofmatter CID<commit_after>import _ from "lodash";
import React from "react";
import PropTypes from "prop-types";
import { Popover, OverlayTrigger, Panel } from "react-bootstrap";
import SplitTaxon from "../../../shared/components/split_taxon";
import CommunityIDPopover from "./community_id_popover";
import TaxonSummaryPopover from "./taxon_summary_popover";
import ConservationStatusBadge from "../components/conservation_status_badge";
import EstablishmentMeansBadge from "../components/establishment_means_badge";
import util from "../util";

class CommunityIdentification extends React.Component {

  constructor( props ) {
    super( props );
    this.ownerID = null;
    this.setInstanceVars = this.setInstanceVars.bind( this );
    this.communityIDOptIn = this.communityIDOptIn.bind( this );
    this.communityIDOptOut = this.communityIDOptOut.bind( this );
    this.showCommunityIDModal = this.showCommunityIDModal.bind( this );
    this.communityIDOverridePanel = this.communityIDOverridePanel.bind( this );
    this.communityIDOverrideStatement = this.communityIDOverrideStatement.bind( this );
    this.optOutPopoverClose = this.optOutPopoverClose.bind( this );
    const currentUser = props.config && props.config.currentUser;
    this.state = {
      open: currentUser ? !currentUser.prefers_hide_obs_show_expanded_cid : false
    };
  }

  setInstanceVars( ) {
    const { observation, config } = this.props;
    this.loggedIn = config && config.currentUser;
    this.observerOptedOut = ( observation.user.preferences &&
      observation.user.preferences.prefers_community_taxa === false );
    this.observationOptedIn = ( observation.preferences &&
      observation.preferences.prefers_community_taxon === true );
    this.observationOptedOut = ( observation.preferences &&
      observation.preferences.prefers_community_taxon === false );
    this.userIsObserver = this.loggedIn && config.currentUser.id === observation.user.id;
    this.communityIDIsRejected = ( this.observationOptedOut ||
      ( this.observerOptedOut && !this.observationOptedIn ) );
  }

  communityIDOptIn( e ) {
    e.preventDefault( );
    this.props.updateObservation( { prefers_community_taxon: true } );
  }

  communityIDOptOut( e ) {
    e.preventDefault( );
    this.props.updateObservation( { prefers_community_taxon: false } );
    this.optOutPopoverClose( );
  }

  optOutPopoverClose( ) {
    this.refs["popover-trigger"].hide( );
  }

  communityIDInfoPopover( ) {
    return (
      <Popover
        className="CommunityIDInfoOverlay"
        id="popover-community-id-info"
      >
        <div dangerouslySetInnerHTML={ { __html:
          I18n.t( "views.observations.community_id.explanation" ) } }
        />
      </Popover>
    );
  }

  communityIDOverridePanel( ) {
    if ( !( this.userIsObserver && this.ownerID && this.communityIDIsRejected ) ) {
      return ( <div /> );
    }
    return (
      <div className="override out">
        <span className="bold">
          { I18n.t( "views.observations.community_id.you_have_opted_out" ) }.
        </span>
        <a href="#" onClick={ this.communityIDOptIn }>
          { I18n.t( "views.observations.community_id.opt_in_for_this_observation" ) }
        </a>
        <span className="separator">·</span>
        <a href="/users/edit">
          { I18n.t( "edit_your_default_settings" ) }
        </a>
      </div>
    );
  }

  communityIDOverrideStatement( ) {
    let statement;
    if ( this.communityIDIsRejected ) {
      statement = ( <div className="opted_out stacked">
        { I18n.t( "user_has_opted_out_of_community_id" ) }
        <OverlayTrigger
          trigger="click"
          rootClose
          placement="top"
          overlay={ this.communityIDInfoPopover( ) }
          containerPadding={ 20 }
        >
          <i className="fa fa-question-circle" />
        </OverlayTrigger>
      </div> );
    }
    return statement;
  }

  optOutPopover( ) {
    // must be observer, IDer, must not have opted out already
    if ( !( this.userIsObserver && this.ownerID && this.props.observation.taxon && !this.observationOptedOut ) ) {
      return null;
    }
    // the taxa must be different, or the user defaults to opt-out, but opted in here
    if ( this.ownerID.taxon.id === this.props.observation.taxon.id &&
         !( this.observerOptedOut && this.observationOptedIn ) ) {
      return null;
    }
    let dissimilarMessage;
    const idName = this.ownerID.taxon.preferred_common_name || this.ownerID.taxon.name;
    if ( this.ownerID.taxon.id !== this.props.observation.taxon.id ) {
      dissimilarMessage = ( <span className="something" dangerouslySetInnerHTML={ { __html:
        I18n.t( "views.observations.community_id.your_id_does_not_match", {
          taxon_name: idName } ) } }
      /> );
    }
    const popover = (
      <Popover
        className="OptOutPopover"
        id="popover-opt-out"
      >
        <p>
          { I18n.t( "if_for_some_reason_a_user_doesnt_agree" ) }
        </p>
        <p>
          { dissimilarMessage }
        </p>
        <div className="action">
          <button
            className="btn btn-default reject"
            onClick={ this.communityIDOptOut }
          >
            { I18n.t( "yes_reject_it" ) }
          </button>
          <div
            className="cancel linky"
            onClick={ this.optOutPopoverClose }
          >
            { I18n.t( "cancel" ) }
          </div>
        </div>
      </Popover>
    );
    return (
      <OverlayTrigger
        trigger="click"
        rootClose
        placement="top"
        containerPadding={ 20 }
        overlay={ popover }
        ref="popover-trigger"
      >
        <div className="reject linky">
          { I18n.t( "reject?" ) }
        </div>
      </OverlayTrigger>
    );
  }

  showCommunityIDModal( ) {
    this.props.setCommunityIDModalState( { show: true } );
  }

  sortedIdents( ) {
    const { observation } = this.props;
    const currentIdents = _.filter( observation.identifications, i => ( i.current && i.taxon.is_active ) );
    const taxonCounts = _.countBy( currentIdents, i => i.taxon.id );
    // Mavericks last, then sort by counts desc
    return _.sortBy( currentIdents, i =>
      `${i.category === "maverick" ? 1 : 0}-${taxonCounts[i.taxon.id] * -1 + 1000}` );
  }

  dataForTaxon( taxon ) {
    const { observation, config } = this.props;
    const loggedIn = config && config.currentUser;
    const votesFor = [];
    const votesAgainst = [];
    let userAgreedToThis;
    let canAgree = true;
    const taxonImageTag = util.taxonImage( taxon );
    const tid = taxon.rank_level <= 10 ? taxon.ancestor_ids[taxon.ancestor_ids - 2] : taxon.id;
    const compareLink = `/observations/identotron?observation_id=${observation.id}&taxon=${tid}`;
    const currentUserID = loggedIn && _.findLast( observation.identifications, i => (
      i.current && i.user && i.user.id === config.currentUser.id
    ) );
    this.ownerID = _.findLast( observation.identifications, i => (
      i.current && i.user && i.user.id === observation.user.id
    ) );
    if ( currentUserID ) {
      canAgree = util.taxaDissimilar( currentUserID.taxon, taxon );
      userAgreedToThis = currentUserID.agreedTo && currentUserID.agreedTo === "communityID";
    }
    const sortedIdents = this.sortedIdents( );
    let taxonIsMaverick = false;
    if ( sortedIdents.length === 1 ) {
      votesFor.push( sortedIdents[0] );
    } else if ( observation.communityTaxon ) {
      let obsTaxonAncestry = `${observation.communityTaxon.id}`;
      if ( observation.communityTaxon.ancestry ) {
        obsTaxonAncestry = `${observation.communityTaxon.ancestry}/${observation.communityTaxon.id}`;
      }
      const taxonAncestry = `${taxon.ancestry}/${taxon.id}`;
      taxonIsMaverick = (
        !obsTaxonAncestry.includes( taxonAncestry ) && !taxonAncestry.includes( obsTaxonAncestry )
      );
      const first_ident_of_taxon = _.filter( sortedIdents, si => ( si.taxon.id == `${observation.communityTaxon.id}` ) )[0];
      _.each( sortedIdents, i => {
        const idAncestry = `${i.taxon.ancestry}/${i.taxon.id}`;
        if ( obsTaxonAncestry.includes( idAncestry ) || idAncestry.includes( obsTaxonAncestry ) ) {
          if ( obsTaxonAncestry.includes( i.previous_observation_taxon_id  ) && obsTaxonAncestry !== idAncestry && i.disagreement ) {
            votesAgainst.push( i );
          } else if ( first_ident_of_taxon && obsTaxonAncestry.includes( idAncestry ) && obsTaxonAncestry !== idAncestry && i.disagreement == null && i.id > first_ident_of_taxon.id ) {
            votesAgainst.push( i );
          } else if ( observation.communityTaxon.ancestry && !observation.communityTaxon.ancestry.includes( i.taxon.id ) ) {
            votesFor.push( i );
          }
        } else if ( observation.communityTaxon.ancestry && !observation.communityTaxon.ancestry.includes( i.taxon.id ) ) {
          votesAgainst.push( i );
        }
      } );
    }
    const totalVotes = votesFor.length + votesAgainst.length;
    let voteCells = [];
    const width = `${_.round( 100 / totalVotes, 3 )}%`;
    let taxaSeen = [];
    _.each( votesFor, v => {
      if ( taxaSeen.indexOf( v.taxon.id ) < 0 ) {
        taxaSeen.push( v.taxon.id );
      }
      let voteCellClassName = `for taxon-${taxaSeen.indexOf( v.taxon.id )} ${taxon.id === v.taxon.id ? "exact" : "not-exact"}`;
      voteCells.push( (
        <CommunityIDPopover
          className={ taxon.id === v.taxon.id ? "exact" : "not-exact" }
          key={ `community-id-${v.id}` }
          keyPrefix="ids"
          identification={ v }
          communityIDTaxon={ observation.communityTaxon }
{'fraction_non_alphanumeric': 0.13138498015566208, 'fraction_numerical': 0.004432761197876398, 'mean_word_length': 2.6878920357346514, 'pattern_counts': {'":': 0, '<': 81, '<?xml version=': 0, '>': 93, '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': '533884', 'n_tokens_mistral': 6402, 'n_tokens_neox': 6169, 'n_words': 1062}