text:
"""Tests for one off statistics jobs."""

import datetime

from core.domain import config_domain
from core.domain import exp_domain
from core.domain import exp_fetchers
from core.domain import exp_services
from core.domain import stats_domain
from core.domain import stats_jobs_continuous
from core.domain import stats_jobs_one_off
from core.domain import stats_services
from core.platform import models
from core.platform.taskqueue import gae_taskqueue_services as taskqueue_services
from core.tests import test_utils
import feconf

(exp_models, stats_models,) = models.Registry.import_models(
    [models.NAMES.exploration, models.NAMES.statistics])

class OneOffJobTestBase(test_utils.GenericTestBase):
    """Base class providing convenience methods for testing one off jobs."""

    def count_one_off_jobs_in_queue(self):
        """Counts one off jobs in the taskqueue."""
        return self.count_jobs_in_taskqueue(
            taskqueue_services.QUEUE_NAME_ONE_OFF_JOBS)

    def run_one_off_job(self):
        """Begins the one off job and asserts it completes as expected.

        Assumes the existence of a class constant ONE_OFF_JOB_CLASS, pointing
        to the queue under test.

        Returns:
            *. The output of the one off job.
        """
        job_id = self.ONE_OFF_JOB_CLASS.create_new()
        self.assertEqual(self.count_one_off_jobs_in_queue(), 0)
        self.ONE_OFF_JOB_CLASS.enqueue(job_id)
        self.assertEqual(self.count_one_off_jobs_in_queue(), 1)
        self.process_and_flush_pending_tasks()
        self.assertEqual(self.count_one_off_jobs_in_queue(), 0)
        return self.ONE_OFF_JOB_CLASS.get_output(job_id)

class PlaythroughAuditTests(OneOffJobTestBase):
    ONE_OFF_JOB_CLASS = stats_jobs_one_off.PlaythroughAudit

    def setUp(self):
        super(PlaythroughAuditTests, self).setUp()
        self.exp = self.save_new_valid_exploration('EXP_ID', 'owner_id')

    def create_playthrough(self):
        """Helper method to create and return a simple playthrough model."""
        playthrough_id = stats_models.PlaythroughModel.create(
            self.exp.id, self.exp.version, issue_type='EarlyQuit',
            issue_customization_args={
                'state_name': {'value': 'state_name'},
                'time_spent_in_exp_in_msecs': {'value': 200},
            },
            actions=[])
        return stats_models.PlaythroughModel.get(playthrough_id)

    def create_exp_issues_with_playthroughs(self, playthrough_ids_list):
        """Helper method to create and return an ExplorationIssuesModel instance
        with the given sets of playthrough ids as reference issues.
        """
        return stats_models.ExplorationIssuesModel.create(
            self.exp.id, self.exp.version, unresolved_issues=[
                {
                    'issue_type': 'EarlyQuit',
                    'issue_customization_args': {
                        'state_name': {'value': 'state_name'},
                        'time_spent_in_exp_in_msecs': {'value': 200},
                    },
                    'playthrough_ids': list(playthrough_ids),
                    'schema_version': 1,
                    'is_valid': True,
                }
                for playthrough_ids in playthrough_ids_list
            ])

    def test_empty_output_for_good_playthrough(self):
        self.set_config_property(
            config_domain.WHITELISTED_EXPLORATION_IDS_FOR_PLAYTHROUGHS,
            [self.exp.id])
        playthrough = self.create_playthrough()
        self.create_exp_issues_with_playthroughs([[playthrough.id]])

        output = self.run_one_off_job()

        self.assertEqual(output, [])

    def test_output_for_pre_release_playthrough(self):
        self.set_config_property(
            config_domain.WHITELISTED_EXPLORATION_IDS_FOR_PLAYTHROUGHS,
            [self.exp.id])
        playthrough = self.create_playthrough()
        # Set created_on to a date which is definitely before GSoC 2018.
        playthrough.created_on = datetime.datetime(2017, 12, 31)
        playthrough.put()
        self.create_exp_issues_with_playthroughs([[playthrough.id]])

        output = self.run_one_off_job()

        self.assertEqual(len(output), 1)
        self.assertIn('before the GSoC 2018 submission deadline', output[0])

    def test_output_for_non_whitelisted_playthrough(self):
        self.set_config_property(
            config_domain.WHITELISTED_EXPLORATION_IDS_FOR_PLAYTHROUGHS,
            [self.exp.id + 'differentiated'])
        playthrough = self.create_playthrough()
        self.create_exp_issues_with_playthroughs([[playthrough.id]])

        output = self.run_one_off_job()

        self.assertEqual(len(output), 1)
        self.assertIn('has not been curated for recording', output[0])

    def test_output_for_bad_schema_playthrough(self):
        self.set_config_property(
            config_domain.WHITELISTED_EXPLORATION_IDS_FOR_PLAYTHROUGHS,
            [self.exp.id])
        playthrough = self.create_playthrough()
        playthrough.actions.append({'bad schema key': 'bad schema value'})
        playthrough.put()
        self.create_exp_issues_with_playthroughs([[playthrough.id]])

        output = self.run_one_off_job()

        self.assertEqual(len(output), 1)
        self.assertIn('could not be validated', output[0])

    def test_output_for_missing_reference(self):
        self.set_config_property(
            config_domain.WHITELISTED_EXPLORATION_IDS_FOR_PLAYTHROUGHS,
            [self.exp.id])
        playthrough = self.create_playthrough()
        self.create_exp_issues_with_playthroughs([
            [playthrough.id + '-different'],
        ])

        output = self.run_one_off_job()

        self.assertEqual(len(output), 1)
        self.assertIn('not found as a reference', output[0])

class RegenerateMissingStatsModelsOneOffJobTests(OneOffJobTestBase):
    ONE_OFF_JOB_CLASS = stats_jobs_one_off.RegenerateMissingStatsModelsOneOffJob
    EXP_ID = 'EXP_ID1'

    def setUp(self):
        super(RegenerateMissingStatsModelsOneOffJobTests, self).setUp()

        self.exp1 = self.save_new_valid_exploration(self.EXP_ID, 'owner')

        state_stats_dict = {
            'total_answers_count_v1': 3,
            'total_answers_count_v2': 0,
            'useful_feedback_count_v1': 3,
            'useful_feedback_count_v2': 0,
            'total_hit_count_v1': 3,
            'total_hit_count_v2': 0,
            'first_hit_count_v1': 3,
            'first_hit_count_v2': 0,
            'num_times_solution_viewed_v2': 0,
            'num_completions_v1': 3,
            'num_completions_v2': 0,
        }

        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=1, num_starts_v1=7, num_starts_v2=0,
            num_actual_starts_v1=5, num_actual_starts_v2=0,
            num_completions_v1=2, num_completions_v2=0,
            state_stats_mapping={self.exp1.init_state_name: state_stats_dict})

        self.exp1.add_states(['New state'])
        change_list = [
            exp_domain.ExplorationChange(
                {'cmd': 'add_state', 'state_name': 'New state'})]
        # v2 version does not have ExplorationStatsModel.
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.exp1.id, change_list, '')
        self.exp1 = exp_fetchers.get_exploration_by_id(self.exp1.id)

    def test_stats_models_regeneration_works(self):
        """Test that stats models are regenerated with correct v1 stats
        values.
        """
        self.run_one_off_job()

        # Verify exploration version 2 has a stats model and values are correct.
        exp_stats = (
            stats_services.get_exploration_stats_by_id(self.EXP_ID, 2))

        self.assertEqual(exp_stats.num_starts_v1, 7)
        self.assertEqual(exp_stats.num_actual_starts_v1, 5)
        self.assertEqual(exp_stats.num_completions_v1, 2)

        state_stats = exp_stats.state_stats_mapping[self.exp1.init_state_name]

        self.assertEqual(state_stats.total_answers_count_v1, 3)
        self.assertEqual(state_stats.useful_feedback_count_v1, 3)
        self.assertEqual(state_stats.total_hit_count_v1, 3)
        self.assertEqual(state_stats.first_hit_count_v1, 3)
        self.assertEqual(state_stats.num_completions_v1, 3)

    def test_output_for_deleted_exploration(self):
        job_id = self.ONE_OFF_JOB_CLASS.create_new()
        self.ONE_OFF_JOB_CLASS.enqueue(job_id)
        self.assertEqual(self.count_one_off_jobs_in_queue(), 1)

        exp_services.delete_exploration('owner', self.EXP_ID)
        self.process_and_flush_pending_tasks()
        self.assertEqual(self.count_one_off_jobs_in_queue(), 0)

        output = self.ONE_OFF_JOB_CLASS.get_output(job_id)
        self.assertEqual(output, [])

    def test_stats_models_regeneration_with_no_stats_model(self):
        change_list = [exp_domain.ExplorationChange({
            'cmd': exp_domain.CMD_RENAME_STATE,
            'old_state_name': 'New state',
            'new_state_name': 'Another state'
        })]
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.exp1.id, change_list, '')

        model_id = '%s.3' % self.EXP_ID
        model1 = stats_models.ExplorationStatsModel.get(model_id)
        model1.delete()

        output = self.run_one_off_job()
        self.assertEqual(
            output,
            ['[u\'ExplorationStatsModel for missing versions regenerated: \', '
             '[u\'EXP_ID1 v3\']]'])

        model_id = '%s.2' % self.EXP_ID
        model1 = stats_models.ExplorationStatsModel.get(model_id)
        model1.delete()

        output = self.run_one_off_job()
        self.assertEqual(
            output,
            ['[u\'ExplorationStatsModel for missing versions regenerated: \', '
             '[u\'EXP_ID1 v2\']]'])

class RecomputeStateCompleteStatisticsTests(OneOffJobTestBase):
    ONE_OFF_JOB_CLASS = stats_jobs_one_off.RecomputeStatisticsOneOffJob
    EXP_ID = 'EXP_ID'
    STATE_B = 'b'

    def setUp(self):
        super(RecomputeStateCompleteStatisticsTests, self).setUp()
        self.exp = self.save_new_valid_exploration(self.EXP_ID, 'owner')

        change_list = []
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')
        change_list = [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_RENAME_STATE,
                'old_state_name': self.exp.init_state_name,
                'new_state_name': self.STATE_B,
            })
        ]
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')

        stats_models.StateCompleteEventLogEntryModel(
            id='id0',
            exp_id=self.EXP_ID,
            exp_version=1,
            state_name=self.exp.init_state_name,
            session_id='session_id_1',
            time_spent_in_state_secs=1.0,
            event_schema_version=2).put()
        stats_models.StateCompleteEventLogEntryModel(
            id='id1',
            exp_id=self.EXP_ID,
            exp_version=1,
            state_name=self.exp.init_state_name,
            session_id='session_id_2',
            time_spent_in_state_secs=1.0,
            event_schema_version=2).put()
        stats_models.StateCompleteEventLogEntryModel(
            id='id2',
            exp_id=self.EXP_ID,
            exp_version=1,
            state_name=self.exp.init_state_name,
            session_id='session_id_3',
            time_spent_in_state_secs=1.0,
            event_schema_version=1).put()
        stats_models.StateCompleteEventLogEntryModel(
            id='id3',
            exp_id=self.EXP_ID,
            exp_version=2,
            state_name=self.exp.init_state_name,
            session_id='session_id_4',
            time_spent_in_state_secs=1.0,
            event_schema_version=2).put()
        stats_models.StateCompleteEventLogEntryModel(
            id='id4',
            exp_id=self.EXP_ID,
            exp_version=3,
            state_name=self.STATE_B,
            session_id='session_id_4',
            time_spent_in_state_secs=1.0,
            event_schema_version=2).put()

        state_stats_dict = {
            'total_answers_count_v1': 3,
            'total_answers_count_v2': 0,
            'useful_feedback_count_v1': 3,
            'useful_feedback_count_v2': 0,
            'total_hit_count_v1': 3,
            'total_hit_count_v2': 0,
            'first_hit_count_v1': 3,
            'first_hit_count_v2': 0,
            'num_times_solution_viewed_v2': 0,
            'num_completions_v1': 3,
            'num_completions_v2': 9,
        }
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=1, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.exp.init_state_name: state_stats_dict})
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=2, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.exp.init_state_name: state_stats_dict})
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=3, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.STATE_B: state_stats_dict})

    def test_standard_operation(self):
        self.run_one_off_job()

        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 1))
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping[self.exp.init_state_name]
        # Check the old event schema version was not counted.
        self.assertEqual(state_stats['num_completions_v2'], 2)

        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 2))
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping[self.exp.init_state_name]
        # Check that the new version counts events for the previous
        # versions.
        self.assertEqual(state_stats['num_completions_v2'], 3)

        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 3))
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping[self.STATE_B]
        # Check that the new version with a renamed state still
        # counts the events for the state in previous versions
        # with the old name.
        self.assertEqual(state_stats['num_completions_v2'], 4)

    def test_job_for_addition_and_deletion_of_state_names(self):
        change_list = [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_ADD_STATE,
                'state_name': 'State A',
            })
        ]
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')

        job_output = self.run_one_off_job()
        self.assertEqual(job_output, [])

        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 4))
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping.get('State A')

        expected_state_stats_dict = stats_domain.StateStats.create_default(
            ).to_dict()
        self.assertEqual(state_stats, expected_state_stats_dict)

        change_list = [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_DELETE_STATE,
                'state_name': 'State A',
            })
        ]
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')

        job_output = self.run_one_off_job()
        self.assertEqual(job_output, [])

        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 5))
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping.get('State A')

        self.assertIsNone(state_stats)

class RecomputeAnswerSubmittedStatisticsTests(OneOffJobTestBase):
    ONE_OFF_JOB_CLASS = stats_jobs_one_off.RecomputeStatisticsOneOffJob
    EXP_ID = 'EXP_ID'
    EXP_VERSION = 1
    SESSION_ID_1 = 'session_id_1'
    SESSION_ID_2 = 'session_id_2'

    def setUp(self):
        super(RecomputeAnswerSubmittedStatisticsTests, self).setUp()
        self.exp = self.save_new_valid_exploration(self.EXP_ID, 'owner')
        self.state_name = self.exp.init_state_name

        change_list = [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_RENAME_STATE,
                'old_state_name': self.state_name,
                'new_state_name': 'b',
            })
        ]
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')

        stats_models.AnswerSubmittedEventLogEntryModel(
            id='id1',
            exp_id=self.EXP_ID,
            exp_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_1,
            time_spent_in_state_secs=1.0,
            is_feedback_useful=True,
            event_schema_version=2).put()
        stats_models.AnswerSubmittedEventLogEntryModel(
            id='id2',
            exp_id=self.EXP_ID,
            exp_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_1,
            time_spent_in_state_secs=1.0,
            is_feedback_useful=True,
            event_schema_version=2).put()
        stats_models.AnswerSubmittedEventLogEntryModel(
            id='id3',
            exp_id=self.EXP_ID,
            exp_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_1,
            time_spent_in_state_secs=1.0,
            is_feedback_useful=False,
            event_schema_version=2).put()
        stats_models.AnswerSubmittedEventLogEntryModel(
            id='id4',
            exp_id=self.EXP_ID,
            exp_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_1,
            time_spent_in_state_secs=1.0,
            is_feedback_useful=True,
            event_schema_version=1).put()
        stats_models.AnswerSubmittedEventLogEntryModel(
            id='id5',
            exp_id=self.EXP_ID,
            exp_version=2,
            state_name='b',
            session_id=self.SESSION_ID_1,
            time_spent_in_state_secs=1.0,
            is_feedback_useful=True,
            event_schema_version=2).put()

        state_stats_dict = {
            'total_answers_count_v1': 3,
            'total_answers_count_v2': 9,
            'useful_feedback_count_v1': 3,
            'useful_feedback_count_v2': 9,
            'total_hit_count_v1': 3,
            'total_hit_count_v2': 0,
            'first_hit_count_v1': 3,
            'first_hit_count_v2': 0,
            'num_times_solution_viewed_v2': 0,
            'num_completions_v1': 3,
            'num_completions_v2': 0,
        }
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, self.EXP_VERSION, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.state_name: state_stats_dict})
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=2, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={'b': state_stats_dict})

    def test_standard_operation(self):
        self.run_one_off_job()

        model_id = stats_models.ExplorationStatsModel.get_entity_id(
            self.EXP_ID, self.EXP_VERSION)
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping[self.state_name]
        self.assertEqual(state_stats['total_answers_count_v2'], 3)
        self.assertEqual(state_stats['useful_feedback_count_v2'], 2)

        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 2))
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping['b']
        self.assertEqual(state_stats['total_answers_count_v2'], 4)
        self.assertEqual(state_stats['useful_feedback_count_v2'], 3)

class RecomputeStateHitStatisticsTests(OneOffJobTestBase):
    ONE_OFF_JOB_CLASS = stats_jobs_one_off.RecomputeStatisticsOneOffJob
    EXP_ID = 'EXP_ID'
    # Start on version 2 as the update from version 1 to 2 is only to
    # setup states for testing.
    EXP_VERSION = 1
    SESSION_ID_1 = 'session_id_1'
    SESSION_ID_2 = 'session_id_2'

    def setUp(self):
        super(RecomputeStateHitStatisticsTests, self).setUp()
        self.exp = self.save_new_valid_exploration(self.EXP_ID, 'owner')
        self.state_name = self.exp.init_state_name

        change_list = []
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')
        change_list = [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_RENAME_STATE,
                'old_state_name': self.state_name,
                'new_state_name': 'b',
            })
        ]
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')

        stats_models.StateHitEventLogEntryModel(
            id='id1',
            event_type=feconf.EVENT_TYPE_STATE_HIT,
            exploration_id=self.EXP_ID,
            exploration_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_1,
            params={},
            play_type=feconf.PLAY_TYPE_NORMAL,
            event_schema_version=2).put()
        stats_models.StateHitEventLogEntryModel(
            id='id2',
            event_type=feconf.EVENT_TYPE_STATE_HIT,
            exploration_id=self.EXP_ID,
            exploration_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_1,
            params={},
            play_type=feconf.PLAY_TYPE_NORMAL,
            event_schema_version=2).put()
        stats_models.StateHitEventLogEntryModel(
            id='id3',
            event_type=feconf.EVENT_TYPE_STATE_HIT,
            exploration_id=self.EXP_ID,
            exploration_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_2,
            params={},
            play_type=feconf.PLAY_TYPE_NORMAL,
            event_schema_version=2).put()
        stats_models.StateHitEventLogEntryModel(
            id='id4',
            event_type=feconf.EVENT_TYPE_STATE_HIT,
            exploration_id=self.EXP_ID,
            exploration_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_2,
            params={},
            play_type=feconf.PLAY_TYPE_NORMAL,
            event_schema_version=1).put()
        stats_models.StateHitEventLogEntryModel(
            id='id5',
            event_type=feconf.EVENT_TYPE_STATE_HIT,
            exploration_id=self.EXP_ID,
            exploration_version=2,
            state_name='a',
            session_id=self.SESSION_ID_2,
            params={},
            play_type=feconf.PLAY_TYPE_NORMAL,
            event_schema_version=2).put()
        stats_models.StateHitEventLogEntryModel(
            id='id6',
            event_type=feconf.EVENT_TYPE_STATE_HIT,
            exploration_id=self.EXP_ID,
            exploration_version=3,
            state_name='b',
            session_id=self.SESSION_ID_2,
            params={},
            play_type=feconf.PLAY_TYPE_NORMAL,
            event_schema_version=2).put()

        state_stats_dict = {
            'total_answers_count_v1': 3,
            'total_answers_count_v2': 9,
            'useful_feedback_count_v1': 3,
            'useful_feedback_count_v2': 9,
            'total_hit_count_v1': 3,
            'total_hit_count_v2': 0,
            'first_hit_count_v1': 3,
            'first_hit_count_v2': 0,
            'num_times_solution_viewed_v2': 0,
            'num_completions_v1': 3,
            'num_completions_v2': 0,
        }
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=1, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={
                'a': state_stats_dict,
                self.state_name: state_stats_dict,
            })
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=2, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={
                'a': state_stats_dict,
                self.state_name: state_stats_dict,
            })
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=3, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={'a': state_stats_dict, 'b': state_stats_dict})

    def test_standard_operation(self):
        self.run_one_off_job()

        model_id = stats_models.ExplorationStatsModel.get_entity_id(
            self.EXP_ID, self.EXP_VERSION)
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping[self.state_name]
        self.assertEqual(state_stats['first_hit_count_v2'], 2)
        self.assertEqual(state_stats['total_hit_count_v2'], 3)

        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 3))
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping['b']
        self.assertEqual(state_stats['first_hit_count_v2'], 3)
        self.assertEqual(state_stats['total_hit_count_v2'], 4)

    def test_session_hitting_multiple_states(self):
        self.run_one_off_job()

        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 2))
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping['a']
        self.assertEqual(state_stats['first_hit_count_v2'], 1)
        self.assertEqual(state_stats['total_hit_count_v2'], 1)

class RecomputeSolutionHitStatisticsTests(OneOffJobTestBase):
    ONE_OFF_JOB_CLASS = stats_jobs_one_off.RecomputeStatisticsOneOffJob
    EXP_ID = 'EXP_ID'
    EXP_VERSION = 1
    SESSION_ID_1 = 'session_id_1'
    SESSION_ID_2 = 'session_id_2'
    SESSION_ID_3 = 'session_id_3'

    def setUp(self):
        super(RecomputeSolutionHitStatisticsTests, self).setUp()
        self.exp = self.save_new_valid_exploration(self.EXP_ID, 'owner')
        self.state_name = self.exp.init_state_name

        change_list = [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_RENAME_STATE,
                'old_state_name': self.state_name,
                'new_state_name': 'b',
            })
        ]
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')

        stats_models.SolutionHitEventLogEntryModel(
            id='id1',
            exp_id=self.EXP_ID,
            exp_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_1,
            time_spent_in_state_secs=1.0,
            event_schema_version=2).put()
        stats_models.SolutionHitEventLogEntryModel(
            id='id2',
            exp_id=self.EXP_ID,
            exp_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_1,
            time_spent_in_state_secs=1.0,
            event_schema_version=2).put()
        stats_models.SolutionHitEventLogEntryModel(
            id='id3',
            exp_id=self.EXP_ID,
            exp_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_2,
            time_spent_in_state_secs=1.0,
            event_schema_version=2).put()
        stats_models.SolutionHitEventLogEntryModel(
            id='id4',
            exp_id=self.EXP_ID,
            exp_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_3,
            time_spent_in_state_secs=1.0,
            event_schema_version=2).put()
        stats_models.SolutionHitEventLogEntryModel(
            id='id1',
            exp_id=self.EXP_ID,
            exp_version=self.EXP_VERSION,
            state_name=self.state_name,
            session_id=self.SESSION_ID_1,
            time_spent_in_state_secs=1.0,
            event_schema_version=1).put()
        stats_models.SolutionHitEventLogEntryModel(
            id='id5',
            exp_id=self.EXP_ID,
            exp_version=2,
            state_name='b',
            session_id=self.SESSION_ID_3,
            time_spent_in_state_secs=1.0,
            event_schema_version=2).put()

        state_stats_dict = {
            'total_answers_count_v1': 3,
            'total_answers_count_v2': 9,
            'useful_feedback_count_v1': 3,
            'useful_feedback_count_v2': 9,
            'total_hit_count_v1': 3,
            'total_hit_count_v2': 0,
            'first_hit_count_v1': 3,
            'first_hit_count_v2': 0,
            'num_times_solution_viewed_v2': 0,
            'num_completions_v1': 3,
            'num_completions_v2': 0,
        }
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, self.EXP_VERSION, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.state_name: state_stats_dict})
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=2, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={'b': state_stats_dict})

    def test_standard_operation(self):
        self.run_one_off_job()

        model_id = stats_models.ExplorationStatsModel.get_entity_id(
            self.EXP_ID, self.EXP_VERSION)
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping[self.state_name]
        self.assertEqual(state_stats['num_times_solution_viewed_v2'], 3)

        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 2))
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping['b']
        self.assertEqual(state_stats['num_times_solution_viewed_v2'], 4)

class RecomputeActualStartStatisticsTests(OneOffJobTestBase):
    ONE_OFF_JOB_CLASS = stats_jobs_one_off.RecomputeStatisticsOneOffJob
    EXP_ID = 'EXP_ID'
    EXP_VERSION = 1
    STATE_NAME = 'state_1'

    def setUp(self):
        super(RecomputeActualStartStatisticsTests, self).setUp()
        self.save_new_default_exploration(self.EXP_ID, 'owner')

        change_list = []
        # Update exploration to version 3.
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')

        stats_models.ExplorationActualStartEventLogEntryModel(
            id='id1',
            exp_id=self.EXP_ID,
            exp_version=self.EXP_VERSION,
            state_name=self.STATE_NAME,
            session_id='session_id_1',
            event_schema_version=2).put()
        stats_models.ExplorationActualStartEventLogEntryModel(
            id='id2',
            exp_id=self.EXP_ID,
            exp_version=self.EXP_VERSION,
            state_name=self.STATE_NAME,
            session_id='session_id_2',
            event_schema_version=2).put()
        stats_models.ExplorationActualStartEventLogEntryModel(
            id='id3',
            exp_id=self.EXP_ID,
            exp_version=self.EXP_VERSION,
            state_name=self.STATE_NAME,
            session_id='session_id_3',
            event_schema_version=1).put()
        stats_models.ExplorationActualStartEventLogEntryModel(
            id='id4',
            exp_id=self.EXP_ID,
            exp_version=2,
            state_name=self.STATE_NAME,
            session_id='session_id_4',
            event_schema_version=2).put()

        state_stats_dict = {
            'total_answers_count_v1': 3,
            'total_answers_count_v2': 9,
            'useful_feedback_count_v1': 3,
            'useful_feedback_count_v2': 9,
            'total_hit_count_v1': 3,
            'total_hit_count_v2': 0,
            'first_hit_count_v1': 3,
            'first_hit_count_v2': 0,
            'num_times_solution_viewed_v2': 0,
            'num_completions_v1': 3,
            'num_completions_v2': 0,
        }
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, self.EXP_VERSION, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.STATE_NAME: state_stats_dict})
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=2, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.STATE_NAME: state_stats_dict})
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=3, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.STATE_NAME: state_stats_dict})

    def test_standard_operation(self):
        self.run_one_off_job()

        model_id = stats_models.ExplorationStatsModel.get_entity_id(
            self.EXP_ID, self.EXP_VERSION)
        model = stats_models.ExplorationStatsModel.get(model_id)
        self.assertEqual(model.num_actual_starts_v2, 2)
        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 2))
        model = stats_models.ExplorationStatsModel.get(model_id)
        self.assertEqual(model.num_actual_starts_v2, 3)

    def test_recompute_for_version_with_no_events(self):
        self.run_one_off_job()

        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 3))
        model = stats_models.ExplorationStatsModel.get(model_id)
        self.assertEqual(model.num_actual_starts_v2, 3)

    def test_job_with_no_exploration_version(self):
        model1 = stats_models.ExplorationActualStartEventLogEntryModel.get(
            'id1')
        model2 = stats_models.ExplorationActualStartEventLogEntryModel.get(
            'id2')
        model3 = stats_models.ExplorationActualStartEventLogEntryModel.get(
            'id3')
        model4 = stats_models.ExplorationActualStartEventLogEntryModel.get(
            'id4')
        stats_models.ExplorationActualStartEventLogEntryModel.delete_multi(
            [model1, model2, model3, model4])
        stats_models.ExplorationActualStartEventLogEntryModel(
            id='id',
            exp_id=self.EXP_ID,
            exp_version=None,
            state_name=self.STATE_NAME,
            session_id='session_id_4',
            event_schema_version=2).put()
        created_on = stats_models.ExplorationActualStartEventLogEntryModel.get(
            'id').created_on

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'None version for EXP_ID actual_start state_1 id %s\']'
             % created_on])

    def test_job_with_invalid_exploration_id(self):
        stats_models.ExplorationActualStartEventLogEntryModel(
            id='id',
            exp_id='invalid_exp_id',
            exp_version=self.EXP_VERSION,
            state_name=self.STATE_NAME,
            session_id='session_id_4',
            event_schema_version=2).put()

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'Exploration with exploration_id invalid_exp_id not found\']'])

    def test_job_with_non_existing_datastore_stats(self):
        model1 = stats_models.ExplorationStatsModel.get('%s.2' % self.EXP_ID)
        model2 = stats_models.ExplorationStatsModel.get('%s.3' % self.EXP_ID)
        stats_models.ExplorationStatsModel.delete_multi([model1, model2])

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'ERROR in retrieving datastore stats. They do not exist for: '
             'exp_id: EXP_ID, exp_version: 2\']'])

class RecomputeCompleteEventStatisticsTests(OneOffJobTestBase):
    ONE_OFF_JOB_CLASS = stats_jobs_one_off.RecomputeStatisticsOneOffJob
    EXP_ID = 'EXP_ID'
    EXP_VERSION = 1
    STATE_NAME = 'state_1'

    def setUp(self):
        super(RecomputeCompleteEventStatisticsTests, self).setUp()
        self.save_new_default_exploration(self.EXP_ID, 'owner')

        change_list = []
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')

        stats_models.CompleteExplorationEventLogEntryModel(
            id='id1',
            event_type=feconf.EVENT_TYPE_COMPLETE_EXPLORATION,
            exploration_id=self.EXP_ID,
            exploration_version=self.EXP_VERSION,
            state_name=self.STATE_NAME,
            session_id='session_id_1',
            client_time_spent_in_secs=1.0,
            params={},
            play_type=feconf.PLAY_TYPE_NORMAL,
            event_schema_version=2).put()
        stats_models.CompleteExplorationEventLogEntryModel(
            id='id2',
            event_type=feconf.EVENT_TYPE_COMPLETE_EXPLORATION,
            exploration_id=self.EXP_ID,
            exploration_version=self.EXP_VERSION,
            state_name=self.STATE_NAME,
            session_id='session_id_2',
            client_time_spent_in_secs=1.0,
            params={},
            play_type=feconf.PLAY_TYPE_NORMAL,
            event_schema_version=2).put()
        stats_models.CompleteExplorationEventLogEntryModel(
            id='id3',
            event_type=feconf.EVENT_TYPE_COMPLETE_EXPLORATION,
            exploration_id=self.EXP_ID,
            exploration_version=2,
            state_name=self.STATE_NAME,
            session_id='session_id_3',
            client_time_spent_in_secs=1.0,
            params={},
            play_type=feconf.PLAY_TYPE_NORMAL,
            event_schema_version=2).put()

        state_stats_dict = {
            'total_answers_count_v1': 3,
            'total_answers_count_v2': 9,
            'useful_feedback_count_v1': 3,
            'useful_feedback_count_v2': 9,
            'total_hit_count_v1': 3,
            'total_hit_count_v2': 0,
            'first_hit_count_v1': 3,
            'first_hit_count_v2': 0,
            'num_times_solution_viewed_v2': 0,
            'num_completions_v1': 3,
            'num_completions_v2': 0,
        }
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, self.EXP_VERSION, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.STATE_NAME: state_stats_dict})
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=2, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.STATE_NAME: state_stats_dict})

    def test_standard_operation(self):
        self.run_one_off_job()

        model_id = stats_models.ExplorationStatsModel.get_entity_id(
            self.EXP_ID, self.EXP_VERSION)
        model = stats_models.ExplorationStatsModel.get(model_id)
        self.assertEqual(model.num_completions_v2, 2)

        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 2))
        model = stats_models.ExplorationStatsModel.get(model_id)
        self.assertEqual(model.num_completions_v2, 3)

class StatisticsAuditTests(OneOffJobTestBase):

    ONE_OFF_JOB_CLASS = stats_jobs_one_off.StatisticsAudit
    EXP_ID = 'EXP_ID'
    EXP_VERSION = 1
    STATE_NAME = 'state_1'

    def setUp(self):
        super(StatisticsAuditTests, self).setUp()

        state_count = {
            'state_name': {
                'first_entry_count': 1,
                'total_entry_count': 1,
                'no_answer_count': 1
            }
        }
        stats_models.ExplorationAnnotationsModel.create(
            'exp_id1', '1', 0, 0, state_count)
        stats_models.StateCounterModel.get_or_create('exp_id1', 'state_name')

    def test_statistics_audit_with_negative_start_count(self):
        model1 = stats_models.ExplorationAnnotationsModel.get('exp_id1:1')
        model1.num_starts = -2
        model1.put()
        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'Negative start count: exp_id:exp_id1 version:1 starts:-2\']',
             '[u\'Completions > starts: exp_id:exp_id1 version:1 0>-2\']',
             '[u\'Non-all != all for starts: exp_id:exp_id1 sum: -2 all: 0\']'])

    def test_statistics_audit_with_negative_completion_count(self):
        model1 = stats_models.ExplorationAnnotationsModel.get('exp_id1:1')
        model1.num_completions = -2
        model1.put()
        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'Negative completion count: exp_id:exp_id1 version:1 '
             'completions:-2\']',
             '[u\'Non-all != all for completions: exp_id:exp_id1 sum: -2 '
             'all: 0\']'])

    def test_statistics_audit_with_version_all(self):
        model1 = stats_models.ExplorationAnnotationsModel.get('exp_id1:1')
        model1.version = stats_jobs_continuous.VERSION_ALL
        model1.put()

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'state hit count not same exp_id:exp_id1 state:state_name, '
             'all:1 sum: null\']'])

    def test_statistics_audit_with_negative_first_entry_count(self):
        model1 = stats_models.StateCounterModel.get_or_create(
            'exp_id1', 'state_name')
        model1.first_entry_count = -1
        model1.put()

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u"Less than 0: Key('
             '\'StateCounterModel\', \'exp_id1.state_name\') -1"]'])

class StatisticsAuditVTwoTests(OneOffJobTestBase):

    ONE_OFF_JOB_CLASS = stats_jobs_one_off.StatisticsAuditV2
    EXP_ID = 'EXP_ID'
    EXP_VERSION = 1
    STATE_NAME = 'state_1'

    def setUp(self):
        super(StatisticsAuditVTwoTests, self).setUp()

        state_stats_dict = {
            'total_answers_count_v1': 3,
            'total_answers_count_v2': 9,
            'useful_feedback_count_v1': 3,
            'useful_feedback_count_v2': 1,
            'total_hit_count_v1': 3,
            'total_hit_count_v2': 1,
            'first_hit_count_v1': 3,
            'first_hit_count_v2': 1,
            'num_times_solution_viewed_v2': 1,
            'num_completions_v1': 3,
            'num_completions_v2': 1,
        }
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, self.EXP_VERSION, num_starts_v1=2, num_starts_v2=2,
            num_actual_starts_v1=1, num_actual_starts_v2=1,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.STATE_NAME: state_stats_dict})

    def test_statistics_audit_v2_with_invalid_completions_count(self):
        model1 = stats_models.ExplorationStatsModel.get_model(
            self.EXP_ID, self.EXP_VERSION)
        model1.num_completions_v2 = 3
        model1.put()

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'Completions > starts: exp_id:EXP_ID version:1 3 > 2\']',
             '[u\'Completions > actual starts: exp_id:EXP_ID version:1 '
             '3 > 1\']'])

    def test_statistics_audit_v2_with_invalid_actual_starts_count(self):
        model1 = stats_models.ExplorationStatsModel.get_model(
            self.EXP_ID, self.EXP_VERSION)
        model1.num_actual_starts_v2 = 3
        model1.put()

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'Actual starts > starts: exp_id:EXP_ID version:1 3 > 2\']'])

    def test_statistics_audit_v2_with_invalid_useful_feedback_count(self):
        state_stats_dict = {
            'total_answers_count_v1': 3,
            'total_answers_count_v2': 9,
            'useful_feedback_count_v1': 3,
            'useful_feedback_count_v2': 10,
            'total_hit_count_v1': 3,
            'total_hit_count_v2': 1,
            'first_hit_count_v1': 3,
            'first_hit_count_v2': 1,
            'num_times_solution_viewed_v2': 1,
            'num_completions_v1': 3,
            'num_completions_v2': 1,
        }

        model1 = stats_models.ExplorationStatsModel.get_model(
            self.EXP_ID, self.EXP_VERSION)
        model1.state_stats_mapping = {self.STATE_NAME: state_stats_dict}
        model1.put()

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'Total answers < Answers with useful feedback: exp_id:EXP_ID '
             'version:1 state:state_1 9 > 10\']'])

    def test_statistics_audit_v2_with_negative_total_hit_count(self):
        state_stats_dict = {
            'total_answers_count_v1': 3,
            'total_answers_count_v2': 9,
            'useful_feedback_count_v1': 3,
            'useful_feedback_count_v2': 1,
            'total_hit_count_v1': 3,
            'total_hit_count_v2': -1,
            'first_hit_count_v1': 3,
            'first_hit_count_v2': 1,
            'num_times_solution_viewed_v2': 1,
            'num_completions_v1': 3,
            'num_completions_v2': 1,
        }

        model1 = stats_models.ExplorationStatsModel.get_model(
            self.EXP_ID, self.EXP_VERSION)
        model1.state_stats_mapping = {self.STATE_NAME: state_stats_dict}
        model1.put()

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'Solution count > Total state hits: exp_id:EXP_ID version:1 '
             'state:state_1 1 > -1\']',
             '[u\'Total state hits < First state hits: exp_id:EXP_ID version:1 '
             'state:state_1 -1 > 1\']',
             '[u\'Total state hits < Total state completions: exp_id:EXP_ID '
             'version:1 state:state_1 -1 > 1\']',
             'Negative count: exp_id:EXP_ID version:1 state:state_1 '
             'total_hit_count_v2:-1'])

class StatisticsAuditVOneTests(OneOffJobTestBase):

    ONE_OFF_JOB_CLASS = stats_jobs_one_off.StatisticsAuditV1
    EXP_ID = 'EXP_ID'
    EXP_VERSION = 1
    STATE_NAME = 'state_1'

    def setUp(self):
        super(StatisticsAuditVOneTests, self).setUp()

        state_stats_dict = {
            'total_answers_count_v1': 9,
            'total_answers_count_v2': 9,
            'useful_feedback_count_v1': 1,
            'useful_feedback_count_v2': 1,
            'total_hit_count_v1': 1,
            'total_hit_count_v2': 1,
            'first_hit_count_v1': 1,
            'first_hit_count_v2': 1,
            'num_times_solution_viewed_v2': 1,
            'num_completions_v1': 1,
            'num_completions_v2': 1,
        }
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, self.EXP_VERSION, num_starts_v1=2, num_starts_v2=2,
            num_actual_starts_v1=1, num_actual_starts_v2=1,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.STATE_NAME: state_stats_dict})

    def test_statistics_audit_v1_with_invalid_completions_count(self):
        model1 = stats_models.ExplorationStatsModel.get_model(
            self.EXP_ID, self.EXP_VERSION)
        model1.num_completions_v1 = 3
        model1.put()

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'Completions > starts: exp_id:EXP_ID version:1 3 > 2\']',
             '[u\'Completions > actual starts: exp_id:EXP_ID version:1 '
             '3 > 1\']'])

    def test_statistics_audit_v1_with_invalid_actual_starts_count(self):
        model1 = stats_models.ExplorationStatsModel.get_model(
            self.EXP_ID, self.EXP_VERSION)
        model1.num_actual_starts_v1 = 3
        model1.put()

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'Actual starts > starts: exp_id:EXP_ID version:1 3 > 2\']'])

    def test_statistics_audit_v1_with_invalid_useful_feedback_count(self):
        state_stats_dict = {
            'total_answers_count_v1': 9,
            'total_answers_count_v2': 9,
            'useful_feedback_count_v1': 10,
            'useful_feedback_count_v2': 1,
            'total_hit_count_v1': 1,
            'total_hit_count_v2': 1,
            'first_hit_count_v1': 1,
            'first_hit_count_v2': 1,
            'num_times_solution_viewed_v2': 1,
            'num_completions_v1': 1,
            'num_completions_v2': 1,
        }

        model1 = stats_models.ExplorationStatsModel.get_model(
            self.EXP_ID, self.EXP_VERSION)
        model1.state_stats_mapping = {self.STATE_NAME: state_stats_dict}
        model1.put()

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'Total answers < Answers with useful feedback: exp_id:EXP_ID '
             'version:1 state:state_1 9 > 10\']'])

    def test_statistics_audit_v1_with_negative_total_hit_count(self):
        state_stats_dict = {
            'total_answers_count_v1': 9,
            'total_answers_count_v2': 9,
            'useful_feedback_count_v1': 1,
            'useful_feedback_count_v2': 1,
            'total_hit_count_v1': -1,
            'total_hit_count_v2': 1,
            'first_hit_count_v1': 1,
            'first_hit_count_v2': 1,
            'num_times_solution_viewed_v2': 1,
            'num_completions_v1': 1,
            'num_completions_v2': 1,
        }

        model1 = stats_models.ExplorationStatsModel.get_model(
            self.EXP_ID, self.EXP_VERSION)
        model1.state_stats_mapping = {self.STATE_NAME: state_stats_dict}
        model1.put()

        job_output = self.run_one_off_job()
        self.assertEqual(
            job_output,
            ['[u\'Total state hits < First state hits: exp_id:EXP_ID version:1 '
             'state:state_1 -1 > 1\']',
             '[u\'Total state hits < Total state completions: exp_id:EXP_ID '
             'version:1 state:state_1 -1 > 1\']',
             'Negative count: exp_id:EXP_ID version:1 state:state_1 '
             'total_hit_count_v1:-1'])

class RecomputeStatisticsValidationCopyOneOffJobTests(OneOffJobTestBase):

    ONE_OFF_JOB_CLASS = (
        stats_jobs_one_off.RecomputeStatisticsValidationCopyOneOffJob)
    EXP_ID = 'EXP_ID'
    EXP_VERSION = 1
    STATE_NAME = 'state_1'

    def setUp(self):
        super(RecomputeStatisticsValidationCopyOneOffJobTests, self).setUp()
        self.exp = self.save_new_valid_exploration(self.EXP_ID, 'owner_id')

        stats_models.StateCompleteEventLogEntryModel(
            id='id0',
            exp_id=self.EXP_ID,
            exp_version=1,
            state_name=self.exp.init_state_name,
            session_id='session_id_1',
            time_spent_in_state_secs=1.0,
            event_schema_version=2).put()
        stats_models.StateCompleteEventLogEntryModel(
            id='id1',
            exp_id=self.EXP_ID,
            exp_version=None,
            state_name=self.exp.init_state_name,
            session_id='session_id_2',
            time_spent_in_state_secs=1.0,
            event_schema_version=2).put()
        stats_models.StateCompleteEventLogEntryModel(
            id='id2',
            exp_id=self.EXP_ID,
            exp_version=1,
            state_name=self.exp.init_state_name,
            session_id='session_id_3',
            time_spent_in_state_secs=1.0,
            event_schema_version=1).put()

        stats_models.StartExplorationEventLogEntryModel.create(
            self.EXP_ID, 1, self.exp.init_state_name, 'session_id1', {},
            feconf.PLAY_TYPE_NORMAL)

        state_stats_dict = {
            'total_answers_count_v1': 3,
            'total_answers_count_v2': 0,
            'useful_feedback_count_v1': 3,
            'useful_feedback_count_v2': 0,
            'total_hit_count_v1': 3,
            'total_hit_count_v2': 0,
            'first_hit_count_v1': 3,
            'first_hit_count_v2': 0,
            'num_times_solution_viewed_v2': 0,
            'num_completions_v1': 3,
            'num_completions_v2': 9,
        }
        stats_models.ExplorationStatsModel.create(
            self.EXP_ID, exp_version=1, num_starts_v1=0, num_starts_v2=0,
            num_actual_starts_v1=0, num_actual_starts_v2=0,
            num_completions_v1=0, num_completions_v2=0,
            state_stats_mapping={self.exp.init_state_name: state_stats_dict})

    def test_recompute_statistics_validation_copy_one_off_job(self):
        self.run_one_off_job()

        model_id = (
            stats_models.ExplorationStatsModel.get_entity_id(self.EXP_ID, 1))
        model = stats_models.ExplorationStatsModel.get(model_id)
        state_stats = model.state_stats_mapping[self.exp.init_state_name]

        self.assertEqual(state_stats['num_completions_v2'], 9)

class TestRegenerateMissingStatsModels(OneOffJobTestBase):
    """Tests the regeneration of missing stats models."""
    ONE_OFF_JOB_CLASS = stats_jobs_one_off.RegenerateMissingStatsModels

    def setUp(self):
        super(TestRegenerateMissingStatsModels, self).setUp()
        self.EXP_ID = 'EXP_ID'
        self.exp = self.save_new_valid_exploration(self.EXP_ID, 'owner_id')
        self.state_name = self.exp.init_state_name

        change_list = []
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')
        change_list = [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_RENAME_STATE,
                'old_state_name': self.state_name,
                'new_state_name': 'b',
            })
        ]
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')

        exp_services.revert_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, 3, 2)

        change_list = [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_RENAME_STATE,
                'old_state_name': self.state_name,
                'new_state_name': 'b',
            })
        ]
        exp_services.update_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID, change_list, '')

    def test_job_successfully_regenerates_deleted_model(self):
        self.exp = exp_fetchers.get_exploration_by_id(self.EXP_ID)
        self.assertEqual(self.exp.version, 5)

        model_id = stats_models.ExplorationStatsModel.get_entity_id(
            self.EXP_ID, 4)
        model = stats_models.ExplorationStatsModel.get(model_id)
        model.delete()

        output = self.run_one_off_job()
        model_id = stats_models.ExplorationStatsModel.get_entity_id(
            self.EXP_ID, 4)
        model = stats_models.ExplorationStatsModel.get(model_id)
        self.assertEqual(output, [u'[u\'Success\', 1]'])

    def test_job_yields_correct_message_when_missing_model_at_version_1(self):
        self.exp = exp_fetchers.get_exploration_by_id(self.EXP_ID)
        self.assertEqual(self.exp.version, 5)

        model_id = stats_models.ExplorationStatsModel.get_entity_id(
            self.EXP_ID, 1)
        model = stats_models.ExplorationStatsModel.get(model_id)
        model.delete()
        model_id = stats_models.ExplorationStatsModel.get_entity_id(
            self.EXP_ID, 2)
        model = stats_models.ExplorationStatsModel.get(model_id)
        model.delete()

        output = self.run_one_off_job()
        self.assertEqual(
            output, [u'[u\'Missing model at version 1\', [u\''
                     + self.EXP_ID + '\']]'])

    def test_job_yields_no_change_when_no_regeneration_is_needed(self):
        self.exp = exp_fetchers.get_exploration_by_id(self.EXP_ID)
        self.assertEqual(self.exp.version, 5)

        output = self.run_one_off_job()
        self.assertEqual(output, [u'[u\'No change\', 1]'])

    def test_job_yields_correct_message_when_missing_model_at_non_revert_commit(
            self):
        self.exp = exp_fetchers.get_exploration_by_id(self.EXP_ID)
        self.assertEqual(self.exp.version, 5)

        model_id = stats_models.ExplorationStatsModel.get_entity_id(
            self.EXP_ID, 2)
        model = stats_models.ExplorationStatsModel.get(model_id)
        model.delete()

        output = self.run_one_off_job()
        self.assertEqual(
            output,
            [u'[u\'Missing model without revert commit\', [u\''
             + self.EXP_ID + '\']]'])


House bolting is one of the required steps for the earthquake retrofit program that was established by the state of California. Why house bolting? House bolting takes your existing home and foundation and requires the addition of bolts and/or brackets to connect the house to the foundation. A home that is securely fastened to the foundation is far more likely to survive an earthquake, or at least be dealt less damage than homes without house bolting. The number and type of bolts that are needed in your home will vary. When you contact us for an evaluation, we will determine what your home needs.
The state of California is vulnerable to losing millions of dollars when major earthquakes strike. Therefore, they established a retrofit program that lets homeowners receive up to $3,000 towards a complete retrofit of their property. Home bolting is one requirement of this program and, in order to qualify, homeowners must apply for eligibility while also using a certified and approved retrofit company. We meet all the requirements and can help homeowners with the process of getting approved for the program. Start your savings by calling us for a home bolting.
A house bolting is just like it sounds. We will bolt your house down. Isn't it already bolted down? Yes, it is slightly secured to the foundation. However, the bolts and bolt pattern, that are used in the initial build, are not enough to withstand the seismic activity that develop from an earthquake. Our bolts are much larger and are often accompanied with brackets that are designed to flex with your home. The initial home building bolts are not able to flex, so will be vulnerable to shearing. Also, the bolt pattern is usually too spaced out to provide adequate support.
Yes, you can choose to ignore the problem and take the gamble that your house is properly secured. However, we encourage all of customers to spend some time looking at photos on the internet of homes that are bolted, and which are not. Bolted homes have a much higher probability of surviving severe earthquakes with only minor damage. Even minor earthquakes are able to shear small bolts as the swaying home puts too much pressure on them. We don't want to see this happen to you, so hope that you don't ignore the problem.
The short answer, it is extremely reliable. It is difficult to quantitively measure the effectiveness of home bolting, but history has a way of providing us with plenty of photos for understanding just how effective house bolting is. Again, there are a myriad of images on the internet which can attest to the effectiveness of home bolting reliability. Additionally, the state of California wouldn't require it as part of their retrofit program if it wasn't considered a crucial aspect of home stabilization for earthquakes. Let us come do your home bolting and you will have a house that is immediately more equipped for earthquakes.


On this week's show, I had the pleasure of speaking with Ashkan Karbasfrooshan, CEO @ WatchMojo. Ash is one of the most successful entrepreneurs I know, yet he doesn't flaunt it. Ash is the Ernst & Young 2015 Entrepreneur of the Year winner in the Media & Entertainment Category for the Quebec region.
– His previous company sold to News Corp.
WatchMojo is a Top 10 YouTube channel globally when measured by views (over 250M), and a top 5 channel when measured by minutes of content watched (over 1.6B minutes in fact).
Ash was very generous with this time and insights. He's incredibly humble despite having built one of the most successful brands on YouTube (of all time!). I hope that you enjoy the conversation.


text:
Charlotte Banfield English Adaptive Surf Champion
Charlotte Banfield, a regular in the Newquay surf, was crowned Women's Open Champion at The Dryrobe English Adaptive Surfing Open, which took place last week at The Wave in Bristol.
The competition, organised by Surfing England, was an inspirational event, with international as well as English & Welsh surfers taking part and pushing the boundaries of Adaptive Surfing.
Charlotte, 23, who trains in Newquay with Mike Young from Escape Surf School, is a previous holder of the Women's Open title, having won it in 2019. She competed in 2 divisions: The Women's Open, which included a field of standing, prone and assisted surfers. She also competed in the Standing Open, a division open to all standing surfers with disabilities.
Charlotte showed great determination and courage to take the win, with some fantastic scores on both the Left and Right at The Wave, an inland surfing destination in Bristol.
She told us "I really enjoy surfing at The Wave. The consistency of the waves means I can focus on particular manoeuvres and keep practising them. It's different in a competition environment though, as there are other pressures and lots of people watching! I was very happy with my scores, especially on the right, as in previous practise sessions I've found it quite tricky"
Charlotte, who suffers from Cerebral Palsy, Epilepsy, Dystonia & Autism, as well as other health conditions, is not a stranger to Adaptive & Para-Surfing Competitions, having competed on both a national and international stage since 2017. In 2020, she competed in the ISA Ampsurf World Para Surfing Championships where she brought home a Silver Medal for Team England.
She is also preparing for 2 more para surfing / adaptive surfing events to be held in 2021. First it's the US Open Adaptive Surfing Championships, from the 9th – 12th September, then the Nissan Super Girl Surf Pro from 17th September – both held in Oceanside California.
Later in the year, although yet to be announced is the big one: the ISA World Para Surfing Championship.
We caught up with Mike Young, Charlotte's Surf Coach and Owner of Escape Surf School "Charlotte really enjoys competing, it gives her a focus, and helps her deal with the issues she experiences due to her health conditions. Charlotte hadn't surfed a huge amount in the build up to this competition, so I was super proud of her when she nailed all her heats and dropped some big scores. We will now be working towards the events in America in later in the year"
Charlotte has come a long way in her surfing journey. 10 years ago she was afraid of the water, however after enrolling on a Surf Therapy Course, delivered by The Wave Project, she has gone from strength to strength.
Charlotte told us "Surfing has had a hugely positive affect on my life. Prior to my experience with the Wave Project, I was withdrawn, anxious & suffered from depression. Surfing has allowed me to overcome these challenges, to gain confidence and take part in disability sporting events".
"It was really great to compete again and to catch up with all the adaptive surfers as well as the Surfing England crew and all the helpers and volunteers. We are like a big family"
"I'd like to thank my sponsors, friends and supporters, as well as Surfing England and all the sponsors of the event. It's great to see adaptive surfing getting bigger and better in the UK"
Here's the contest results
Men's Open 1 Adi Klang 2 Llywelyn 'Sponge' Williams 3 Pegleg Bennett 4 Martin Pollock
Women's Open 1 Charlotte Banfield 2 Natasha Davies 3 Juliette Friedlander 4 Katie Richards
Assisted 1 Mark Hagger 2 Juliette Friedlander 3 Mark Bowra 4 Natasha Davies
Sitting/Kneeling 1 Llywelyn 'Sponge' Williams 2 Martin Pollock 3 Isaac Heaher
Standing 1 Adi Klang 2 Nachman Yariv Balaulu 3 Pegleg Bennett 4 Louis Sutton
Click here for full contest report.
Follow Charlotte:
PreviousWSL Announces 2021 Challenger Series Schedule
NextSmoothie Bowls Inspiration

redpajama_set_name: RedPajamaCommonCrawl


On October 1st, the American people saw the effects of stubbornness come to fruition. Many people will not be going to work for an indefinite period. Under these circumstances, it is time to ask ourselves if we have turned our political parties into idols.
As Catholics, we hold strong beliefs on different issues, and it is right to hold fast to our beliefs. Within a two-party system, it is easy to take one or two key issues and find the party that speaks to those points in a way that is in line with Church teaching. But there is a fine line between supporting a party and idolizing it. There is nothing wrong with donating money, putting up bumper stickers, placing signs in our yards, and campaigning for a party we support. That falls easily into the realm of "rendering unto Caesar what is Caesar's." However, we cross over into idolatry when we decide that the party of our choosing is the only party with anything to offer, thereby assuming that the other party is evil and can do no right. That is, when we only look at the points of agreement with our party on a few narrow issues and disregard the rest, we've lost sight of the importance of choosing a party for faith-related reasons, and created an idol. The mere presence of liberal and conservative media is a testament to Americans' idolization of their chosen party. Some of the news media personalities even have their own followings. People align their views to those of the writer, radio host, or television reporter before they stop and align them with the teachings of the Church. These views are strongly tied to one party or the other. Political parties are, it seems, the new idols of our day.
It's easy to do this on specific issues as well. The present government shutdown is the result of holding up a vote over a few key issues, most notably, in order to eliminate the Patient Protection and Affordable Care Act, also known as "Obamacare." When issues are so important that the government is shut down, those issues have then become idols. Evidently, it's now acceptable to take people out of work and hurt the reputation of our country out of stubbornness and an unwillingness to accept a legally passed piece of legislation. This stubbornness is not confined to one party or one issue. On both sides of the aisle, the devotion of lawmakers to these idols is remarkable.
Now is the time to crush the idols of our time. Now is the time to turn back to the heart of our beliefs. Now is the time to isolate issues and reflect upon them in light of Church teachings. Now is the time to communicate our beliefs to our lawmakers and party leaders focusing on the issues one at a time in accordance with our faith. Now is the time to break free from the chains of excessive partisanship and minister directly to God's people. Now is the time to use the words of Ronald Reagan when we address the barrier between our two parties: "Tear down this wall." Yes, now is the time to destroy our idols and work for the good of God's people, each and every one.
Where do we start? We can revisit issues in accordance with Catholic teaching, most specifically through the use of the USCCB's "Forming Catholic Consciences" document. We need to contemplate our reasons for choosing our alignments. Most importantly, we need to answer this question: to whom is my allegiance? Is it God, a political party, a media voice? We need to turn ourselves to the light and let the idols crumble.
This entry was posted in Blog and tagged Government Shutdown. Bookmark the permalink.


text:
A Reception on the Occasion of the National Day of Thailand 2022
On 1 December 2022, the Royal Thai Embassy and Permanent Mission of Thailand in Vienna organised a reception on the Occasion of the National Day of Thailand 2022 at the InterContinental Hotel in Vienna. The event was attended by approximately 550 guests from the Austrian public and private sectors, diplomatic corps, international organisations, foreign guests as well as members of the Thai community.
Ambassador Morakot Sriswasdi delivered her opening remarks by citing His Majesty the late King Bhumibol Adulyadej's commitment and devotion to all dimensions of development works , the Sufficiency Economy Philosophy as well as the Alternative Development. The Royal Projects have led to better quality of life and income generation to the peoples, environmental rehabilitation and illicit crop eradication which has been recognised by the United Nations Office on Drugs and Crime (UNODC) and other international bodies for its successes. Ambassador Sriswasdi also reflected on Thailand's constructive and cordial relations with Austria at all levels and in all dimensions, which have remained strong amidst the challenges.
Ambassador Maximilian Hennig, Chief of Protocol of the Austrian Foreign Ministry, gave his remarks as the guest of honour by commending the longstanding Austrian-Thai friendship which has demonstrated itself through the multifaceted cooperation between the two countries. He pointed out that the people-to-people relations have contributed to a strong foundation on which the friendship will further grow in the years to come.
The official ceremony was followed by a reception with the exhibition of Sufficiency Economy Philosophy. The guests are served with variety of Thai savory and sweet delicacies. Some food and beverage were supported by CPF Europe and Red Bull GmbH.


text:
Q: Printing an ArrayList from a different method Design a function whose responsibility is to display the prime number array nicely. It should display the contents of the array 10 per line. A combination of print and println would be needed. Display each
number in a field of width 7 (use printf).
Here is what I have, but I'm not sure if correct.
public  static void printArray(ArrayList<Integer> primes){
    System.out.printf("%7s", primes);
    if (prrimeCount % 10 == 0){
        System.out.println();
    }
}
}

A: System.out.printf("%7s", primes);

this will throw an exception primes is an Arraylist but %7s requires a String .

A: public  static void printArray(ArrayList<Integer> primes){
    // You need to have a counter as you iterate over the list
    int count = 1;
    // Integer is autoboxed into an int when interating over prime
    for(int prime : primes){
        // "%7d" instead of "%7s" as d is used for integer but s is used for strings
        System.out.printf("%7d ", prime);
        // count++ will icrement count after this statement is called
        if (count++ % 10 == 0){
            System.out.println();
        }
    }
}

redpajama_set_name: RedPajamaStackExchange


text:
Resolute announces $50 million in lumber investments
Resolute Forest Products declared a special cash dividend of $1.00 per share of common stock, payable on July 7 for holders of record at the close of business on June 28. The company also announced additional capital investments of $50 million in its wood products operations to support its continued growth, and it confirmed the repayment of all amounts outstanding under its revolving and term credit facilities, reducing debt by $180 million in the second quarter.
"The cash generated with our lumber platform in these strong lumber markets provides the opportunity to share benefits directly with shareholders," said Remi G. Lalonde, president and chief executive officer. "We're also enhancing long-term value by improving our company fundamentals with investments in high-return and quick payback lumber projects in three key regions for Resolute: the Abitibi in Quebec, Northwestern Ontario and the Southeast U.S. What's more, we've removed a material amount of debt from the balance sheet with the further $180 million repayment in Q2, leaving as our only remaining debt the $300 million of unsecured 4.875% senior notes due 2026, and our liquidity stands at around $850 million. As we improve the competitiveness of the strongest parts of our business, today's announcement will benefit shareholders, employees and a wide range of community and business partners. As demonstrated with this announcement, our intention is to maintain a balanced approach to capital allocation, using our free cash flow to generate value for shareholders and build a stronger company while driving economic activity in the communities where we operate."
The additional $50 million in wood products investments include:
– $22 million to modernize equipment at the Senneterre (Quebec) sawmill, which will enhance overall efficiency and productivity of the Abitibi regional operations;
– $13 million at the Thunder Bay / Fort William First Nation (Ontario) sawmill to increase capacity by up to 40 million board feet with new equipment and modifications to the fiber flow; and
– $15 million at the Glenwood (Arkansas) and Cross City (Florida) sawmills to support fiber optimization and overall efficiency. The Cross City investment will also increase capacity by up to 20 million board feet.
Each of these projects will serve to generate additional value from Resolute's wood products segment across market cycles. The improvements at the U.S. sawmills build on the previously announced investments associated with the restart of the El Dorado (Arkansas) sawmill. The company anticipates completing all the projects by the end of the third quarter of 2022. Accordingly, it is raising its 2021 capital investments guidance from $100 million to $125 million.
#Tags: lumberResolute Forest
Resolute sets target to reduce GHG emissions by 30%
Resolute Forest Products appoints of Remi Lalonde as President and CEO, succeeding Yves Laflamme
Performance BioFilaments to scale cellulose production at Resolute plant
Resolute completes acquisition of three sawmills
Resolute rejects Repap Resources' bid for Fort Frances mill, says it will proceed with redevelopment
Georgia-Pacific completes $100-million lumber production facility in Talladega, Alabama
Possible buyer for Shuttered Fort Frances Paper Mill
Softwood logs and softwood lumber imports to China have more than tripled in ten years


One of the best ways to get great results in your marketing is by repel some customers. Your marketing will be much more effective when you speak to ideal customer. Trying to speak to everyone is like talking to no one. I recently had to take measures to keep a certain creature from my patio, although this type of animal has a great appeal it was causing me more harm than good.


Jonang Culture centre which is within Dzamthang. From the junction, we drove 15Km to Jonang culture centre through a beautiful valley with full of typical Tibetan styled houses whch are extremely beautiful. There are three monasteries at Jonang culture centre which are Tsechu, Choeje and Sangnga Gompa. Among them, the oldest monastery today is Choeji and the assembly hall of Choeji is old and it has many old Thangka and Statues. In the upper part of Choeji, an old statue of Shakyamuni. Underneath the statue, you can go and do clockwise direction around the pillar under the statue in order to get blessing and get rid of once's bad karmic action. It is said that the statue is always not be able to be taken nice photos.
The three monasteries are all together and the most upper part or on the left side is Tsechu Gompa and the main statue is also Bhuddha Shykyamuni and an old statue of Dolpopa who is the main lineage of Jonangpa. The middle one is Choeji which is the oldest one. The last one or the biggest one is Sang Nga. It is a newly re-built monastery and there is a big tomb stupa of the monastery Lama who passed away. There are beautiful wall painting of Kalachakara and it's attendents, Bhuddha's life sotry. The forth floor chaple is dedicated to the great masters from different orders in Tibet. Fifth floor is the library and printing boards shelfs. The top floor is dedicated to a mandala of Tara. Next to this temple, there is a temple of a huge Maitreya Bhuddha. There are also a tantric college and philosophical college where they call Shedra and Drupdra nowadays. Joangpa order is said to be started from Dolpopa based on Bhudha Shakyamuni teachings. Dolpopa is the first lineage of Jonangpa and now they are 47th lineages. Dolpopa was born in Dolpo in Nepal. Jonangpa is mainly practising Tantra and they have a very deep source of Kalachakara practice and meditation. They are building a giant new mandala of Kalachakara which will be the world's largest Kalachakara Mandala.


where quality horse videos are made!
Carol Wood has extensive experience providing equine videos of various kinds for both advertising and educational purposes. She has created videos for sale horses, clinics, training and events. Carol, a cutter herself, is frequently at cutting events and is an NCHA certified videographer. Although Cowgirl Video is frequently at Cutting events, they also have experience in Reining, Reined Cow Horse, and Combined Driving Events.
Please take a moment to view some of Cowgirl Video's Samples.
If you would like to schedule video services, or perhaps order a video from a recent event, please use our Order Form or contact Carol at cwood518@hotmail.com or (209)304-4519.
©2013-2016 Cowgirl Video. All rights reserved.