## Bug description

We use the Posthog API to create ephemeral projects while developing our app locally. The script that creates these projects attempts to delete an existing project that matches the naming convention to prevent a pile of projects from being created. It was working fine for weeks but started to receive a 500 response consistently as of NOV 16, 2022. After encountering the 500 error, the project still appears in the front end but cannot be deleted there either.

## How to reproduce

0. Using NodeJS 16 and Axios@0.27.2
1. Create a project via the API `POST /projects`
2. Delete the project via the API `DELETE /projects/<id>`
3. _500 error_
4. Try to delete the project in the front-end
5. _500 error_

## Environment

- [x] PostHog Cloud
- [ ] self-hosted PostHog, version/commit: please provide

## Additional context

> Script logs

<img width="528" alt="Screen Shot 2022-11-17 at 10 16 26 AM" src="https://user-images.githubusercontent.com/5888427/202525627-f055f2fe-3237-4d76-820f-bc4c5158f3cb.png">

> Error in Chrome Dev Tools - Network Tab

<img width="1472" alt="Screen Shot 2022-11-17 at 10 17 26 AM" src="https://user-images.githubusercontent.com/5888427/202526222-b5467c0c-702a-4282-8578-5144bd31a54d.png">


<img width="1475" alt="Screen Shot 2022-11-17 at 10 17 38 AM" src="https://user-images.githubusercontent.com/5888427/202526242-6d6b4330-f55b-452a-8972-c12c68f4d8f0.png">

> Script Source Code - `setup-posthog-project.ts` _(pardon the roughness)_

```typescript
import axios from 'axios';
import { config } from 'dotenv';
import { expand } from 'dotenv-expand';
import { readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { inspect } from 'node:util';
import { PostHog } from 'posthog-node';
import PrettyError from 'pretty-error';

type EnvironmentVariables = {
  POSTHOG_PERSONAL_API_KEY: string;
  POSTHOG_PERSONAL_PROJECT: string;
  POSTHOG_REAL_PROJECT_ID: number;
};

type Paginated<T> = {
  count: number;
  next: null | string;
  results: T[];
};

type ProjectMinimal = {
  id: number;
  uuid: string;
  organization: string;
  api_token: string;
  name: string;
  completed_snippet_onboarding: boolean;
  ingested_event: boolean;
  is_demo: boolean;
  timezone: string;
  access_control: boolean;
  effective_membership_level: number;
};

type ProjectFull = ProjectMinimal & {
  app_urls: string[];
  slack_incoming_webhook: string;
  anonymize_ips: boolean;
  test_account_filters: Record<string, any>;
  test_account_filters_default_checked: boolean;
  path_cleaning_filters: Record<string, any>;
  timezone: string;
  data_attributes: Record<string, any>;
  person_display_name_properties: string[];
  correlation_config: Record<string, any>;
  session_recording_opt_in: boolean;
  access_control: boolean;
  primary_dashboard: number;
  live_events_columns: string[];
  recording_domains: string[];
};

type PropertyType = 'DateTime' | 'String' | 'Numeric' | 'Boolean';

type EventProperty = {
  id: string;
  name: string;
  description: string;
  tags: any[];
  is_numerical: boolean;
  is_event_property: string;
  property_type: PropertyType;
};

type Cohort = {
  id: number;
  name: string;
  description: string;
  deleted: boolean;
  groups: Record<string, any>;
  filters: Record<string, any>;
  is_calculating: boolean;
  last_calculation: string;
  errors_calculating: number;
  count: number;
  is_static: boolean;
};

type Experiment = {
  id: number;
  name: string;
  description: string;
  start_date: string;
  end_date: string;
  feature_flag_key: string;
  feature_flag: number;
  parameters: Record<string, any>;
  secondary_metrics: Record<string, any>;
  filters: Record<string, any>;
  archived: boolean;
};

type FeatureFlag = {
  id: number;
  name: string;
  key: string;
  filters: Record<string, any>;
  deleted: boolean;
  active: boolean;
  is_simple_flag: boolean;
  rollout_percentage: number;
  ensure_experience_continuity: boolean;
  experiment_set: number[];
};

function createProjectObject<
  T extends Omit<
    ProjectFull,
    | 'primary_dashboard'
    | Exclude<
        keyof ProjectMinimal,
        'name' | 'completed_snippet_onboarding' | 'timezone' | 'access_control'
      >
  >
>(payload: T): T {
  return payload;
}

const DEV_PROJECT_PREFIX = 'Webapp DEV - ';

async function main() {
  const pathToEnvFile = join(__dirname, '../../../../.env');

  console.log('Loading .env file');

  const beforeExpansion = config({
    path: pathToEnvFile
  });

  if (beforeExpansion.error) {
    throw beforeExpansion.error;
  }

  if (!beforeExpansion.parsed) {
    throw new Error('No `beforeExpansion.parsed` property found');
  }

  const INITIAL_VARS =
    beforeExpansion.parsed as unknown as EnvironmentVariables;

  const printableInitialVars = inspect(INITIAL_VARS, false, null, true);

  if (!INITIAL_VARS.POSTHOG_PERSONAL_API_KEY) {
    throw new Error(
      `The current '.env' file (before variable expansion) does not have a 'POSTHOG_PERSONAL_API_KEY' variable or it's empty. This variable should point to another variable "POSTHOG_DEV_API_KEY" that is set in your shell startup script or "~/.mayan/env". Are you sure you are in an "emulation" flow? \n${printableInitialVars}`
    );
  }

  if (!INITIAL_VARS.POSTHOG_PERSONAL_PROJECT) {
    throw new Error(
      `The current '.env' file (before variable expansion) does not have a 'POSTHOG_PERSONAL_PROJECT' variable or it's empty. This variable should point to another variable "POSTHOG_DEV_PROJECT" that is set in your shell startup script or "~/.mayan/env". Are you sure you are in an "emulation" flow? \n${printableInitialVars}`
    );
  }

  if (!INITIAL_VARS.POSTHOG_REAL_PROJECT_ID) {
    throw new Error(
      `The current '.env' file (before variable expansion) does not have a 'POSTHOG_REAL_PROJECT_ID' variable or it's empty. This variable should be an integer. Are you sure you are in an "emulation" flow? \n${printableInitialVars}`
    );
  }

  // console.log('initial `.env` contents = ', INITIAL_VARS);

  console.log('Expanding `.env` file');

  const afterExpansion = expand(beforeExpansion);

  if (afterExpansion.error) {
    throw afterExpansion.error;
  }

  if (!afterExpansion.parsed) {
    throw new Error('No `afterExpansion.parsed` property found');
  }

  const FINAL_VARS = afterExpansion.parsed as unknown as EnvironmentVariables;

  const printableFinalVars = inspect(FINAL_VARS, false, null, true);

  if (!FINAL_VARS.POSTHOG_PERSONAL_API_KEY) {
    throw new Error(
      `The current '.env' file (after variable expansion) does not have a 'POSTHOG_PERSONAL_API_KEY' variable or it's empty. This variable should point to another variable "POSTHOG_DEV_API_KEY" that is set in your shell startup script or "~/.mayan/env". Are you sure you are in an "emulation" flow? \n${printableFinalVars}`
    );
  }

  if (!FINAL_VARS.POSTHOG_PERSONAL_PROJECT) {
    throw new Error(
      `The current '.env' file (after variable expansion) does not have a 'POSTHOG_PERSONAL_PROJECT' variable or it's empty. This variable should point to another variable "POSTHOG_DEV_PROJECT" that is set in your shell startup script or "~/.mayan/env". Are you sure you are in an "emulation" flow? \n${printableFinalVars}`
    );
  }

  if (!FINAL_VARS.POSTHOG_REAL_PROJECT_ID) {
    throw new Error(
      `The current '.env' file (after variable expansion) does not have a 'POSTHOG_REAL_PROJECT_ID' variable or it's empty. This variable should be an integer. Are you sure you are in an "emulation" flow? \n${printableFinalVars}`
    );
  }

  // console.log('expanded `.env` contents = ', FINAL_VARS);

  const posthogApi = axios.create({
    baseURL: 'https://app.posthog.com/api',
    headers: {
      Authorization: `Bearer ${FINAL_VARS.POSTHOG_PERSONAL_API_KEY}`
    }
  });

  const paginationOptions = {
    params: {
      limit: 1000
    }
  };

  console.log('Fetching real project info');

  const { data: realProject } = await posthogApi.get<ProjectFull>(
    `/projects/${FINAL_VARS.POSTHOG_REAL_PROJECT_ID}`
  );

  // console.log('real project contents = ', realProject);

  console.log('Fetching real property definitions');

  const { data: propertyDefinitions } = await posthogApi.get<
    Paginated<EventProperty>
  >(`/projects/${realProject.id}/property_definitions`, paginationOptions);

  // console.dir(propertyDefinitions, {
  //   depth: null,
  //   colors: true
  // });

  console.log('Fetching real cohorts');

  const { data: cohorts } = await posthogApi.get<Paginated<Cohort>>(
    `/projects/${realProject.id}/cohorts`,
    paginationOptions
  );

  // console.log('real experiments contents = ', realProject);

  console.log('Fetching real experiments');

  const { data: experiments } = await posthogApi.get<Paginated<Experiment>>(
    `/projects/${realProject.id}/experiments`,
    paginationOptions
  );

  // console.log('real experiments contents = ', realProject);

  console.log('Fetching real feature flags');

  const { data: featureFlags } = await posthogApi.get<Paginated<FeatureFlag>>(
    `/projects/${realProject.id}/feature_flags`,
    paginationOptions
  );

  // console.log('real feature flags contents = ', realProject);

  console.log('Fetching project list');

  const { data } = await posthogApi.get<Paginated<ProjectMinimal>>(
    '/projects',
    paginationOptions
  );

  // console.log('Project list loaded; searching for existing dev project');

  if (data.count > 100) {
    throw new Error('More that 100 projects?!');
  }

  let existingProject: ProjectMinimal | undefined;
  const projectName = `${DEV_PROJECT_PREFIX}${FINAL_VARS.POSTHOG_PERSONAL_PROJECT}`;

  for (const item of data.results) {
    if (item.name === projectName) {
      existingProject = item;
      continue;
    }
  }

  if (existingProject) {
    console.log('Existing dev project found; cleaning up...');

    await posthogApi.delete(`/projects/${existingProject.id}`);

    console.log('Existing dev project deleted');
  } else {
    console.log('No existing dev project found so nothing to cleanup');
  }

  console.log(`Creating new project named: ${projectName}`);

  const { data: newProject } = await posthogApi.post<ProjectFull>(
    '/projects',
    createProjectObject({
      name: projectName,
      completed_snippet_onboarding: true,
      access_control: realProject.access_control,
      app_urls: realProject.app_urls,
      anonymize_ips: realProject.anonymize_ips,
      slack_incoming_webhook: realProject.slack_incoming_webhook,
      timezone: realProject.timezone,
      test_account_filters: realProject.test_account_filters,
      test_account_filters_default_checked:
        realProject.test_account_filters_default_checked,
      path_cleaning_filters: realProject.path_cleaning_filters,
      person_display_name_properties:
        realProject.person_display_name_properties,
      data_attributes: realProject.data_attributes,
      correlation_config: realProject.correlation_config,
      session_recording_opt_in: realProject.session_recording_opt_in,
      // primary_dashboard: realProject.primary_dashboard,
      live_events_columns: realProject.live_events_columns,
      recording_domains: realProject.recording_domains
    })
  );

  // await axios.post('https://app.posthog.com/capture', {
  //   api_key: newProject.api_token,
  //   event: 'Emulation Setup',
  //   properties: propertyDefinitions.results.map(({ name, property_type }) => {
  //     let stub: any;

  //     if (property_type === 'DateTime') {
  //       stub = new Date().toISOString();
  //     } else if (property_type === 'String') {
  //       stub = 'stub';
  //     } else if (property_type === 'Numeric') {
  //       stub = 1;
  //     } else if (property_type === 'Boolean') {
  //       stub = true;
  //     } else {
  //       throw new Error(`Unknown property type: ${property_type}`);
  //     }

  //     return [name, stub];
  //   })
  // });

  console.log('Creating cohorts');

  const cohortMapping: Record<number, number> = {};

  for (const cohort of cohorts.results) {
    if (cohort.deleted) {
      continue;
    }

    const { data: newCohort } = await posthogApi.post<Cohort>(
      `/projects/${newProject.id}/cohorts`,
      {
        name: cohort.name,
        description: cohort.description,
        groups: cohort.groups,
        filters: cohort.filters,
        is_static: cohort.is_static
      }
    );

    cohortMapping[cohort.id] = newCohort.id;
  }

  const experimentMapping: Record<number, number> = {};

  console.log('Creating experiments');

  for (const experiment of experiments.results) {
    if (
      new Date(experiment.end_date).getTime() < Date.now() ||
      experiment.archived
    ) {
      continue;
    }

    const { data: newExperiment } = await posthogApi.post<Experiment>(
      `/projects/${newProject.id}/experiments`,
      {
        name: experiment.name,
        description: experiment.description,
        start_date: experiment.start_date,
        end_date: experiment.end_date,
        feature_flag_key: experiment.feature_flag_key,
        parameters: experiment.parameters,
        secondary_metrics: experiment.secondary_metrics,
        filters: experiment.filters
      }
    );

    experimentMapping[experiment.id] = newExperiment.id;
  }

  console.log('Creating feature flags');

  const activeFeatureFlags: number[] = [];

  for (const featureFlag of featureFlags.results) {
    if (featureFlag.deleted) {
      continue;
    }

    const { data: newFeatureFlag } = await posthogApi.post<FeatureFlag>(
      `/projects/${newProject.id}/feature_flags`,
      {
        name: featureFlag.name,
        key: featureFlag.key,
        filters: Object.fromEntries(
          Object.entries(featureFlag.filters).map(([key, value]) => {
            let finalValue: any = value;

            if (key === 'groups') {
              finalValue = value.map(
                (group: {
                  properties: Array<{
                    key: string;
                    type: string;
                    value: any;
                    operator: any;
                  }>;
                }) => {
                  const newGroup = { ...group };

                  newGroup.properties = newGroup.properties.map((property) => {
                    const newProperty = { ...property };

                    if (newProperty.type === 'cohort') {
                      newProperty.value = cohortMapping[newProperty.value];
                    }

                    return newProperty;
                  });

                  return newGroup;
                }
              );
            }

            return [key, finalValue];
          })
        ),
        active: false,
        ensure_experience_continuity: featureFlag.ensure_experience_continuity
      }
    );

    if (featureFlag.active) {
      activeFeatureFlags.push(newFeatureFlag.id);
    }
  }

  const client = new PostHog(newProject.api_token);

  console.log('Creating events');

  client.identify({
    distinctId: 'local_dev_script',
    properties: {
      email: 'posthog.setup@mayan.co'
    }
  });

  client.capture({
    distinctId: 'local_dev_script',
    event: 'Emulation Setup'
  });

  await client.shutdownAsync();

  // This still doesn't work... The flag and in the active state (according to the UI) but it doesn't actually work
  console.log('Activating feature flags');

  for (const featureFlagId of activeFeatureFlags) {
    await posthogApi.patch(
      `/projects/${newProject.id}/feature_flags/${featureFlagId}`,
      {
        active: true
      }
    );
  }

  console.log('Reading raw `.env` file');

  const envFileContent = await readFile(pathToEnvFile, 'utf8');

  console.log('Updating `.env` file');

  const envFileUpdated = [
    envFileContent
      .trim()
      .replace(/^POSTHOG_PERSONAL_PROJECT_ID=\w+\s*$/gm, '')
      .replace(/^POSTHOG_PERSONAL_PROJECT_API_TOKEN=\w+\s*$/gm, '')
      .trim(),
    `POSTHOG_PERSONAL_PROJECT_ID=${newProject.id}`,
    `POSTHOG_PERSONAL_PROJECT_API_TOKEN=${newProject.api_token}`,
    ''
  ].join('\n');

  await writeFile(pathToEnvFile, envFileUpdated, 'utf8');

  console.log('Done');
}

const pe = new PrettyError();

function logError(err: any) {
  if (err instanceof Error === false) {
    err = new Error(err);
  }

  console.error(pe.render(err));

  if (axios.isAxiosError(err)) {
    const { code, name, message, status, response } = err;
    const { data } = response || {};

    console.error({
      status,
      code,
      name,
      message,
      data
    });
  }
}

try {
  main().catch((err) => {
    logError(err);
    process.exit(1);
  });
} catch (err) {
  logError(err);
  process.exit(1);
}

```

#### *Thank you* for your bug report – we love squashing them!

posthog/models/team/util.py
from datetime import timedelta
from typing import Any, List
from posthog.cache_utils import cache_for
from posthog.models.async_migration import is_async_migration_complete
from posthog.models.cohort import CohortPeople
from posthog.models.person import Person, PersonDistinctId
def delete_bulky_postgres_data(team_ids: List[int]):
    "Efficiently delete large tables for teams from postgres. Using normal CASCADE delete here can time out"
    _raw_delete(PersonDistinctId.objects.filter(team_id__in=team_ids))
    _raw_delete(CohortPeople.objects.filter(person__team_id__in=team_ids))
    _raw_delete(Person.objects.filter(team_id__in=team_ids))
def _raw_delete(queryset: Any):
    "Issues a single DELETE statement for the queryset"
    queryset._raw_delete(queryset.db)
can_enable_person_on_events = False
def person_on_events_ready() -> bool:
    global can_enable_person_on_events
    if can_enable_person_on_events:
        return True
    can_enable_person_on_events = _person_on_events_ready()
    return can_enable_person_on_events
@cache_for(timedelta(minutes=1))
def _person_on_events_ready() -> bool:
    return is_async_migration_complete("0007_persons_and_groups_on_events_backfill")