From a70f551d1791ad9d9eadd2f7fc03eac2a521db4c Mon Sep 17 00:00:00 2001 From: AbId KhAn Date: Wed, 10 Jan 2024 14:05:24 +0600 Subject: [PATCH 1/9] Fix env substitute issue in websocket docker setup (#3296) * fix websocket connection issue in docker makeplane/plane#3195 * fix websocket connection issue for local env with removing from the prod nginx conf template makeplane/plane#319 * fix env substitution issue of proxy_set_header makeplane/plane#3196 * review fixes --------- Co-authored-by: Manish Gupta --- docker-compose-local.yml | 3 --- nginx/env.sh | 2 ++ nginx/nginx.conf.dev | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docker-compose-local.yml b/docker-compose-local.yml index 4e1e3b39f..b0fb9da24 100644 --- a/docker-compose-local.yml +++ b/docker-compose-local.yml @@ -44,9 +44,6 @@ services: env_file: - .env environment: - POSTGRES_USER: ${PGUSER} - POSTGRES_DB: ${PGDATABASE} - POSTGRES_PASSWORD: ${PGPASSWORD} PGDATA: /var/lib/postgresql/data web: diff --git a/nginx/env.sh b/nginx/env.sh index 59e4a46a0..7db471eca 100644 --- a/nginx/env.sh +++ b/nginx/env.sh @@ -1,4 +1,6 @@ #!/bin/sh +export dollar="$" +export http_upgrade="http_upgrade" envsubst < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf exec nginx -g 'daemon off;' diff --git a/nginx/nginx.conf.dev b/nginx/nginx.conf.dev index 182fc4d83..f86c84aa8 100644 --- a/nginx/nginx.conf.dev +++ b/nginx/nginx.conf.dev @@ -19,7 +19,7 @@ http { location / { proxy_pass http://web:3000/; proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; + proxy_set_header Upgrade ${dollar}http_upgrade; proxy_set_header Connection "upgrade"; } From f58a00a4ab51758e0c36ed67610f40ff9aea62e0 Mon Sep 17 00:00:00 2001 From: Henit Chobisa Date: Fri, 12 Jan 2024 13:26:48 +0530 Subject: [PATCH 2/9] [FIX] Pages Malfunctioning on Load and Recent Pages Computation (#3359) * fix: fixed `usePage` hook returning context instead of IPageStore * fix: updated recent pages with `updated_at` instead of `created_at` * fix: thown error instead of returning empty array --- web/hooks/store/use-page.ts | 4 ++-- web/store/page.store.ts | 36 +++++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/web/hooks/store/use-page.ts b/web/hooks/store/use-page.ts index cc348a4ad..8cd13dcdc 100644 --- a/web/hooks/store/use-page.ts +++ b/web/hooks/store/use-page.ts @@ -4,8 +4,8 @@ import { StoreContext } from "contexts/store-context"; // types import { IPageStore } from "store/page.store"; -export const usePage = (): any => { +export const usePage = (): IPageStore => { const context = useContext(StoreContext); if (context === undefined) throw new Error("usePage must be used within StoreProvider"); - return context as any; + return context.page; }; diff --git a/web/store/page.store.ts b/web/store/page.store.ts index debbad0ff..abb196334 100644 --- a/web/store/page.store.ts +++ b/web/store/page.store.ts @@ -133,21 +133,21 @@ export class PageStore implements IPageStore { get recentProjectPages() { if (!this.projectPageIds) return null; const data: IRecentPages = { today: [], yesterday: [], this_week: [], older: [] }; - data.today = this.projectPageIds.filter((p) => isToday(new Date(this.pages?.[p]?.created_at))) || []; - data.yesterday = this.projectPageIds.filter((p) => isYesterday(new Date(this.pages?.[p]?.created_at))) || []; + data.today = this.projectPageIds.filter((p) => isToday(new Date(this.pages?.[p]?.updated_at))) || []; + data.yesterday = this.projectPageIds.filter((p) => isYesterday(new Date(this.pages?.[p]?.updated_at))) || []; data.this_week = this.projectPageIds.filter((p) => { - const pageCreatedAt = this.pages?.[p]?.created_at; + const pageUpdatedAt = this.pages?.[p]?.updated_at; return ( - isThisWeek(new Date(pageCreatedAt)) && - !isToday(new Date(pageCreatedAt)) && - !isYesterday(new Date(pageCreatedAt)) + isThisWeek(new Date(pageUpdatedAt)) && + !isToday(new Date(pageUpdatedAt)) && + !isYesterday(new Date(pageUpdatedAt)) ); }) || []; data.older = this.projectPageIds.filter((p) => { - const pageCreatedAt = this.pages?.[p]?.created_at; - return !isThisWeek(new Date(pageCreatedAt)) && !isYesterday(new Date(pageCreatedAt)); + const pageUpdatedAt = this.pages?.[p]?.updated_at; + return !isThisWeek(new Date(pageUpdatedAt)) && !isYesterday(new Date(pageUpdatedAt)); }) || []; return data; } @@ -184,15 +184,21 @@ export class PageStore implements IPageStore { * @param projectId * @returns Promise */ - fetchProjectPages = async (workspaceSlug: string, projectId: string) => - await this.pageService.getProjectPages(workspaceSlug, projectId).then((response) => { - runInAction(() => { - response.forEach((page) => { - set(this.pages, [page.id], page); + fetchProjectPages = async (workspaceSlug: string, projectId: string) => { + try { + return await this.pageService.getProjectPages(workspaceSlug, projectId).then((response) => { + console.log("Response from backend 1", response); + runInAction(() => { + response.forEach((page) => { + set(this.pages, [page.id], page); + }); }); + return response; }); - return response; - }); + } catch (error) { + throw error; + } + }; /** * fetches all archived pages for a project. From d64ae9a2e48ec047f191692fd077ca892b286db5 Mon Sep 17 00:00:00 2001 From: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:51:00 +0530 Subject: [PATCH 3/9] fix: project loaders for mobx store (#3356) * add loaders to all the dropdowns outside project wrpper * fix build errors * minor refactor for project states color --------- Co-authored-by: Rahul R --- .../cycles/active-cycle-details.tsx | 2 +- .../inbox/modals/select-duplicate.tsx | 59 ++++++++++--------- .../issue-layouts/calendar/issue-blocks.tsx | 8 ++- .../issues/issue-layouts/gantt/blocks.tsx | 4 +- web/components/issues/select/label.tsx | 5 +- web/store/cycle.store.ts | 28 +++++---- web/store/label/project-label.store.ts | 20 ++++++- web/store/member/project-member.store.ts | 3 +- web/store/module.store.ts | 12 +++- web/store/project-view.store.ts | 8 ++- web/store/state.store.ts | 16 ++++- 11 files changed, 112 insertions(+), 53 deletions(-) diff --git a/web/components/cycles/active-cycle-details.tsx b/web/components/cycles/active-cycle-details.tsx index ed946bd82..56c5e1bc9 100644 --- a/web/components/cycles/active-cycle-details.tsx +++ b/web/components/cycles/active-cycle-details.tsx @@ -464,7 +464,7 @@ export const ActiveCycleDetails: React.FC = observer((props { issueIds?.filter( (issueId) => - getProjectStates(issueMap[issueId]?.project_id).find( + getProjectStates(issueMap[issueId]?.project_id)?.find( (issue) => issue.id === issueMap[issueId]?.state_id )?.group === "completed" )?.length diff --git a/web/components/inbox/modals/select-duplicate.tsx b/web/components/inbox/modals/select-duplicate.tsx index 415882daa..e4acca626 100644 --- a/web/components/inbox/modals/select-duplicate.tsx +++ b/web/components/inbox/modals/select-duplicate.tsx @@ -129,34 +129,37 @@ export const SelectDuplicateInboxIssueModal: React.FC = (props) => {

Select issue

)}
    - {filteredIssues.map((issue) => ( - - `flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 text-custom-text-200 ${ - active || selected ? "bg-custom-background-80 text-custom-text-100" : "" - } ` - } - > -
    - state?.id == issue?.state_id - )?.color || "", - }} - /> - - {getProjectById(issue?.project_id)?.identifier}-{issue.sequence_id} - - {issue.name} -
    -
    - ))} + {filteredIssues.map((issue) => { + const stateColor = + getProjectStates(issue?.project_id)?.find((state) => state?.id == issue?.state_id) + ?.color || ""; + + return ( + + `flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 text-custom-text-200 ${ + active || selected ? "bg-custom-background-80 text-custom-text-100" : "" + } ` + } + > +
    + + + {getProjectById(issue?.project_id)?.identifier}-{issue.sequence_id} + + {issue.name} +
    +
    + ); + })}
) : ( diff --git a/web/components/issues/issue-layouts/calendar/issue-blocks.tsx b/web/components/issues/issue-layouts/calendar/issue-blocks.tsx index 34bcac188..5711c89f6 100644 --- a/web/components/issues/issue-layouts/calendar/issue-blocks.tsx +++ b/web/components/issues/issue-layouts/calendar/issue-blocks.tsx @@ -59,6 +59,10 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { if (!issues?.[issueId]) return null; const issue = issues?.[issueId]; + + const stateColor = + getProjectStates(issue?.project_id)?.find((state) => state?.id == issue?.state_id)?.color || ""; + return ( {(provided, snapshot) => ( @@ -90,9 +94,7 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { state?.id == issue?.state_id - )?.color, + backgroundColor: stateColor, }} />
diff --git a/web/components/issues/issue-layouts/gantt/blocks.tsx b/web/components/issues/issue-layouts/gantt/blocks.tsx index 5622cd672..fefee880d 100644 --- a/web/components/issues/issue-layouts/gantt/blocks.tsx +++ b/web/components/issues/issue-layouts/gantt/blocks.tsx @@ -22,11 +22,13 @@ export const IssueGanttBlock = ({ data }: { data: TIssue }) => { data.id && setPeekIssue({ workspaceSlug, projectId: data.project_id, issueId: data.id }); + const stateColor = getProjectStates(data?.project_id)?.find((state) => state?.id == data?.state_id)?.color || ""; + return (
state?.id == data?.state_id)?.color, + backgroundColor: stateColor, }} onClick={handleIssuePeekOverview} > diff --git a/web/components/issues/select/label.tsx b/web/components/issues/select/label.tsx index 485adfcaa..fab1c04fd 100644 --- a/web/components/issues/select/label.tsx +++ b/web/components/issues/select/label.tsx @@ -30,7 +30,7 @@ export const IssueLabelSelect: React.FC = observer((props) => { const { workspaceSlug } = router.query; // store hooks const { - project: { projectLabels, fetchProjectLabels }, + project: { getProjectLabels, fetchProjectLabels }, } = useLabel(); // states const [query, setQuery] = useState(""); @@ -43,6 +43,9 @@ export const IssueLabelSelect: React.FC = observer((props) => { const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: "bottom-start", }); + + const projectLabels = getProjectLabels(projectId); + // derived values const filteredOptions = query === "" ? projectLabels : projectLabels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase())); diff --git a/web/store/cycle.store.ts b/web/store/cycle.store.ts index 5b663f79b..c8ab68f50 100644 --- a/web/store/cycle.store.ts +++ b/web/store/cycle.store.ts @@ -12,6 +12,8 @@ import { IssueService } from "services/issue"; import { CycleService } from "services/cycle.service"; export interface ICycleStore { + //Loaders + fetchedMap: Record; // observables cycleMap: Record; activeCycleIdMap: Record; @@ -50,6 +52,8 @@ export class CycleStore implements ICycleStore { // observables cycleMap: Record = {}; activeCycleIdMap: Record = {}; + //loaders + fetchedMap: Record = {}; // root store rootStore; // services @@ -62,6 +66,7 @@ export class CycleStore implements ICycleStore { // observables cycleMap: observable, activeCycleIdMap: observable, + fetchedMap: observable, // computed currentProjectCycleIds: computed, currentProjectCompletedCycleIds: computed, @@ -96,11 +101,11 @@ export class CycleStore implements ICycleStore { */ get currentProjectCycleIds() { const projectId = this.rootStore.app.router.projectId; - if (!projectId) return null; + if (!projectId || !this.fetchedMap[projectId]) return null; let allCycles = Object.values(this.cycleMap ?? {}).filter((c) => c?.project === projectId); allCycles = sortBy(allCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); const allCycleIds = allCycles.map((c) => c.id); - return allCycleIds || null; + return allCycleIds; } /** @@ -108,14 +113,14 @@ export class CycleStore implements ICycleStore { */ get currentProjectCompletedCycleIds() { const projectId = this.rootStore.app.router.projectId; - if (!projectId) return null; + if (!projectId || !this.fetchedMap[projectId]) return null; let completedCycles = Object.values(this.cycleMap ?? {}).filter((c) => { const hasEndDatePassed = isPast(new Date(c.end_date ?? "")); return c.project === projectId && hasEndDatePassed; }); completedCycles = sortBy(completedCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); const completedCycleIds = completedCycles.map((c) => c.id); - return completedCycleIds || null; + return completedCycleIds; } /** @@ -123,14 +128,14 @@ export class CycleStore implements ICycleStore { */ get currentProjectUpcomingCycleIds() { const projectId = this.rootStore.app.router.projectId; - if (!projectId) return null; + if (!projectId || !this.fetchedMap[projectId]) return null; let upcomingCycles = Object.values(this.cycleMap ?? {}).filter((c) => { const isStartDateUpcoming = isFuture(new Date(c.start_date ?? "")); return c.project === projectId && isStartDateUpcoming; }); upcomingCycles = sortBy(upcomingCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); const upcomingCycleIds = upcomingCycles.map((c) => c.id); - return upcomingCycleIds || null; + return upcomingCycleIds; } /** @@ -138,14 +143,14 @@ export class CycleStore implements ICycleStore { */ get currentProjectIncompleteCycleIds() { const projectId = this.rootStore.app.router.projectId; - if (!projectId) return null; + if (!projectId || !this.fetchedMap[projectId]) return null; let incompleteCycles = Object.values(this.cycleMap ?? {}).filter((c) => { const hasEndDatePassed = isPast(new Date(c.end_date ?? "")); return c.project === projectId && !hasEndDatePassed; }); incompleteCycles = sortBy(incompleteCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); const incompleteCycleIds = incompleteCycles.map((c) => c.id); - return incompleteCycleIds || null; + return incompleteCycleIds; } /** @@ -153,13 +158,13 @@ export class CycleStore implements ICycleStore { */ get currentProjectDraftCycleIds() { const projectId = this.rootStore.app.router.projectId; - if (!projectId) return null; + if (!projectId || !this.fetchedMap[projectId]) return null; let draftCycles = Object.values(this.cycleMap ?? {}).filter( (c) => c.project === projectId && !c.start_date && !c.end_date ); draftCycles = sortBy(draftCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); const draftCycleIds = draftCycles.map((c) => c.id); - return draftCycleIds || null; + return draftCycleIds; } /** @@ -194,6 +199,8 @@ export class CycleStore implements ICycleStore { * @param projectId */ getProjectCycleIds = (projectId: string): string[] | null => { + if (!this.fetchedMap[projectId]) return null; + let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project === projectId); cycles = sortBy(cycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); const cycleIds = cycles.map((c) => c.id); @@ -222,6 +229,7 @@ export class CycleStore implements ICycleStore { response.forEach((cycle) => { set(this.cycleMap, [cycle.id], cycle); }); + set(this.fetchedMap, projectId, true); }); return response; }); diff --git a/web/store/label/project-label.store.ts b/web/store/label/project-label.store.ts index d8782530d..fdbf342c3 100644 --- a/web/store/label/project-label.store.ts +++ b/web/store/label/project-label.store.ts @@ -1,4 +1,4 @@ -import { action, computed, makeObservable, runInAction } from "mobx"; +import { action, computed, makeObservable, observable, runInAction } from "mobx"; import set from "lodash/set"; // services import { IssueLabelService } from "services/issue"; @@ -10,9 +10,13 @@ import { IIssueLabel, IIssueLabelTree } from "@plane/types"; import { ILabelRootStore } from "store/label"; export interface IProjectLabelStore { + //Loaders + fetchedMap: Record; // computed projectLabels: IIssueLabel[] | undefined; projectLabelsTree: IIssueLabelTree[] | undefined; + //computed actions + getProjectLabels: (projectId: string) => IIssueLabel[] | undefined; // fetch actions fetchProjectLabels: (workspaceSlug: string, projectId: string) => Promise; // crud actions @@ -40,15 +44,21 @@ export class ProjectLabelStore implements IProjectLabelStore { rootStore; // root store labelMap labelMap: Record = {}; + //loaders + fetchedMap: Record = {}; // services issueLabelService; constructor(_labelRoot: ILabelRootStore, _rootStore: RootStore) { makeObservable(this, { + labelMap: observable, + fetchedMap: observable, // computed projectLabels: computed, projectLabelsTree: computed, // actions + getProjectLabels: action, + fetchProjectLabels: action, createLabel: action, updateLabel: action, @@ -68,7 +78,7 @@ export class ProjectLabelStore implements IProjectLabelStore { */ get projectLabels() { const projectId = this.rootStore.app.router.projectId; - if (!projectId || !this.labelMap) return; + if (!projectId || !this.fetchedMap[projectId] || !this.labelMap) return; return Object.values(this.labelMap ?? {}).filter((label) => label.project === projectId); } @@ -80,6 +90,11 @@ export class ProjectLabelStore implements IProjectLabelStore { return buildTree(this.projectLabels); } + getProjectLabels = (projectId: string) => { + if (!this.fetchedMap[projectId] || !this.labelMap) return; + return Object.values(this.labelMap ?? {}).filter((label) => label.project === projectId); + }; + /** * Fetches all the labelMap belongs to a specific project * @param workspaceSlug @@ -92,6 +107,7 @@ export class ProjectLabelStore implements IProjectLabelStore { response.forEach((label) => { set(this.labelMap, [label.id], label); }); + set(this.fetchedMap, projectId, true); }); return response; }); diff --git a/web/store/member/project-member.store.ts b/web/store/member/project-member.store.ts index 84718fdae..eaac78f7b 100644 --- a/web/store/member/project-member.store.ts +++ b/web/store/member/project-member.store.ts @@ -120,7 +120,8 @@ export class ProjectMemberStore implements IProjectMemberStore { * @param projectId */ getProjectMemberIds = (projectId: string): string[] | null => { - let members = Object.values(this.projectMemberMap?.[projectId] ?? {}); + if (!this.projectMemberMap?.[projectId]) return null; + let members = Object.values(this.projectMemberMap?.[projectId]); members = sortBy(members, [ (m) => m.member !== this.userStore.currentUser?.id, (m) => this.memberRoot?.memberMap?.[m.member]?.display_name?.toLowerCase(), diff --git a/web/store/module.store.ts b/web/store/module.store.ts index bbe649353..f37d41eaa 100644 --- a/web/store/module.store.ts +++ b/web/store/module.store.ts @@ -9,6 +9,8 @@ import { IModule, ILinkDetails } from "@plane/types"; import { RootStore } from "store/root.store"; export interface IModuleStore { + //Loaders + fetchedMap: Record; // observables moduleMap: Record; // computed @@ -51,6 +53,8 @@ export interface IModuleStore { export class ModulesStore implements IModuleStore { // observables moduleMap: Record = {}; + //loaders + fetchedMap: Record = {}; // root store rootStore; // services @@ -61,6 +65,7 @@ export class ModulesStore implements IModuleStore { makeObservable(this, { // observables moduleMap: observable, + fetchedMap: observable, // computed projectModuleIds: computed, // computed actions @@ -92,7 +97,7 @@ export class ModulesStore implements IModuleStore { */ get projectModuleIds() { const projectId = this.rootStore.app.router.projectId; - if (!projectId) return null; + if (!projectId || !this.fetchedMap[projectId]) return null; let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId); projectModules = sortBy(projectModules, [(m) => !m.is_favorite, (m) => m.name.toLowerCase()]); const projectModuleIds = projectModules.map((m) => m.id); @@ -111,10 +116,12 @@ export class ModulesStore implements IModuleStore { * @param projectId */ getProjectModuleIds = (projectId: string) => { + if (!this.fetchedMap[projectId]) return null; + let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId); projectModules = sortBy(projectModules, [(m) => !m.is_favorite, (m) => m.name.toLowerCase()]); const projectModuleIds = projectModules.map((m) => m.id); - return projectModuleIds || null; + return projectModuleIds; }; /** @@ -129,6 +136,7 @@ export class ModulesStore implements IModuleStore { response.forEach((module) => { set(this.moduleMap, [module.id], { ...this.moduleMap[module.id], ...module }); }); + set(this.fetchedMap, projectId, true); }); return response; }); diff --git a/web/store/project-view.store.ts b/web/store/project-view.store.ts index 674257321..6946271da 100644 --- a/web/store/project-view.store.ts +++ b/web/store/project-view.store.ts @@ -7,6 +7,8 @@ import { RootStore } from "store/root.store"; import { IProjectView } from "@plane/types"; export interface IProjectViewStore { + //Loaders + fetchedMap: Record; // observables viewMap: Record; // computed @@ -33,6 +35,8 @@ export interface IProjectViewStore { export class ProjectViewStore implements IProjectViewStore { // observables viewMap: Record = {}; + //loaders + fetchedMap: Record = {}; // root store rootStore; // services @@ -42,6 +46,7 @@ export class ProjectViewStore implements IProjectViewStore { makeObservable(this, { // observables viewMap: observable, + fetchedMap: observable, // computed projectViewIds: computed, // computed actions @@ -68,7 +73,7 @@ export class ProjectViewStore implements IProjectViewStore { */ get projectViewIds() { const projectId = this.rootStore.app.router.projectId; - if (!projectId) return null; + if (!projectId || !this.fetchedMap[projectId]) return null; const viewIds = Object.keys(this.viewMap ?? {})?.filter((viewId) => this.viewMap?.[viewId]?.project === projectId); return viewIds; } @@ -90,6 +95,7 @@ export class ProjectViewStore implements IProjectViewStore { response.forEach((view) => { set(this.viewMap, [view.id], view); }); + set(this.fetchedMap, projectId, true); }); return response; }); diff --git a/web/store/state.store.ts b/web/store/state.store.ts index fe9a62930..9671a3bf5 100644 --- a/web/store/state.store.ts +++ b/web/store/state.store.ts @@ -9,6 +9,8 @@ import { IState } from "@plane/types"; import { ProjectStateService } from "services/project"; export interface IStateStore { + //Loaders + fetchedMap: Record; // observables stateMap: Record; // computed @@ -16,7 +18,7 @@ export interface IStateStore { groupedProjectStates: Record | undefined; // computed actions getStateById: (stateId: string) => IState | undefined; - getProjectStates: (projectId: string) => IState[]; + getProjectStates: (projectId: string) => IState[] | undefined; // fetch actions fetchProjectStates: (workspaceSlug: string, projectId: string) => Promise; // crud actions @@ -40,6 +42,8 @@ export interface IStateStore { export class StateStore implements IStateStore { stateMap: Record = {}; + //loaders + fetchedMap: Record = {}; router; stateService; @@ -47,6 +51,7 @@ export class StateStore implements IStateStore { makeObservable(this, { // observables stateMap: observable, + fetchedMap: observable, // computed projectStates: computed, groupedProjectStates: computed, @@ -71,7 +76,8 @@ export class StateStore implements IStateStore { * Returns the stateMap belongs to a specific project */ get projectStates() { - if (!this.router.query?.projectId) return; + const projectId = this.router.query?.projectId?.toString(); + if (!projectId || !this.fetchedMap[projectId]) return; return Object.values(this.stateMap).filter((state) => state.project === this.router.query.projectId); } @@ -97,7 +103,10 @@ export class StateStore implements IStateStore { * @param projectId * @returns IState[] */ - getProjectStates = (projectId: string) => Object.values(this.stateMap).filter((state) => state.project === projectId); + getProjectStates = (projectId: string) => { + if (!projectId || !this.fetchedMap[projectId]) return; + return Object.values(this.stateMap).filter((state) => state.project === projectId); + }; /** * fetches the stateMap of a project @@ -111,6 +120,7 @@ export class StateStore implements IStateStore { statesResponse.forEach((state) => { set(this.stateMap, [state.id], state); }); + set(this.fetchedMap, projectId, true); }); return statesResponse; }; From ee2c7c5fa1604ba4ccdc0fdfe05f7292de48d3d0 Mon Sep 17 00:00:00 2001 From: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:52:04 +0530 Subject: [PATCH 4/9] enable peekoverview for spreadsheet and minor refactor for faster opening of the peekoverview component (#3361) Co-authored-by: Rahul R --- .../spreadsheet/base-spreadsheet-root.tsx | 14 +++-- .../issue-layouts/spreadsheet/issue-row.tsx | 39 ++++++------ .../spreadsheet/spreadsheet-table.tsx | 63 +++++++++++++++++++ .../spreadsheet/spreadsheet-view.tsx | 38 ++++------- 4 files changed, 105 insertions(+), 49 deletions(-) create mode 100644 web/components/issues/issue-layouts/spreadsheet/spreadsheet-table.tsx diff --git a/web/components/issues/issue-layouts/spreadsheet/base-spreadsheet-root.tsx b/web/components/issues/issue-layouts/spreadsheet/base-spreadsheet-root.tsx index 31c27e729..9ffc7c641 100644 --- a/web/components/issues/issue-layouts/spreadsheet/base-spreadsheet-root.tsx +++ b/web/components/issues/issue-layouts/spreadsheet/base-spreadsheet-root.tsx @@ -45,12 +45,16 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => { // user role validation const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; - const canEditProperties = (projectId: string | undefined) => { - const isEditingAllowedBasedOnProject = - canEditPropertiesBasedOnProject && projectId ? canEditPropertiesBasedOnProject(projectId) : isEditingAllowed; + const canEditProperties = useCallback( + (projectId: string | undefined) => { + const isEditingAllowedBasedOnProject = + canEditPropertiesBasedOnProject && projectId ? canEditPropertiesBasedOnProject(projectId) : isEditingAllowed; + + return enableInlineEditing && isEditingAllowedBasedOnProject; + }, + [canEditPropertiesBasedOnProject, enableInlineEditing, isEditingAllowed] + ); - return enableInlineEditing && isEditingAllowedBasedOnProject; - }; const issueIds = (issueStore.groupedIssueIds ?? []) as TUnGroupedIssues; diff --git a/web/components/issues/issue-layouts/spreadsheet/issue-row.tsx b/web/components/issues/issue-layouts/spreadsheet/issue-row.tsx index 60847ec54..602c1a842 100644 --- a/web/components/issues/issue-layouts/spreadsheet/issue-row.tsx +++ b/web/components/issues/issue-layouts/spreadsheet/issue-row.tsx @@ -8,7 +8,7 @@ import { SPREADSHEET_PROPERTY_DETAILS, SPREADSHEET_PROPERTY_LIST } from "constan // components import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC"; // ui -import { Tooltip } from "@plane/ui"; +import { ControlLink, Tooltip } from "@plane/ui"; // hooks import useOutsideClickDetector from "hooks/use-outside-click-detector"; import { useIssueDetail, useProject } from "hooks/store"; @@ -42,12 +42,13 @@ export const SpreadsheetIssueRow = observer((props: Props) => { quickActions, canEditProperties, } = props; + // router const router = useRouter(); - const { workspaceSlug } = router.query; - + //hooks const { getProjectById } = useProject(); + const { setPeekIssue } = useIssueDetail(); // states const [isMenuActive, setIsMenuActive] = useState(false); const [isExpanded, setExpanded] = useState(false); @@ -55,12 +56,8 @@ export const SpreadsheetIssueRow = observer((props: Props) => { const menuActionRef = useRef(null); const handleIssuePeekOverview = (issue: TIssue) => { - const { query } = router; - - router.push({ - pathname: router.pathname, - query: { ...query, peekIssueId: issue?.id, peekProjectId: issue?.project_id }, - }); + if (workspaceSlug && issue && issue.project_id && issue.id) + setPeekIssue({ workspaceSlug: workspaceSlug.toString(), projectId: issue.project_id, issueId: issue.id }); }; const { subIssues: subIssuesStore, issue } = useIssueDetail(); @@ -134,16 +131,20 @@ export const SpreadsheetIssueRow = observer((props: Props) => { )}
-
- -
handleIssuePeekOverview(issueDetail)} - > - {issueDetail.name} -
-
-
+ handleIssuePeekOverview(issueDetail)} + className="w-full line-clamp-1 cursor-pointer text-sm text-custom-text-100" + > +
+ +
+ {issueDetail.name} +
+
+
+
{/* Rest of the columns */} {SPREADSHEET_PROPERTY_LIST.map((property) => { diff --git a/web/components/issues/issue-layouts/spreadsheet/spreadsheet-table.tsx b/web/components/issues/issue-layouts/spreadsheet/spreadsheet-table.tsx new file mode 100644 index 000000000..6355a3a31 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/spreadsheet-table.tsx @@ -0,0 +1,63 @@ +import { observer } from "mobx-react-lite"; +//types +import { IIssueDisplayFilterOptions, IIssueDisplayProperties, TIssue } from "@plane/types"; +import { EIssueActions } from "../types"; +//components +import { SpreadsheetIssueRow } from "./issue-row"; +import { SpreadsheetHeader } from "./spreadsheet-header"; + +type Props = { + displayProperties: IIssueDisplayProperties; + displayFilters: IIssueDisplayFilterOptions; + handleDisplayFilterUpdate: (data: Partial) => void; + issues: TIssue[]; + isEstimateEnabled: boolean; + quickActions: ( + issue: TIssue, + customActionButton?: React.ReactElement, + portalElement?: HTMLDivElement | null + ) => React.ReactNode; + handleIssues: (issue: TIssue, action: EIssueActions) => Promise; + canEditProperties: (projectId: string | undefined) => boolean; + portalElement: React.MutableRefObject; +}; + +export const SpreadsheetTable = observer((props: Props) => { + const { + displayProperties, + displayFilters, + handleDisplayFilterUpdate, + issues, + isEstimateEnabled, + portalElement, + quickActions, + handleIssues, + canEditProperties, + } = props; + + return ( + + + + {issues.map(({ id }) => ( + + ))} + +
+ ); +}); diff --git a/web/components/issues/issue-layouts/spreadsheet/spreadsheet-view.tsx b/web/components/issues/issue-layouts/spreadsheet/spreadsheet-view.tsx index 89926f281..ad6e0e25a 100644 --- a/web/components/issues/issue-layouts/spreadsheet/spreadsheet-view.tsx +++ b/web/components/issues/issue-layouts/spreadsheet/spreadsheet-view.tsx @@ -3,12 +3,12 @@ import { observer } from "mobx-react-lite"; // components import { Spinner } from "@plane/ui"; import { SpreadsheetQuickAddIssueForm } from "components/issues"; +import { SpreadsheetTable } from "./spreadsheet-table"; // types import { TIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties } from "@plane/types"; import { EIssueActions } from "../types"; +//hooks import { useProject } from "hooks/store"; -import { SpreadsheetHeader } from "./spreadsheet-header"; -import { SpreadsheetIssueRow } from "./issue-row"; type Props = { displayProperties: IIssueDisplayProperties; @@ -102,29 +102,17 @@ export const SpreadsheetView: React.FC = observer((props) => {
- - - - {issues.map(({ id }) => ( - - ))} - -
+
From e9ef3fb32a859473f795e865201a6f29972f5ae9 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Sat, 13 Jan 2024 19:05:06 +0530 Subject: [PATCH 5/9] chore: formatting all python files using black formatter (#3366) --- apiserver/back_migration.py | 28 +- apiserver/manage.py | 6 +- apiserver/plane/__init__.py | 2 +- apiserver/plane/analytics/apps.py | 2 +- apiserver/plane/api/apps.py | 2 +- .../api/middleware/api_authentication.py | 7 +- apiserver/plane/api/rate_limit.py | 17 +- apiserver/plane/api/serializers/__init__.py | 8 +- apiserver/plane/api/serializers/base.py | 6 +- apiserver/plane/api/serializers/cycle.py | 7 +- apiserver/plane/api/serializers/inbox.py | 4 +- apiserver/plane/api/serializers/issue.py | 37 +- apiserver/plane/api/serializers/module.py | 12 +- apiserver/plane/api/serializers/project.py | 21 +- apiserver/plane/api/serializers/state.py | 8 +- apiserver/plane/api/serializers/user.py | 2 +- apiserver/plane/api/serializers/workspace.py | 3 +- apiserver/plane/api/urls/__init__.py | 2 +- apiserver/plane/api/urls/cycle.py | 2 +- apiserver/plane/api/urls/inbox.py | 2 +- apiserver/plane/api/urls/module.py | 2 +- apiserver/plane/api/urls/project.py | 4 +- apiserver/plane/api/urls/state.py | 2 +- apiserver/plane/api/views/__init__.py | 2 +- apiserver/plane/api/views/base.py | 16 +- apiserver/plane/api/views/cycle.py | 73 +- apiserver/plane/api/views/inbox.py | 44 +- apiserver/plane/api/views/issue.py | 97 +- apiserver/plane/api/views/module.py | 55 +- apiserver/plane/api/views/project.py | 57 +- apiserver/plane/api/views/state.py | 19 +- .../app/middleware/api_authentication.py | 5 +- apiserver/plane/app/permissions/__init__.py | 3 - apiserver/plane/app/serializers/__init__.py | 22 +- apiserver/plane/app/serializers/api.py | 5 +- apiserver/plane/app/serializers/base.py | 18 +- apiserver/plane/app/serializers/cycle.py | 24 +- apiserver/plane/app/serializers/estimate.py | 19 +- apiserver/plane/app/serializers/exporter.py | 4 +- apiserver/plane/app/serializers/importer.py | 8 +- apiserver/plane/app/serializers/inbox.py | 8 +- .../plane/app/serializers/integration/base.py | 4 +- apiserver/plane/app/serializers/issue.py | 99 +- apiserver/plane/app/serializers/module.py | 35 +- .../plane/app/serializers/notification.py | 6 +- apiserver/plane/app/serializers/page.py | 24 +- apiserver/plane/app/serializers/project.py | 30 +- apiserver/plane/app/serializers/state.py | 3 +- apiserver/plane/app/serializers/user.py | 12 +- apiserver/plane/app/serializers/view.py | 10 +- apiserver/plane/app/serializers/webhook.py | 84 +- apiserver/plane/app/serializers/workspace.py | 13 +- apiserver/plane/app/urls/__init__.py | 2 +- apiserver/plane/app/urls/authentication.py | 14 +- apiserver/plane/app/urls/config.py | 2 +- apiserver/plane/app/urls/cycle.py | 2 +- apiserver/plane/app/urls/module.py | 4 +- apiserver/plane/app/urls/project.py | 2 +- apiserver/plane/app/urls/views.py | 2 +- apiserver/plane/app/urls/workspace.py | 2 +- apiserver/plane/app/urls_deprecated.py | 37 +- apiserver/plane/app/views/__init__.py | 6 +- apiserver/plane/app/views/analytic.py | 38 +- apiserver/plane/app/views/api.py | 4 +- apiserver/plane/app/views/asset.py | 46 +- apiserver/plane/app/views/auth_extended.py | 36 +- apiserver/plane/app/views/authentication.py | 14 +- apiserver/plane/app/views/base.py | 44 +- apiserver/plane/app/views/config.py | 28 +- apiserver/plane/app/views/cycle.py | 122 +- apiserver/plane/app/views/estimate.py | 51 +- apiserver/plane/app/views/exporter.py | 10 +- apiserver/plane/app/views/external.py | 10 +- apiserver/plane/app/views/importer.py | 54 +- apiserver/plane/app/views/inbox.py | 55 +- apiserver/plane/app/views/integration/base.py | 16 +- .../plane/app/views/integration/github.py | 8 +- .../plane/app/views/integration/slack.py | 26 +- apiserver/plane/app/views/issue.py | 333 +- apiserver/plane/app/views/module.py | 61 +- apiserver/plane/app/views/notification.py | 40 +- apiserver/plane/app/views/page.py | 36 +- apiserver/plane/app/views/project.py | 131 +- apiserver/plane/app/views/search.py | 31 +- apiserver/plane/app/views/state.py | 11 +- apiserver/plane/app/views/user.py | 40 +- apiserver/plane/app/views/view.py | 40 +- apiserver/plane/app/views/webhook.py | 8 +- apiserver/plane/app/views/workspace.py | 125 +- .../plane/bgtasks/analytic_plot_export.py | 94 +- apiserver/plane/bgtasks/apps.py | 2 +- .../plane/bgtasks/event_tracking_task.py | 42 +- apiserver/plane/bgtasks/export_task.py | 45 +- .../plane/bgtasks/exporter_expired_task.py | 11 +- apiserver/plane/bgtasks/file_asset_task.py | 5 +- .../plane/bgtasks/forgot_password_task.py | 4 +- apiserver/plane/bgtasks/importer_task.py | 11 +- .../plane/bgtasks/issue_activites_task.py | 127 +- .../plane/bgtasks/issue_automation_task.py | 26 +- .../plane/bgtasks/magic_link_code_task.py | 4 +- apiserver/plane/bgtasks/notification_task.py | 278 +- .../plane/bgtasks/project_invitation_task.py | 1 + apiserver/plane/bgtasks/webhook_task.py | 7 +- .../bgtasks/workspace_invitation_task.py | 1 - .../db/management/commands/create_bucket.py | 69 +- .../db/management/commands/reset_password.py | 8 +- .../db/management/commands/wait_for_db.py | 15 +- apiserver/plane/db/migrations/0001_initial.py | 2760 +++++++++++++--- .../db/migrations/0002_auto_20221104_2239.py | 61 +- .../db/migrations/0003_auto_20221109_2320.py | 17 +- .../migrations/0004_alter_state_sequence.py | 7 +- .../db/migrations/0005_auto_20221114_2127.py | 19 +- .../db/migrations/0006_alter_cycle_status.py | 18 +- .../plane/db/migrations/0007_label_parent.py | 15 +- .../plane/db/migrations/0008_label_colour.py | 7 +- .../db/migrations/0009_auto_20221208_0310.py | 23 +- .../db/migrations/0010_auto_20221213_0037.py | 33 +- .../db/migrations/0011_auto_20221222_2357.py | 367 ++- .../db/migrations/0012_auto_20230104_0117.py | 228 +- .../db/migrations/0013_auto_20230107_0041.py | 23 +- ...r_workspacememberinvite_unique_together.py | 7 +- .../db/migrations/0015_auto_20230107_1636.py | 17 +- .../db/migrations/0016_auto_20230107_1735.py | 23 +- .../0017_alter_workspace_unique_together.py | 5 +- .../db/migrations/0018_auto_20230130_0119.py | 110 +- .../db/migrations/0019_auto_20230131_0049.py | 19 +- .../db/migrations/0020_auto_20230214_0118.py | 60 +- .../db/migrations/0021_auto_20230223_0104.py | 691 +++- .../db/migrations/0022_auto_20230307_0304.py | 302 +- .../db/migrations/0023_auto_20230316_0040.py | 325 +- .../db/migrations/0024_auto_20230322_0138.py | 335 +- .../db/migrations/0025_auto_20230331_0203.py | 124 +- .../0026_alter_projectmember_view_props.py | 13 +- .../db/migrations/0027_auto_20230409_0312.py | 314 +- .../db/migrations/0028_auto_20230414_1703.py | 102 +- .../db/migrations/0029_auto_20230502_0126.py | 116 +- ...030_alter_estimatepoint_unique_together.py | 5 +- .../plane/db/migrations/0031_analyticview.py | 78 +- .../db/migrations/0032_auto_20230520_2015.py | 13 +- .../db/migrations/0033_auto_20230618_2125.py | 225 +- .../db/migrations/0034_auto_20230628_1046.py | 27 +- .../db/migrations/0035_auto_20230704_2225.py | 8 +- .../0036_alter_workspace_organization_size.py | 7 +- ...archived_at_project_archive_in_and_more.py | 22 +- .../db/migrations/0038_auto_20230720_1505.py | 10 +- .../db/migrations/0039_auto_20230723_2203.py | 28 +- ...r_preferences_user_cover_image_and_more.py | 227 +- ...sort_order_issuecomment_access_and_more.py | 401 ++- ..._alter_analyticview_created_by_and_more.py | 756 ++++- ..._alter_analyticview_created_by_and_more.py | 142 +- .../db/migrations/0044_auto_20230913_0709.py | 94 +- ...ch_workspacemember_issue_props_and_more.py | 109 +- ..._alter_analyticview_created_by_and_more.py | 2920 +++++++++++------ ...escription_apitoken_expired_at_and_more.py | 319 +- .../db/migrations/0048_auto_20231116_0713.py | 141 +- .../db/migrations/0049_auto_20231116_0713.py | 6 +- ..._case_alter_workspace_organization_size.py | 28 +- ...ernal_id_cycle_external_source_and_more.py | 59 +- .../db/migrations/0052_auto_20231220_1141.py | 405 ++- .../db/migrations/0053_auto_20240102_1315.py | 33 +- apiserver/plane/db/mixins.py | 4 +- apiserver/plane/db/models/__init__.py | 9 +- apiserver/plane/db/models/api.py | 5 +- apiserver/plane/db/models/asset.py | 5 +- apiserver/plane/db/models/base.py | 6 +- apiserver/plane/db/models/cycle.py | 22 +- apiserver/plane/db/models/estimate.py | 4 +- apiserver/plane/db/models/exporter.py | 18 +- apiserver/plane/db/models/importer.py | 4 +- apiserver/plane/db/models/inbox.py | 17 +- .../plane/db/models/integration/__init__.py | 7 +- apiserver/plane/db/models/integration/base.py | 14 +- .../plane/db/models/integration/github.py | 21 +- .../plane/db/models/integration/slack.py | 4 +- apiserver/plane/db/models/issue.py | 79 +- apiserver/plane/db/models/module.py | 53 +- apiserver/plane/db/models/notification.py | 18 +- apiserver/plane/db/models/page.py | 16 +- apiserver/plane/db/models/project.py | 34 +- apiserver/plane/db/models/state.py | 4 +- apiserver/plane/db/models/user.py | 36 +- apiserver/plane/db/models/view.py | 9 +- apiserver/plane/db/models/webhook.py | 8 +- apiserver/plane/db/models/workspace.py | 51 +- .../plane/license/api/permissions/instance.py | 1 - .../plane/license/api/serializers/__init__.py | 6 +- .../plane/license/api/serializers/instance.py | 7 +- apiserver/plane/license/api/views/instance.py | 15 +- .../management/commands/configure_instance.py | 6 +- .../management/commands/register_instance.py | 20 +- .../plane/license/migrations/0001_initial.py | 253 +- apiserver/plane/license/models/__init__.py | 2 +- apiserver/plane/license/models/instance.py | 9 +- apiserver/plane/license/utils/encryption.py | 12 +- .../plane/license/utils/instance_value.py | 67 +- .../plane/middleware/api_log_middleware.py | 8 +- apiserver/plane/middleware/apps.py | 2 +- apiserver/plane/settings/common.py | 25 +- apiserver/plane/settings/test.py | 6 +- apiserver/plane/space/serializer/base.py | 6 +- apiserver/plane/space/serializer/cycle.py | 3 +- apiserver/plane/space/serializer/inbox.py | 10 +- apiserver/plane/space/serializer/issue.py | 77 +- apiserver/plane/space/serializer/module.py | 3 +- apiserver/plane/space/serializer/state.py | 1 - apiserver/plane/space/serializer/workspace.py | 3 +- apiserver/plane/space/views/base.py | 29 +- apiserver/plane/space/views/inbox.py | 48 +- apiserver/plane/space/views/issue.py | 55 +- apiserver/plane/tests/api/base.py | 4 +- apiserver/plane/tests/api/test_asset.py | 2 +- .../plane/tests/api/test_auth_extended.py | 2 +- .../plane/tests/api/test_authentication.py | 41 +- apiserver/plane/tests/api/test_cycle.py | 2 +- apiserver/plane/tests/api/test_issue.py | 2 +- apiserver/plane/tests/api/test_oauth.py | 2 +- apiserver/plane/tests/api/test_people.py | 2 +- apiserver/plane/tests/api/test_project.py | 2 +- apiserver/plane/tests/api/test_shortcut.py | 2 +- apiserver/plane/tests/api/test_state.py | 2 +- apiserver/plane/tests/api/test_workspace.py | 5 +- apiserver/plane/utils/analytics_plot.py | 56 +- apiserver/plane/utils/grouper.py | 148 +- apiserver/plane/utils/html_processor.py | 5 +- apiserver/plane/utils/importers/jira.py | 20 +- apiserver/plane/utils/imports.py | 9 +- apiserver/plane/utils/integrations/github.py | 4 +- apiserver/plane/utils/integrations/slack.py | 1 + apiserver/plane/utils/ip_address.py | 6 +- apiserver/plane/utils/issue_filters.py | 226 +- apiserver/plane/utils/paginator.py | 14 +- apiserver/plane/web/apps.py | 2 +- apiserver/plane/web/urls.py | 3 +- apiserver/plane/wsgi.py | 3 +- apiserver/pyproject.toml | 18 + 235 files changed, 12967 insertions(+), 4168 deletions(-) create mode 100644 apiserver/pyproject.toml diff --git a/apiserver/back_migration.py b/apiserver/back_migration.py index c04ee7771..a0e45416a 100644 --- a/apiserver/back_migration.py +++ b/apiserver/back_migration.py @@ -26,7 +26,9 @@ def update_description(): updated_issues.append(issue) Issue.objects.bulk_update( - updated_issues, ["description_html", "description_stripped"], batch_size=100 + updated_issues, + ["description_html", "description_stripped"], + batch_size=100, ) print("Success") except Exception as e: @@ -40,7 +42,9 @@ def update_comments(): updated_issue_comments = [] for issue_comment in issue_comments: - issue_comment.comment_html = f"

{issue_comment.comment_stripped}

" + issue_comment.comment_html = ( + f"

{issue_comment.comment_stripped}

" + ) updated_issue_comments.append(issue_comment) IssueComment.objects.bulk_update( @@ -99,7 +103,9 @@ def updated_issue_sort_order(): issue.sort_order = issue.sequence_id * random.randint(100, 500) updated_issues.append(issue) - Issue.objects.bulk_update(updated_issues, ["sort_order"], batch_size=100) + Issue.objects.bulk_update( + updated_issues, ["sort_order"], batch_size=100 + ) print("Success") except Exception as e: print(e) @@ -137,7 +143,9 @@ def update_project_cover_images(): project.cover_image = project_cover_images[random.randint(0, 19)] updated_projects.append(project) - Project.objects.bulk_update(updated_projects, ["cover_image"], batch_size=100) + Project.objects.bulk_update( + updated_projects, ["cover_image"], batch_size=100 + ) print("Success") except Exception as e: print(e) @@ -186,7 +194,9 @@ def update_label_color(): def create_slack_integration(): try: - _ = Integration.objects.create(provider="slack", network=2, title="Slack") + _ = Integration.objects.create( + provider="slack", network=2, title="Slack" + ) print("Success") except Exception as e: print(e) @@ -212,12 +222,16 @@ def update_integration_verified(): def update_start_date(): try: - issues = Issue.objects.filter(state__group__in=["started", "completed"]) + issues = Issue.objects.filter( + state__group__in=["started", "completed"] + ) updated_issues = [] for issue in issues: issue.start_date = issue.created_at.date() updated_issues.append(issue) - Issue.objects.bulk_update(updated_issues, ["start_date"], batch_size=500) + Issue.objects.bulk_update( + updated_issues, ["start_date"], batch_size=500 + ) print("Success") except Exception as e: print(e) diff --git a/apiserver/manage.py b/apiserver/manage.py index 837297219..744086783 100644 --- a/apiserver/manage.py +++ b/apiserver/manage.py @@ -2,10 +2,10 @@ import os import sys -if __name__ == '__main__': +if __name__ == "__main__": os.environ.setdefault( - 'DJANGO_SETTINGS_MODULE', - 'plane.settings.production') + "DJANGO_SETTINGS_MODULE", "plane.settings.production" + ) try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/apiserver/plane/__init__.py b/apiserver/plane/__init__.py index fb989c4e6..53f4ccb1d 100644 --- a/apiserver/plane/__init__.py +++ b/apiserver/plane/__init__.py @@ -1,3 +1,3 @@ from .celery import app as celery_app -__all__ = ('celery_app',) +__all__ = ("celery_app",) diff --git a/apiserver/plane/analytics/apps.py b/apiserver/plane/analytics/apps.py index 353779983..52a59f313 100644 --- a/apiserver/plane/analytics/apps.py +++ b/apiserver/plane/analytics/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class AnalyticsConfig(AppConfig): - name = 'plane.analytics' + name = "plane.analytics" diff --git a/apiserver/plane/api/apps.py b/apiserver/plane/api/apps.py index 292ad9344..6ba36e7e5 100644 --- a/apiserver/plane/api/apps.py +++ b/apiserver/plane/api/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class ApiConfig(AppConfig): - name = "plane.api" \ No newline at end of file + name = "plane.api" diff --git a/apiserver/plane/api/middleware/api_authentication.py b/apiserver/plane/api/middleware/api_authentication.py index 1b2c03318..893df7f84 100644 --- a/apiserver/plane/api/middleware/api_authentication.py +++ b/apiserver/plane/api/middleware/api_authentication.py @@ -25,7 +25,10 @@ class APIKeyAuthentication(authentication.BaseAuthentication): def validate_api_token(self, token): try: api_token = APIToken.objects.get( - Q(Q(expired_at__gt=timezone.now()) | Q(expired_at__isnull=True)), + Q( + Q(expired_at__gt=timezone.now()) + | Q(expired_at__isnull=True) + ), token=token, is_active=True, ) @@ -44,4 +47,4 @@ class APIKeyAuthentication(authentication.BaseAuthentication): # Validate the API token user, token = self.validate_api_token(token) - return user, token \ No newline at end of file + return user, token diff --git a/apiserver/plane/api/rate_limit.py b/apiserver/plane/api/rate_limit.py index f91e2d65d..b62936d8e 100644 --- a/apiserver/plane/api/rate_limit.py +++ b/apiserver/plane/api/rate_limit.py @@ -1,17 +1,18 @@ from rest_framework.throttling import SimpleRateThrottle + class ApiKeyRateThrottle(SimpleRateThrottle): - scope = 'api_key' - rate = '60/minute' + scope = "api_key" + rate = "60/minute" def get_cache_key(self, request, view): # Retrieve the API key from the request header - api_key = request.headers.get('X-Api-Key') + api_key = request.headers.get("X-Api-Key") if not api_key: return None # Allow the request if there's no API key # Use the API key as part of the cache key - return f'{self.scope}:{api_key}' + return f"{self.scope}:{api_key}" def allow_request(self, request, view): allowed = super().allow_request(request, view) @@ -24,7 +25,7 @@ class ApiKeyRateThrottle(SimpleRateThrottle): # Remove old histories while history and history[-1] <= now - self.duration: history.pop() - + # Calculate the requests num_requests = len(history) @@ -35,7 +36,7 @@ class ApiKeyRateThrottle(SimpleRateThrottle): reset_time = int(now + self.duration) # Add headers - request.META['X-RateLimit-Remaining'] = max(0, available) - request.META['X-RateLimit-Reset'] = reset_time + request.META["X-RateLimit-Remaining"] = max(0, available) + request.META["X-RateLimit-Reset"] = reset_time - return allowed \ No newline at end of file + return allowed diff --git a/apiserver/plane/api/serializers/__init__.py b/apiserver/plane/api/serializers/__init__.py index 1fd1bce78..10b0182d6 100644 --- a/apiserver/plane/api/serializers/__init__.py +++ b/apiserver/plane/api/serializers/__init__.py @@ -13,5 +13,9 @@ from .issue import ( ) from .state import StateLiteSerializer, StateSerializer from .cycle import CycleSerializer, CycleIssueSerializer, CycleLiteSerializer -from .module import ModuleSerializer, ModuleIssueSerializer, ModuleLiteSerializer -from .inbox import InboxIssueSerializer \ No newline at end of file +from .module import ( + ModuleSerializer, + ModuleIssueSerializer, + ModuleLiteSerializer, +) +from .inbox import InboxIssueSerializer diff --git a/apiserver/plane/api/serializers/base.py b/apiserver/plane/api/serializers/base.py index 4e88597c7..da8b96964 100644 --- a/apiserver/plane/api/serializers/base.py +++ b/apiserver/plane/api/serializers/base.py @@ -100,6 +100,8 @@ class BaseSerializer(serializers.ModelSerializer): response[expand] = exp_serializer.data else: # You might need to handle this case differently - response[expand] = getattr(instance, f"{expand}_id", None) + response[expand] = getattr( + instance, f"{expand}_id", None + ) - return response \ No newline at end of file + return response diff --git a/apiserver/plane/api/serializers/cycle.py b/apiserver/plane/api/serializers/cycle.py index eaff8181a..6fc73a4bc 100644 --- a/apiserver/plane/api/serializers/cycle.py +++ b/apiserver/plane/api/serializers/cycle.py @@ -23,7 +23,9 @@ class CycleSerializer(BaseSerializer): and data.get("end_date", None) is not None and data.get("start_date", None) > data.get("end_date", None) ): - raise serializers.ValidationError("Start date cannot exceed end date") + raise serializers.ValidationError( + "Start date cannot exceed end date" + ) return data class Meta: @@ -55,7 +57,6 @@ class CycleIssueSerializer(BaseSerializer): class CycleLiteSerializer(BaseSerializer): - class Meta: model = Cycle - fields = "__all__" \ No newline at end of file + fields = "__all__" diff --git a/apiserver/plane/api/serializers/inbox.py b/apiserver/plane/api/serializers/inbox.py index 17ae8c1ed..78bb74d13 100644 --- a/apiserver/plane/api/serializers/inbox.py +++ b/apiserver/plane/api/serializers/inbox.py @@ -2,8 +2,8 @@ from .base import BaseSerializer from plane.db.models import InboxIssue -class InboxIssueSerializer(BaseSerializer): +class InboxIssueSerializer(BaseSerializer): class Meta: model = InboxIssue fields = "__all__" @@ -16,4 +16,4 @@ class InboxIssueSerializer(BaseSerializer): "updated_by", "created_at", "updated_at", - ] \ No newline at end of file + ] diff --git a/apiserver/plane/api/serializers/issue.py b/apiserver/plane/api/serializers/issue.py index 75396e9bb..4c8d6e815 100644 --- a/apiserver/plane/api/serializers/issue.py +++ b/apiserver/plane/api/serializers/issue.py @@ -27,6 +27,7 @@ from .module import ModuleSerializer, ModuleLiteSerializer from .user import UserLiteSerializer from .state import StateLiteSerializer + class IssueSerializer(BaseSerializer): assignees = serializers.ListField( child=serializers.PrimaryKeyRelatedField( @@ -66,14 +67,16 @@ class IssueSerializer(BaseSerializer): and data.get("target_date", None) is not None and data.get("start_date", None) > data.get("target_date", None) ): - raise serializers.ValidationError("Start date cannot exceed target date") - + raise serializers.ValidationError( + "Start date cannot exceed target date" + ) + try: - if(data.get("description_html", None) is not None): + if data.get("description_html", None) is not None: parsed = html.fromstring(data["description_html"]) - parsed_str = html.tostring(parsed, encoding='unicode') + parsed_str = html.tostring(parsed, encoding="unicode") data["description_html"] = parsed_str - + except Exception as e: raise serializers.ValidationError(f"Invalid HTML: {str(e)}") @@ -96,7 +99,8 @@ class IssueSerializer(BaseSerializer): if ( data.get("state") and not State.objects.filter( - project_id=self.context.get("project_id"), pk=data.get("state").id + project_id=self.context.get("project_id"), + pk=data.get("state").id, ).exists() ): raise serializers.ValidationError( @@ -107,7 +111,8 @@ class IssueSerializer(BaseSerializer): if ( data.get("parent") and not Issue.objects.filter( - workspace_id=self.context.get("workspace_id"), pk=data.get("parent").id + workspace_id=self.context.get("workspace_id"), + pk=data.get("parent").id, ).exists() ): raise serializers.ValidationError( @@ -238,9 +243,13 @@ class IssueSerializer(BaseSerializer): ] if "labels" in self.fields: if "labels" in self.expand: - data["labels"] = LabelSerializer(instance.labels.all(), many=True).data + data["labels"] = LabelSerializer( + instance.labels.all(), many=True + ).data else: - data["labels"] = [str(label.id) for label in instance.labels.all()] + data["labels"] = [ + str(label.id) for label in instance.labels.all() + ] return data @@ -278,7 +287,8 @@ class IssueLinkSerializer(BaseSerializer): # Validation if url already exists def create(self, validated_data): if IssueLink.objects.filter( - url=validated_data.get("url"), issue_id=validated_data.get("issue_id") + url=validated_data.get("url"), + issue_id=validated_data.get("issue_id"), ).exists(): raise serializers.ValidationError( {"error": "URL already exists for this Issue"} @@ -324,11 +334,11 @@ class IssueCommentSerializer(BaseSerializer): def validate(self, data): try: - if(data.get("comment_html", None) is not None): + if data.get("comment_html", None) is not None: parsed = html.fromstring(data["comment_html"]) - parsed_str = html.tostring(parsed, encoding='unicode') + parsed_str = html.tostring(parsed, encoding="unicode") data["comment_html"] = parsed_str - + except Exception as e: raise serializers.ValidationError(f"Invalid HTML: {str(e)}") return data @@ -362,7 +372,6 @@ class ModuleIssueSerializer(BaseSerializer): class LabelLiteSerializer(BaseSerializer): - class Meta: model = Label fields = [ diff --git a/apiserver/plane/api/serializers/module.py b/apiserver/plane/api/serializers/module.py index a96a9b54d..01a201064 100644 --- a/apiserver/plane/api/serializers/module.py +++ b/apiserver/plane/api/serializers/module.py @@ -52,7 +52,9 @@ class ModuleSerializer(BaseSerializer): and data.get("target_date", None) is not None and data.get("start_date", None) > data.get("target_date", None) ): - raise serializers.ValidationError("Start date cannot exceed target date") + raise serializers.ValidationError( + "Start date cannot exceed target date" + ) if data.get("members", []): data["members"] = ProjectMember.objects.filter( @@ -146,16 +148,16 @@ class ModuleLinkSerializer(BaseSerializer): # Validation if url already exists def create(self, validated_data): if ModuleLink.objects.filter( - url=validated_data.get("url"), module_id=validated_data.get("module_id") + url=validated_data.get("url"), + module_id=validated_data.get("module_id"), ).exists(): raise serializers.ValidationError( {"error": "URL already exists for this Issue"} ) return ModuleLink.objects.create(**validated_data) - + class ModuleLiteSerializer(BaseSerializer): - class Meta: model = Module - fields = "__all__" \ No newline at end of file + fields = "__all__" diff --git a/apiserver/plane/api/serializers/project.py b/apiserver/plane/api/serializers/project.py index c394a080d..342cc1a81 100644 --- a/apiserver/plane/api/serializers/project.py +++ b/apiserver/plane/api/serializers/project.py @@ -2,12 +2,17 @@ from rest_framework import serializers # Module imports -from plane.db.models import Project, ProjectIdentifier, WorkspaceMember, State, Estimate +from plane.db.models import ( + Project, + ProjectIdentifier, + WorkspaceMember, + State, + Estimate, +) from .base import BaseSerializer class ProjectSerializer(BaseSerializer): - total_members = serializers.IntegerField(read_only=True) total_cycles = serializers.IntegerField(read_only=True) total_modules = serializers.IntegerField(read_only=True) @@ -21,7 +26,7 @@ class ProjectSerializer(BaseSerializer): fields = "__all__" read_only_fields = [ "id", - 'emoji', + "emoji", "workspace", "created_at", "updated_at", @@ -59,12 +64,16 @@ class ProjectSerializer(BaseSerializer): def create(self, validated_data): identifier = validated_data.get("identifier", "").strip().upper() if identifier == "": - raise serializers.ValidationError(detail="Project Identifier is required") + raise serializers.ValidationError( + detail="Project Identifier is required" + ) if ProjectIdentifier.objects.filter( name=identifier, workspace_id=self.context["workspace_id"] ).exists(): - raise serializers.ValidationError(detail="Project Identifier is taken") + raise serializers.ValidationError( + detail="Project Identifier is taken" + ) project = Project.objects.create( **validated_data, workspace_id=self.context["workspace_id"] @@ -89,4 +98,4 @@ class ProjectLiteSerializer(BaseSerializer): "emoji", "description", ] - read_only_fields = fields \ No newline at end of file + read_only_fields = fields diff --git a/apiserver/plane/api/serializers/state.py b/apiserver/plane/api/serializers/state.py index 9d08193d8..1649a7bcf 100644 --- a/apiserver/plane/api/serializers/state.py +++ b/apiserver/plane/api/serializers/state.py @@ -7,9 +7,9 @@ class StateSerializer(BaseSerializer): def validate(self, data): # If the default is being provided then make all other states default False if data.get("default", False): - State.objects.filter(project_id=self.context.get("project_id")).update( - default=False - ) + State.objects.filter( + project_id=self.context.get("project_id") + ).update(default=False) return data class Meta: @@ -35,4 +35,4 @@ class StateLiteSerializer(BaseSerializer): "color", "group", ] - read_only_fields = fields \ No newline at end of file + read_only_fields = fields diff --git a/apiserver/plane/api/serializers/user.py b/apiserver/plane/api/serializers/user.py index 42b6c3967..fe50021b5 100644 --- a/apiserver/plane/api/serializers/user.py +++ b/apiserver/plane/api/serializers/user.py @@ -13,4 +13,4 @@ class UserLiteSerializer(BaseSerializer): "avatar", "display_name", ] - read_only_fields = fields \ No newline at end of file + read_only_fields = fields diff --git a/apiserver/plane/api/serializers/workspace.py b/apiserver/plane/api/serializers/workspace.py index c4c5caceb..a47de3d31 100644 --- a/apiserver/plane/api/serializers/workspace.py +++ b/apiserver/plane/api/serializers/workspace.py @@ -5,6 +5,7 @@ from .base import BaseSerializer class WorkspaceLiteSerializer(BaseSerializer): """Lite serializer with only required fields""" + class Meta: model = Workspace fields = [ @@ -12,4 +13,4 @@ class WorkspaceLiteSerializer(BaseSerializer): "slug", "id", ] - read_only_fields = fields \ No newline at end of file + read_only_fields = fields diff --git a/apiserver/plane/api/urls/__init__.py b/apiserver/plane/api/urls/__init__.py index a5ef0f5f1..84927439e 100644 --- a/apiserver/plane/api/urls/__init__.py +++ b/apiserver/plane/api/urls/__init__.py @@ -12,4 +12,4 @@ urlpatterns = [ *cycle_patterns, *module_patterns, *inbox_patterns, -] \ No newline at end of file +] diff --git a/apiserver/plane/api/urls/cycle.py b/apiserver/plane/api/urls/cycle.py index f557f8af0..593e501bf 100644 --- a/apiserver/plane/api/urls/cycle.py +++ b/apiserver/plane/api/urls/cycle.py @@ -32,4 +32,4 @@ urlpatterns = [ TransferCycleIssueAPIEndpoint.as_view(), name="transfer-issues", ), -] \ No newline at end of file +] diff --git a/apiserver/plane/api/urls/inbox.py b/apiserver/plane/api/urls/inbox.py index 3a2a57786..95eb68f3f 100644 --- a/apiserver/plane/api/urls/inbox.py +++ b/apiserver/plane/api/urls/inbox.py @@ -14,4 +14,4 @@ urlpatterns = [ InboxIssueAPIEndpoint.as_view(), name="inbox-issue", ), -] \ No newline at end of file +] diff --git a/apiserver/plane/api/urls/module.py b/apiserver/plane/api/urls/module.py index 7117a9e8b..4309f44e9 100644 --- a/apiserver/plane/api/urls/module.py +++ b/apiserver/plane/api/urls/module.py @@ -23,4 +23,4 @@ urlpatterns = [ ModuleIssueAPIEndpoint.as_view(), name="module-issues", ), -] \ No newline at end of file +] diff --git a/apiserver/plane/api/urls/project.py b/apiserver/plane/api/urls/project.py index c73e84c89..1ed450c86 100644 --- a/apiserver/plane/api/urls/project.py +++ b/apiserver/plane/api/urls/project.py @@ -3,7 +3,7 @@ from django.urls import path from plane.api.views import ProjectAPIEndpoint urlpatterns = [ - path( + path( "workspaces//projects/", ProjectAPIEndpoint.as_view(), name="project", @@ -13,4 +13,4 @@ urlpatterns = [ ProjectAPIEndpoint.as_view(), name="project", ), -] \ No newline at end of file +] diff --git a/apiserver/plane/api/urls/state.py b/apiserver/plane/api/urls/state.py index 0676ac5ad..b03f386e6 100644 --- a/apiserver/plane/api/urls/state.py +++ b/apiserver/plane/api/urls/state.py @@ -13,4 +13,4 @@ urlpatterns = [ StateAPIEndpoint.as_view(), name="states", ), -] \ No newline at end of file +] diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index 84d8dcabb..0da79566f 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -18,4 +18,4 @@ from .cycle import ( from .module import ModuleAPIEndpoint, ModuleIssueAPIEndpoint -from .inbox import InboxIssueAPIEndpoint \ No newline at end of file +from .inbox import InboxIssueAPIEndpoint diff --git a/apiserver/plane/api/views/base.py b/apiserver/plane/api/views/base.py index 035266bd5..b069ef78c 100644 --- a/apiserver/plane/api/views/base.py +++ b/apiserver/plane/api/views/base.py @@ -41,7 +41,9 @@ class WebhookMixin: bulk = False def finalize_response(self, request, response, *args, **kwargs): - response = super().finalize_response(request, response, *args, **kwargs) + response = super().finalize_response( + request, response, *args, **kwargs + ) # Check for the case should webhook be sent if ( @@ -139,7 +141,9 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): def finalize_response(self, request, response, *args, **kwargs): # Call super to get the default response - response = super().finalize_response(request, response, *args, **kwargs) + response = super().finalize_response( + request, response, *args, **kwargs + ) # Add custom headers if they exist in the request META ratelimit_remaining = request.META.get("X-RateLimit-Remaining") @@ -163,13 +167,17 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): @property def fields(self): fields = [ - field for field in self.request.GET.get("fields", "").split(",") if field + field + for field in self.request.GET.get("fields", "").split(",") + if field ] return fields if fields else None @property def expand(self): expand = [ - expand for expand in self.request.GET.get("expand", "").split(",") if expand + expand + for expand in self.request.GET.get("expand", "").split(",") + if expand ] return expand if expand else None diff --git a/apiserver/plane/api/views/cycle.py b/apiserver/plane/api/views/cycle.py index 310332333..c296bb111 100644 --- a/apiserver/plane/api/views/cycle.py +++ b/apiserver/plane/api/views/cycle.py @@ -12,7 +12,13 @@ from rest_framework import status # Module imports from .base import BaseAPIView, WebhookMixin -from plane.db.models import Cycle, Issue, CycleIssue, IssueLink, IssueAttachment +from plane.db.models import ( + Cycle, + Issue, + CycleIssue, + IssueLink, + IssueAttachment, +) from plane.app.permissions import ProjectEntityPermission from plane.api.serializers import ( CycleSerializer, @@ -102,7 +108,9 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView): ), ) ) - .annotate(total_estimates=Sum("issue_cycle__issue__estimate_point")) + .annotate( + total_estimates=Sum("issue_cycle__issue__estimate_point") + ) .annotate( completed_estimates=Sum( "issue_cycle__issue__estimate_point", @@ -201,7 +209,8 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView): # Incomplete Cycles if cycle_view == "incomplete": queryset = queryset.filter( - Q(end_date__gte=timezone.now().date()) | Q(end_date__isnull=True), + Q(end_date__gte=timezone.now().date()) + | Q(end_date__isnull=True), ) return self.paginate( request=request, @@ -238,8 +247,12 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView): project_id=project_id, owned_by=request.user, ) - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + serializer.data, status=status.HTTP_201_CREATED + ) + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) else: return Response( { @@ -249,15 +262,22 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView): ) def patch(self, request, slug, project_id, pk): - cycle = Cycle.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) + cycle = Cycle.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk + ) request_data = request.data - if cycle.end_date is not None and cycle.end_date < timezone.now().date(): + if ( + cycle.end_date is not None + and cycle.end_date < timezone.now().date() + ): if "sort_order" in request_data: # Can only change sort order request_data = { - "sort_order": request_data.get("sort_order", cycle.sort_order) + "sort_order": request_data.get( + "sort_order", cycle.sort_order + ) } else: return Response( @@ -275,11 +295,13 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView): def delete(self, request, slug, project_id, pk): cycle_issues = list( - CycleIssue.objects.filter(cycle_id=self.kwargs.get("pk")).values_list( - "issue", flat=True - ) + CycleIssue.objects.filter( + cycle_id=self.kwargs.get("pk") + ).values_list("issue", flat=True) + ) + cycle = Cycle.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk ) - cycle = Cycle.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) issue_activity.delay( type="cycle.activity.deleted", @@ -319,7 +341,9 @@ class CycleIssueAPIEndpoint(WebhookMixin, BaseAPIView): def get_queryset(self): return ( CycleIssue.objects.annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("issue_id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("issue_id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -342,7 +366,9 @@ class CycleIssueAPIEndpoint(WebhookMixin, BaseAPIView): issues = ( Issue.issue_objects.filter(issue_cycle__cycle_id=cycle_id) .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -364,7 +390,9 @@ class CycleIssueAPIEndpoint(WebhookMixin, BaseAPIView): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -387,14 +415,18 @@ class CycleIssueAPIEndpoint(WebhookMixin, BaseAPIView): if not issues: return Response( - {"error": "Issues are required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Issues are required"}, + status=status.HTTP_400_BAD_REQUEST, ) cycle = Cycle.objects.get( workspace__slug=slug, project_id=project_id, pk=cycle_id ) - if cycle.end_date is not None and cycle.end_date < timezone.now().date(): + if ( + cycle.end_date is not None + and cycle.end_date < timezone.now().date() + ): return Response( { "error": "The Cycle has already been completed so no new issues can be added" @@ -479,7 +511,10 @@ class CycleIssueAPIEndpoint(WebhookMixin, BaseAPIView): def delete(self, request, slug, project_id, cycle_id, issue_id): cycle_issue = CycleIssue.objects.get( - issue_id=issue_id, workspace__slug=slug, project_id=project_id, cycle_id=cycle_id + issue_id=issue_id, + workspace__slug=slug, + project_id=project_id, + cycle_id=cycle_id, ) issue_id = cycle_issue.issue_id cycle_issue.delete() @@ -550,4 +585,4 @@ class TransferCycleIssueAPIEndpoint(BaseAPIView): updated_cycles, ["cycle_id"], batch_size=100 ) - return Response({"message": "Success"}, status=status.HTTP_200_OK) \ No newline at end of file + return Response({"message": "Success"}, status=status.HTTP_200_OK) diff --git a/apiserver/plane/api/views/inbox.py b/apiserver/plane/api/views/inbox.py index 4f4cdc4ef..c1079345a 100644 --- a/apiserver/plane/api/views/inbox.py +++ b/apiserver/plane/api/views/inbox.py @@ -14,7 +14,14 @@ from rest_framework.response import Response from .base import BaseAPIView from plane.app.permissions import ProjectLitePermission from plane.api.serializers import InboxIssueSerializer, IssueSerializer -from plane.db.models import InboxIssue, Issue, State, ProjectMember, Project, Inbox +from plane.db.models import ( + InboxIssue, + Issue, + State, + ProjectMember, + Project, + Inbox, +) from plane.bgtasks.issue_activites_task import issue_activity @@ -43,7 +50,8 @@ class InboxIssueAPIEndpoint(BaseAPIView): ).first() project = Project.objects.get( - workspace__slug=self.kwargs.get("slug"), pk=self.kwargs.get("project_id") + workspace__slug=self.kwargs.get("slug"), + pk=self.kwargs.get("project_id"), ) if inbox is None and not project.inbox_view: @@ -51,7 +59,8 @@ class InboxIssueAPIEndpoint(BaseAPIView): return ( InboxIssue.objects.filter( - Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True), + Q(snoozed_till__gte=timezone.now()) + | Q(snoozed_till__isnull=True), workspace__slug=self.kwargs.get("slug"), project_id=self.kwargs.get("project_id"), inbox_id=inbox.id, @@ -87,7 +96,8 @@ class InboxIssueAPIEndpoint(BaseAPIView): def post(self, request, slug, project_id): if not request.data.get("issue", {}).get("name", False): return Response( - {"error": "Name is required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Name is required"}, + status=status.HTTP_400_BAD_REQUEST, ) inbox = Inbox.objects.filter( @@ -117,7 +127,8 @@ class InboxIssueAPIEndpoint(BaseAPIView): "none", ]: return Response( - {"error": "Invalid priority"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Invalid priority"}, + status=status.HTTP_400_BAD_REQUEST, ) # Create or get state @@ -222,10 +233,14 @@ class InboxIssueAPIEndpoint(BaseAPIView): "description_html": issue_data.get( "description_html", issue.description_html ), - "description": issue_data.get("description", issue.description), + "description": issue_data.get( + "description", issue.description + ), } - issue_serializer = IssueSerializer(issue, data=issue_data, partial=True) + issue_serializer = IssueSerializer( + issue, data=issue_data, partial=True + ) if issue_serializer.is_valid(): current_instance = issue @@ -266,7 +281,9 @@ class InboxIssueAPIEndpoint(BaseAPIView): project_id=project_id, ) state = State.objects.filter( - group="cancelled", workspace__slug=slug, project_id=project_id + group="cancelled", + workspace__slug=slug, + project_id=project_id, ).first() if state is not None: issue.state = state @@ -284,17 +301,22 @@ class InboxIssueAPIEndpoint(BaseAPIView): if issue.state.name == "Triage": # Move to default state state = State.objects.filter( - workspace__slug=slug, project_id=project_id, default=True + workspace__slug=slug, + project_id=project_id, + default=True, ).first() if state is not None: issue.state = state issue.save() return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) else: return Response( - InboxIssueSerializer(inbox_issue).data, status=status.HTTP_200_OK + InboxIssueSerializer(inbox_issue).data, + status=status.HTTP_200_OK, ) def delete(self, request, slug, project_id, issue_id): diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index 1ac8ddcff..e91f2a5f6 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -67,7 +67,9 @@ class IssueAPIEndpoint(WebhookMixin, BaseAPIView): def get_queryset(self): return ( Issue.issue_objects.annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -86,7 +88,9 @@ class IssueAPIEndpoint(WebhookMixin, BaseAPIView): def get(self, request, slug, project_id, pk=None): if pk: issue = Issue.issue_objects.annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -102,7 +106,13 @@ class IssueAPIEndpoint(WebhookMixin, BaseAPIView): # Custom ordering for priority and state priority_order = ["urgent", "high", "medium", "low", "none"] - state_order = ["backlog", "unstarted", "started", "completed", "cancelled"] + state_order = [ + "backlog", + "unstarted", + "started", + "completed", + "cancelled", + ] order_by_param = request.GET.get("order_by", "-created_at") @@ -117,7 +127,9 @@ class IssueAPIEndpoint(WebhookMixin, BaseAPIView): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -127,7 +139,9 @@ class IssueAPIEndpoint(WebhookMixin, BaseAPIView): # Priority Ordering if order_by_param == "priority" or order_by_param == "-priority": priority_order = ( - priority_order if order_by_param == "priority" else priority_order[::-1] + priority_order + if order_by_param == "priority" + else priority_order[::-1] ) issue_queryset = issue_queryset.annotate( priority_order=Case( @@ -175,7 +189,9 @@ class IssueAPIEndpoint(WebhookMixin, BaseAPIView): else order_by_param ) ).order_by( - "-max_values" if order_by_param.startswith("-") else "max_values" + "-max_values" + if order_by_param.startswith("-") + else "max_values" ) else: issue_queryset = issue_queryset.order_by(order_by_param) @@ -209,7 +225,9 @@ class IssueAPIEndpoint(WebhookMixin, BaseAPIView): # Track the issue issue_activity.delay( type="issue.activity.created", - requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), + requested_data=json.dumps( + self.request.data, cls=DjangoJSONEncoder + ), actor_id=str(request.user.id), issue_id=str(serializer.data.get("id", None)), project_id=str(project_id), @@ -220,7 +238,9 @@ class IssueAPIEndpoint(WebhookMixin, BaseAPIView): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def patch(self, request, slug, project_id, pk=None): - issue = Issue.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) + issue = Issue.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk + ) project = Project.objects.get(pk=project_id) current_instance = json.dumps( IssueSerializer(issue).data, cls=DjangoJSONEncoder @@ -250,7 +270,9 @@ class IssueAPIEndpoint(WebhookMixin, BaseAPIView): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, slug, project_id, pk=None): - issue = Issue.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) + issue = Issue.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk + ) current_instance = json.dumps( IssueSerializer(issue).data, cls=DjangoJSONEncoder ) @@ -297,11 +319,17 @@ class LabelAPIEndpoint(BaseAPIView): serializer = LabelSerializer(data=request.data) if serializer.is_valid(): serializer.save(project_id=project_id) - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + serializer.data, status=status.HTTP_201_CREATED + ) + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) except IntegrityError: return Response( - {"error": "Label with the same name already exists in the project"}, + { + "error": "Label with the same name already exists in the project" + }, status=status.HTTP_400_BAD_REQUEST, ) @@ -318,7 +346,11 @@ class LabelAPIEndpoint(BaseAPIView): ).data, ) label = self.get_queryset().get(pk=pk) - serializer = LabelSerializer(label, fields=self.fields, expand=self.expand,) + serializer = LabelSerializer( + label, + fields=self.fields, + expand=self.expand, + ) return Response(serializer.data, status=status.HTTP_200_OK) def patch(self, request, slug, project_id, pk=None): @@ -328,7 +360,6 @@ class LabelAPIEndpoint(BaseAPIView): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - def delete(self, request, slug, project_id, pk=None): label = self.get_queryset().get(pk=pk) @@ -395,7 +426,9 @@ class IssueLinkAPIEndpoint(BaseAPIView): ) issue_activity.delay( type="link.activity.created", - requested_data=json.dumps(serializer.data, cls=DjangoJSONEncoder), + requested_data=json.dumps( + serializer.data, cls=DjangoJSONEncoder + ), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id")), project_id=str(self.kwargs.get("project_id")), @@ -407,14 +440,19 @@ class IssueLinkAPIEndpoint(BaseAPIView): def patch(self, request, slug, project_id, issue_id, pk): issue_link = IssueLink.objects.get( - workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk + workspace__slug=slug, + project_id=project_id, + issue_id=issue_id, + pk=pk, ) requested_data = json.dumps(request.data, cls=DjangoJSONEncoder) current_instance = json.dumps( IssueLinkSerializer(issue_link).data, cls=DjangoJSONEncoder, ) - serializer = IssueLinkSerializer(issue_link, data=request.data, partial=True) + serializer = IssueLinkSerializer( + issue_link, data=request.data, partial=True + ) if serializer.is_valid(): serializer.save() issue_activity.delay( @@ -431,7 +469,10 @@ class IssueLinkAPIEndpoint(BaseAPIView): def delete(self, request, slug, project_id, issue_id, pk): issue_link = IssueLink.objects.get( - workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk + workspace__slug=slug, + project_id=project_id, + issue_id=issue_id, + pk=pk, ) current_instance = json.dumps( IssueLinkSerializer(issue_link).data, @@ -466,7 +507,9 @@ class IssueCommentAPIEndpoint(WebhookMixin, BaseAPIView): def get_queryset(self): return ( - IssueComment.objects.filter(workspace__slug=self.kwargs.get("slug")) + IssueComment.objects.filter( + workspace__slug=self.kwargs.get("slug") + ) .filter(project_id=self.kwargs.get("project_id")) .filter(issue_id=self.kwargs.get("issue_id")) .filter(project__project_projectmember__member=self.request.user) @@ -518,7 +561,9 @@ class IssueCommentAPIEndpoint(WebhookMixin, BaseAPIView): ) issue_activity.delay( type="comment.activity.created", - requested_data=json.dumps(serializer.data, cls=DjangoJSONEncoder), + requested_data=json.dumps( + serializer.data, cls=DjangoJSONEncoder + ), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id")), project_id=str(self.kwargs.get("project_id")), @@ -530,7 +575,10 @@ class IssueCommentAPIEndpoint(WebhookMixin, BaseAPIView): def patch(self, request, slug, project_id, issue_id, pk): issue_comment = IssueComment.objects.get( - workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk + workspace__slug=slug, + project_id=project_id, + issue_id=issue_id, + pk=pk, ) requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder) current_instance = json.dumps( @@ -556,7 +604,10 @@ class IssueCommentAPIEndpoint(WebhookMixin, BaseAPIView): def delete(self, request, slug, project_id, issue_id, pk): issue_comment = IssueComment.objects.get( - workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk + workspace__slug=slug, + project_id=project_id, + issue_id=issue_id, + pk=pk, ) current_instance = json.dumps( IssueCommentSerializer(issue_comment).data, @@ -591,7 +642,7 @@ class IssueActivityAPIEndpoint(BaseAPIView): ) .select_related("actor", "workspace", "issue", "project") ).order_by(request.GET.get("order_by", "created_at")) - + if pk: issue_activities = issue_activities.get(pk=pk) serializer = IssueActivitySerializer(issue_activities) diff --git a/apiserver/plane/api/views/module.py b/apiserver/plane/api/views/module.py index 959b7ccc3..1a9a21a3c 100644 --- a/apiserver/plane/api/views/module.py +++ b/apiserver/plane/api/views/module.py @@ -55,7 +55,9 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): .prefetch_related( Prefetch( "link_module", - queryset=ModuleLink.objects.select_related("module", "created_by"), + queryset=ModuleLink.objects.select_related( + "module", "created_by" + ), ) ) .annotate( @@ -122,17 +124,30 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): def post(self, request, slug, project_id): project = Project.objects.get(pk=project_id, workspace__slug=slug) - serializer = ModuleSerializer(data=request.data, context={"project_id": project_id, "workspace_id": project.workspace_id}) + serializer = ModuleSerializer( + data=request.data, + context={ + "project_id": project_id, + "workspace_id": project.workspace_id, + }, + ) if serializer.is_valid(): serializer.save() module = Module.objects.get(pk=serializer.data["id"]) serializer = ModuleSerializer(module) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - + def patch(self, request, slug, project_id, pk): - module = Module.objects.get(pk=pk, project_id=project_id, workspace__slug=slug) - serializer = ModuleSerializer(module, data=request.data, context={"project_id": project_id}, partial=True) + module = Module.objects.get( + pk=pk, project_id=project_id, workspace__slug=slug + ) + serializer = ModuleSerializer( + module, + data=request.data, + context={"project_id": project_id}, + partial=True, + ) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -162,9 +177,13 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): ) def delete(self, request, slug, project_id, pk): - module = Module.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) + module = Module.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk + ) module_issues = list( - ModuleIssue.objects.filter(module_id=pk).values_list("issue", flat=True) + ModuleIssue.objects.filter(module_id=pk).values_list( + "issue", flat=True + ) ) issue_activity.delay( type="module.activity.deleted", @@ -204,7 +223,9 @@ class ModuleIssueAPIEndpoint(WebhookMixin, BaseAPIView): def get_queryset(self): return ( ModuleIssue.objects.annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("issue")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("issue") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -228,7 +249,9 @@ class ModuleIssueAPIEndpoint(WebhookMixin, BaseAPIView): issues = ( Issue.issue_objects.filter(issue_module__module_id=module_id) .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -250,7 +273,9 @@ class ModuleIssueAPIEndpoint(WebhookMixin, BaseAPIView): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -271,7 +296,8 @@ class ModuleIssueAPIEndpoint(WebhookMixin, BaseAPIView): issues = request.data.get("issues", []) if not len(issues): return Response( - {"error": "Issues are required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Issues are required"}, + status=status.HTTP_400_BAD_REQUEST, ) module = Module.objects.get( workspace__slug=slug, project_id=project_id, pk=module_id @@ -354,7 +380,10 @@ class ModuleIssueAPIEndpoint(WebhookMixin, BaseAPIView): def delete(self, request, slug, project_id, module_id, issue_id): module_issue = ModuleIssue.objects.get( - workspace__slug=slug, project_id=project_id, module_id=module_id, issue_id=issue_id + workspace__slug=slug, + project_id=project_id, + module_id=module_id, + issue_id=issue_id, ) module_issue.delete() issue_activity.delay( @@ -371,4 +400,4 @@ class ModuleIssueAPIEndpoint(WebhookMixin, BaseAPIView): current_instance=None, epoch=int(timezone.now().timestamp()), ) - return Response(status=status.HTTP_204_NO_CONTENT) \ No newline at end of file + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apiserver/plane/api/views/project.py b/apiserver/plane/api/views/project.py index e8dc9f5a9..cb1f7dc7b 100644 --- a/apiserver/plane/api/views/project.py +++ b/apiserver/plane/api/views/project.py @@ -39,9 +39,15 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView): def get_queryset(self): return ( Project.objects.filter(workspace__slug=self.kwargs.get("slug")) - .filter(Q(project_projectmember__member=self.request.user) | Q(network=2)) + .filter( + Q(project_projectmember__member=self.request.user) + | Q(network=2) + ) .select_related( - "workspace", "workspace__owner", "default_assignee", "project_lead" + "workspace", + "workspace__owner", + "default_assignee", + "project_lead", ) .annotate( is_member=Exists( @@ -120,11 +126,18 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView): request=request, queryset=(projects), on_results=lambda projects: ProjectSerializer( - projects, many=True, fields=self.fields, expand=self.expand, + projects, + many=True, + fields=self.fields, + expand=self.expand, ).data, ) project = self.get_queryset().get(workspace__slug=slug, pk=project_id) - serializer = ProjectSerializer(project, fields=self.fields, expand=self.expand,) + serializer = ProjectSerializer( + project, + fields=self.fields, + expand=self.expand, + ) return Response(serializer.data, status=status.HTTP_200_OK) def post(self, request, slug): @@ -138,7 +151,9 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView): # Add the user as Administrator to the project project_member = ProjectMember.objects.create( - project_id=serializer.data["id"], member=request.user, role=20 + project_id=serializer.data["id"], + member=request.user, + role=20, ) # Also create the issue property for the user _ = IssueProperty.objects.create( @@ -211,9 +226,15 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView): ] ) - project = self.get_queryset().filter(pk=serializer.data["id"]).first() + project = ( + self.get_queryset() + .filter(pk=serializer.data["id"]) + .first() + ) serializer = ProjectSerializer(project) - return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response( + serializer.data, status=status.HTTP_201_CREATED + ) return Response( serializer.errors, status=status.HTTP_400_BAD_REQUEST, @@ -226,7 +247,8 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView): ) except Workspace.DoesNotExist as e: return Response( - {"error": "Workspace does not exist"}, status=status.HTTP_404_NOT_FOUND + {"error": "Workspace does not exist"}, + status=status.HTTP_404_NOT_FOUND, ) except ValidationError as e: return Response( @@ -250,7 +272,9 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView): serializer.save() if serializer.data["inbox_view"]: Inbox.objects.get_or_create( - name=f"{project.name} Inbox", project=project, is_default=True + name=f"{project.name} Inbox", + project=project, + is_default=True, ) # Create the triage state in Backlog group @@ -262,10 +286,16 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView): color="#ff7700", ) - project = self.get_queryset().filter(pk=serializer.data["id"]).first() + project = ( + self.get_queryset() + .filter(pk=serializer.data["id"]) + .first() + ) serializer = ProjectSerializer(project) return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) except IntegrityError as e: if "already exists" in str(e): return Response( @@ -274,7 +304,8 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView): ) except (Project.DoesNotExist, Workspace.DoesNotExist): return Response( - {"error": "Project does not exist"}, status=status.HTTP_404_NOT_FOUND + {"error": "Project does not exist"}, + status=status.HTTP_404_NOT_FOUND, ) except ValidationError as e: return Response( @@ -285,4 +316,4 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView): def delete(self, request, slug, project_id): project = Project.objects.get(pk=project_id, workspace__slug=slug) project.delete() - return Response(status=status.HTTP_204_NO_CONTENT) \ No newline at end of file + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apiserver/plane/api/views/state.py b/apiserver/plane/api/views/state.py index 3d2861778..f931c2ed2 100644 --- a/apiserver/plane/api/views/state.py +++ b/apiserver/plane/api/views/state.py @@ -34,7 +34,9 @@ class StateAPIEndpoint(BaseAPIView): ) def post(self, request, slug, project_id): - serializer = StateSerializer(data=request.data, context={"project_id": project_id}) + serializer = StateSerializer( + data=request.data, context={"project_id": project_id} + ) if serializer.is_valid(): serializer.save(project_id=project_id) return Response(serializer.data, status=status.HTTP_200_OK) @@ -64,14 +66,19 @@ class StateAPIEndpoint(BaseAPIView): ) if state.default: - return Response({"error": "Default state cannot be deleted"}, status=status.HTTP_400_BAD_REQUEST) + return Response( + {"error": "Default state cannot be deleted"}, + status=status.HTTP_400_BAD_REQUEST, + ) # Check for any issues in the state issue_exist = Issue.issue_objects.filter(state=state_id).exists() if issue_exist: return Response( - {"error": "The state is not empty, only empty states can be deleted"}, + { + "error": "The state is not empty, only empty states can be deleted" + }, status=status.HTTP_400_BAD_REQUEST, ) @@ -79,9 +86,11 @@ class StateAPIEndpoint(BaseAPIView): return Response(status=status.HTTP_204_NO_CONTENT) def patch(self, request, slug, project_id, state_id=None): - state = State.objects.get(workspace__slug=slug, project_id=project_id, pk=state_id) + state = State.objects.get( + workspace__slug=slug, project_id=project_id, pk=state_id + ) serializer = StateSerializer(state, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/apiserver/plane/app/middleware/api_authentication.py b/apiserver/plane/app/middleware/api_authentication.py index ddabb4132..893df7f84 100644 --- a/apiserver/plane/app/middleware/api_authentication.py +++ b/apiserver/plane/app/middleware/api_authentication.py @@ -25,7 +25,10 @@ class APIKeyAuthentication(authentication.BaseAuthentication): def validate_api_token(self, token): try: api_token = APIToken.objects.get( - Q(Q(expired_at__gt=timezone.now()) | Q(expired_at__isnull=True)), + Q( + Q(expired_at__gt=timezone.now()) + | Q(expired_at__isnull=True) + ), token=token, is_active=True, ) diff --git a/apiserver/plane/app/permissions/__init__.py b/apiserver/plane/app/permissions/__init__.py index 2298f3442..8e8793504 100644 --- a/apiserver/plane/app/permissions/__init__.py +++ b/apiserver/plane/app/permissions/__init__.py @@ -1,4 +1,3 @@ - from .workspace import ( WorkSpaceBasePermission, WorkspaceOwnerPermission, @@ -13,5 +12,3 @@ from .project import ( ProjectMemberPermission, ProjectLitePermission, ) - - diff --git a/apiserver/plane/app/serializers/__init__.py b/apiserver/plane/app/serializers/__init__.py index 4e0c12fe5..e1f322fde 100644 --- a/apiserver/plane/app/serializers/__init__.py +++ b/apiserver/plane/app/serializers/__init__.py @@ -35,7 +35,11 @@ from .project import ( ProjectMemberRoleSerializer, ) from .state import StateSerializer, StateLiteSerializer -from .view import GlobalViewSerializer, IssueViewSerializer, IssueViewFavoriteSerializer +from .view import ( + GlobalViewSerializer, + IssueViewSerializer, + IssueViewFavoriteSerializer, +) from .cycle import ( CycleSerializer, CycleIssueSerializer, @@ -65,7 +69,6 @@ from .issue import ( RelatedIssueSerializer, IssuePublicSerializer, IssueRelationLiteSerializer, - ) from .module import ( @@ -91,7 +94,12 @@ from .integration import ( from .importer import ImporterSerializer -from .page import PageSerializer, PageLogSerializer, SubPageSerializer, PageFavoriteSerializer +from .page import ( + PageSerializer, + PageLogSerializer, + SubPageSerializer, + PageFavoriteSerializer, +) from .estimate import ( EstimateSerializer, @@ -99,7 +107,11 @@ from .estimate import ( EstimateReadSerializer, ) -from .inbox import InboxSerializer, InboxIssueSerializer, IssueStateInboxSerializer +from .inbox import ( + InboxSerializer, + InboxIssueSerializer, + IssueStateInboxSerializer, +) from .analytic import AnalyticViewSerializer @@ -107,4 +119,4 @@ from .notification import NotificationSerializer from .exporter import ExporterHistorySerializer -from .webhook import WebhookSerializer, WebhookLogSerializer \ No newline at end of file +from .webhook import WebhookSerializer, WebhookLogSerializer diff --git a/apiserver/plane/app/serializers/api.py b/apiserver/plane/app/serializers/api.py index 08bb747d9..264a58f92 100644 --- a/apiserver/plane/app/serializers/api.py +++ b/apiserver/plane/app/serializers/api.py @@ -3,7 +3,6 @@ from plane.db.models import APIToken, APIActivityLog class APITokenSerializer(BaseSerializer): - class Meta: model = APIToken fields = "__all__" @@ -18,14 +17,12 @@ class APITokenSerializer(BaseSerializer): class APITokenReadSerializer(BaseSerializer): - class Meta: model = APIToken - exclude = ('token',) + exclude = ("token",) class APIActivityLogSerializer(BaseSerializer): - class Meta: model = APIActivityLog fields = "__all__" diff --git a/apiserver/plane/app/serializers/base.py b/apiserver/plane/app/serializers/base.py index d25d3b4b0..f617124bf 100644 --- a/apiserver/plane/app/serializers/base.py +++ b/apiserver/plane/app/serializers/base.py @@ -4,8 +4,8 @@ from rest_framework import serializers class BaseSerializer(serializers.ModelSerializer): id = serializers.PrimaryKeyRelatedField(read_only=True) -class DynamicBaseSerializer(BaseSerializer): +class DynamicBaseSerializer(BaseSerializer): def __init__(self, *args, **kwargs): # If 'fields' is provided in the arguments, remove it and store it separately. # This is done so as not to pass this custom argument up to the superclass. @@ -32,7 +32,7 @@ class DynamicBaseSerializer(BaseSerializer): # loop through its keys and values. if isinstance(field_name, dict): for key, value in field_name.items(): - # If the value of this nested field is a list, + # If the value of this nested field is a list, # perform a recursive filter on it. if isinstance(value, list): self._filter_fields(self.fields[key], value) @@ -79,12 +79,16 @@ class DynamicBaseSerializer(BaseSerializer): "issue_cycle": CycleIssueSerializer, "parent": IssueSerializer, } - - self.fields[field] = expansion[field](many=True if field in ["members", "assignees", "labels", "issue_cycle"] else False) + + self.fields[field] = expansion[field]( + many=True + if field + in ["members", "assignees", "labels", "issue_cycle"] + else False + ) return self.fields - def to_representation(self, instance): response = super().to_representation(instance) @@ -134,6 +138,8 @@ class DynamicBaseSerializer(BaseSerializer): response[expand] = exp_serializer.data else: # You might need to handle this case differently - response[expand] = getattr(instance, f"{expand}_id", None) + response[expand] = getattr( + instance, f"{expand}_id", None + ) return response diff --git a/apiserver/plane/app/serializers/cycle.py b/apiserver/plane/app/serializers/cycle.py index f0ee8f9da..a041dd227 100644 --- a/apiserver/plane/app/serializers/cycle.py +++ b/apiserver/plane/app/serializers/cycle.py @@ -7,7 +7,12 @@ from .user import UserLiteSerializer from .issue import IssueStateSerializer from .workspace import WorkspaceLiteSerializer from .project import ProjectLiteSerializer -from plane.db.models import Cycle, CycleIssue, CycleFavorite, CycleUserProperties +from plane.db.models import ( + Cycle, + CycleIssue, + CycleFavorite, + CycleUserProperties, +) class CycleWriteSerializer(BaseSerializer): @@ -17,7 +22,9 @@ class CycleWriteSerializer(BaseSerializer): and data.get("end_date", None) is not None and data.get("start_date", None) > data.get("end_date", None) ): - raise serializers.ValidationError("Start date cannot exceed end date") + raise serializers.ValidationError( + "Start date cannot exceed end date" + ) return data class Meta: @@ -38,7 +45,9 @@ class CycleSerializer(BaseSerializer): total_estimates = serializers.IntegerField(read_only=True) completed_estimates = serializers.IntegerField(read_only=True) started_estimates = serializers.IntegerField(read_only=True) - workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") + workspace_detail = WorkspaceLiteSerializer( + read_only=True, source="workspace" + ) project_detail = ProjectLiteSerializer(read_only=True, source="project") status = serializers.CharField(read_only=True) @@ -48,7 +57,9 @@ class CycleSerializer(BaseSerializer): and data.get("end_date", None) is not None and data.get("start_date", None) > data.get("end_date", None) ): - raise serializers.ValidationError("Start date cannot exceed end date") + raise serializers.ValidationError( + "Start date cannot exceed end date" + ) return data def get_assignees(self, obj): @@ -115,6 +126,5 @@ class CycleUserPropertiesSerializer(BaseSerializer): read_only_fields = [ "workspace", "project", - "cycle" - "user", - ] \ No newline at end of file + "cycle" "user", + ] diff --git a/apiserver/plane/app/serializers/estimate.py b/apiserver/plane/app/serializers/estimate.py index 2c2f26e4e..a563c6956 100644 --- a/apiserver/plane/app/serializers/estimate.py +++ b/apiserver/plane/app/serializers/estimate.py @@ -2,12 +2,18 @@ from .base import BaseSerializer from plane.db.models import Estimate, EstimatePoint -from plane.app.serializers import WorkspaceLiteSerializer, ProjectLiteSerializer +from plane.app.serializers import ( + WorkspaceLiteSerializer, + ProjectLiteSerializer, +) from rest_framework import serializers + class EstimateSerializer(BaseSerializer): - workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") + workspace_detail = WorkspaceLiteSerializer( + read_only=True, source="workspace" + ) project_detail = ProjectLiteSerializer(read_only=True, source="project") class Meta: @@ -20,13 +26,14 @@ class EstimateSerializer(BaseSerializer): class EstimatePointSerializer(BaseSerializer): - def validate(self, data): if not data: raise serializers.ValidationError("Estimate points are required") value = data.get("value") if value and len(value) > 20: - raise serializers.ValidationError("Value can't be more than 20 characters") + raise serializers.ValidationError( + "Value can't be more than 20 characters" + ) return data class Meta: @@ -41,7 +48,9 @@ class EstimatePointSerializer(BaseSerializer): class EstimateReadSerializer(BaseSerializer): points = EstimatePointSerializer(read_only=True, many=True) - workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") + workspace_detail = WorkspaceLiteSerializer( + read_only=True, source="workspace" + ) project_detail = ProjectLiteSerializer(read_only=True, source="project") class Meta: diff --git a/apiserver/plane/app/serializers/exporter.py b/apiserver/plane/app/serializers/exporter.py index 5c78cfa69..2dd850fd3 100644 --- a/apiserver/plane/app/serializers/exporter.py +++ b/apiserver/plane/app/serializers/exporter.py @@ -5,7 +5,9 @@ from .user import UserLiteSerializer class ExporterHistorySerializer(BaseSerializer): - initiated_by_detail = UserLiteSerializer(source="initiated_by", read_only=True) + initiated_by_detail = UserLiteSerializer( + source="initiated_by", read_only=True + ) class Meta: model = ExporterHistory diff --git a/apiserver/plane/app/serializers/importer.py b/apiserver/plane/app/serializers/importer.py index 8997f6392..c058994d6 100644 --- a/apiserver/plane/app/serializers/importer.py +++ b/apiserver/plane/app/serializers/importer.py @@ -7,9 +7,13 @@ from plane.db.models import Importer class ImporterSerializer(BaseSerializer): - initiated_by_detail = UserLiteSerializer(source="initiated_by", read_only=True) + initiated_by_detail = UserLiteSerializer( + source="initiated_by", read_only=True + ) project_detail = ProjectLiteSerializer(source="project", read_only=True) - workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) + workspace_detail = WorkspaceLiteSerializer( + source="workspace", read_only=True + ) class Meta: model = Importer diff --git a/apiserver/plane/app/serializers/inbox.py b/apiserver/plane/app/serializers/inbox.py index cdc2646dd..1dc6f1f4a 100644 --- a/apiserver/plane/app/serializers/inbox.py +++ b/apiserver/plane/app/serializers/inbox.py @@ -46,8 +46,12 @@ class InboxIssueLiteSerializer(BaseSerializer): class IssueStateInboxSerializer(BaseSerializer): state_detail = StateLiteSerializer(read_only=True, source="state") project_detail = ProjectLiteSerializer(read_only=True, source="project") - label_details = LabelLiteSerializer(read_only=True, source="labels", many=True) - assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True) + label_details = LabelLiteSerializer( + read_only=True, source="labels", many=True + ) + assignee_details = UserLiteSerializer( + read_only=True, source="assignees", many=True + ) sub_issues_count = serializers.IntegerField(read_only=True) issue_inbox = InboxIssueLiteSerializer(read_only=True, many=True) diff --git a/apiserver/plane/app/serializers/integration/base.py b/apiserver/plane/app/serializers/integration/base.py index 6f6543b9e..01e484ed0 100644 --- a/apiserver/plane/app/serializers/integration/base.py +++ b/apiserver/plane/app/serializers/integration/base.py @@ -13,7 +13,9 @@ class IntegrationSerializer(BaseSerializer): class WorkspaceIntegrationSerializer(BaseSerializer): - integration_detail = IntegrationSerializer(read_only=True, source="integration") + integration_detail = IntegrationSerializer( + read_only=True, source="integration" + ) class Meta: model = WorkspaceIntegration diff --git a/apiserver/plane/app/serializers/issue.py b/apiserver/plane/app/serializers/issue.py index 64ae9ccfb..322caa275 100644 --- a/apiserver/plane/app/serializers/issue.py +++ b/apiserver/plane/app/serializers/issue.py @@ -72,8 +72,18 @@ class IssueProjectLiteSerializer(BaseSerializer): ## Find a better approach to save manytomany? class IssueCreateSerializer(BaseSerializer): # ids - state_id = serializers.PrimaryKeyRelatedField(source="state", queryset=State.objects.all(), required=False, allow_null=True) - parent_id = serializers.PrimaryKeyRelatedField(source='parent', queryset=Issue.objects.all(), required=False, allow_null=True) + state_id = serializers.PrimaryKeyRelatedField( + source="state", + queryset=State.objects.all(), + required=False, + allow_null=True, + ) + parent_id = serializers.PrimaryKeyRelatedField( + source="parent", + queryset=Issue.objects.all(), + required=False, + allow_null=True, + ) label_ids = serializers.ListField( child=serializers.PrimaryKeyRelatedField(queryset=Label.objects.all()), write_only=True, @@ -99,10 +109,10 @@ class IssueCreateSerializer(BaseSerializer): def to_representation(self, instance): data = super().to_representation(instance) - assignee_ids = self.initial_data.get('assignee_ids') - data['assignee_ids'] = assignee_ids if assignee_ids else [] - label_ids = self.initial_data.get('label_ids') - data['label_ids'] = label_ids if label_ids else [] + assignee_ids = self.initial_data.get("assignee_ids") + data["assignee_ids"] = assignee_ids if assignee_ids else [] + label_ids = self.initial_data.get("label_ids") + data["label_ids"] = label_ids if label_ids else [] return data def validate(self, data): @@ -111,7 +121,9 @@ class IssueCreateSerializer(BaseSerializer): and data.get("target_date", None) is not None and data.get("start_date", None) > data.get("target_date", None) ): - raise serializers.ValidationError("Start date cannot exceed target date") + raise serializers.ValidationError( + "Start date cannot exceed target date" + ) return data def create(self, validated_data): @@ -226,14 +238,15 @@ class IssueActivitySerializer(BaseSerializer): actor_detail = UserLiteSerializer(read_only=True, source="actor") issue_detail = IssueFlatSerializer(read_only=True, source="issue") project_detail = ProjectLiteSerializer(read_only=True, source="project") - workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") + workspace_detail = WorkspaceLiteSerializer( + read_only=True, source="workspace" + ) class Meta: model = IssueActivity fields = "__all__" - class IssuePropertySerializer(BaseSerializer): class Meta: model = IssueProperty @@ -246,7 +259,9 @@ class IssuePropertySerializer(BaseSerializer): class LabelSerializer(BaseSerializer): - workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) + workspace_detail = WorkspaceLiteSerializer( + source="workspace", read_only=True + ) project_detail = ProjectLiteSerializer(source="project", read_only=True) class Meta: @@ -269,7 +284,6 @@ class LabelLiteSerializer(BaseSerializer): class IssueLabelSerializer(BaseSerializer): - class Meta: model = IssueLabel fields = "__all__" @@ -281,6 +295,7 @@ class IssueLabelSerializer(BaseSerializer): class IssueRelationLiteSerializer(DynamicBaseSerializer): project_id = serializers.PrimaryKeyRelatedField(read_only=True) + class Meta: model = Issue fields = [ @@ -295,7 +310,9 @@ class IssueRelationLiteSerializer(DynamicBaseSerializer): class IssueRelationSerializer(BaseSerializer): - issue_detail = IssueRelationLiteSerializer(read_only=True, source="related_issue") + issue_detail = IssueRelationLiteSerializer( + read_only=True, source="related_issue" + ) class Meta: model = IssueRelation @@ -307,6 +324,7 @@ class IssueRelationSerializer(BaseSerializer): "project", ] + class RelatedIssueSerializer(BaseSerializer): issue_detail = IssueRelationLiteSerializer(read_only=True, source="issue") @@ -408,7 +426,8 @@ class IssueLinkSerializer(BaseSerializer): # Validation if url already exists def create(self, validated_data): if IssueLink.objects.filter( - url=validated_data.get("url"), issue_id=validated_data.get("issue_id") + url=validated_data.get("url"), + issue_id=validated_data.get("issue_id"), ).exists(): raise serializers.ValidationError( {"error": "URL already exists for this Issue"} @@ -432,9 +451,8 @@ class IssueAttachmentSerializer(BaseSerializer): class IssueReactionSerializer(BaseSerializer): - actor_detail = UserLiteSerializer(read_only=True, source="actor") - + class Meta: model = IssueReaction fields = "__all__" @@ -467,12 +485,18 @@ class CommentReactionSerializer(BaseSerializer): class IssueVoteSerializer(BaseSerializer): - actor_detail = UserLiteSerializer(read_only=True, source="actor") class Meta: model = IssueVote - fields = ["issue", "vote", "workspace", "project", "actor", "actor_detail"] + fields = [ + "issue", + "vote", + "workspace", + "project", + "actor", + "actor_detail", + ] read_only_fields = fields @@ -480,8 +504,12 @@ class IssueCommentSerializer(BaseSerializer): actor_detail = UserLiteSerializer(read_only=True, source="actor") issue_detail = IssueFlatSerializer(read_only=True, source="issue") project_detail = ProjectLiteSerializer(read_only=True, source="project") - workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") - comment_reactions = CommentReactionLiteSerializer(read_only=True, many=True) + workspace_detail = WorkspaceLiteSerializer( + read_only=True, source="workspace" + ) + comment_reactions = CommentReactionLiteSerializer( + read_only=True, many=True + ) is_member = serializers.BooleanField(read_only=True) class Meta: @@ -515,10 +543,14 @@ class IssueStateFlatSerializer(BaseSerializer): # Issue Serializer with state details class IssueStateSerializer(DynamicBaseSerializer): - label_details = LabelLiteSerializer(read_only=True, source="labels", many=True) + label_details = LabelLiteSerializer( + read_only=True, source="labels", many=True + ) state_detail = StateLiteSerializer(read_only=True, source="state") project_detail = ProjectLiteSerializer(read_only=True, source="project") - assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True) + assignee_details = UserLiteSerializer( + read_only=True, source="assignees", many=True + ) sub_issues_count = serializers.IntegerField(read_only=True) attachment_count = serializers.IntegerField(read_only=True) link_count = serializers.IntegerField(read_only=True) @@ -537,8 +569,12 @@ class IssueSerializer(DynamicBaseSerializer): module_id = serializers.PrimaryKeyRelatedField(read_only=True) # Many to many - label_ids = serializers.PrimaryKeyRelatedField(read_only=True, many=True, source="labels") - assignee_ids = serializers.PrimaryKeyRelatedField(read_only=True, many=True, source="assignees") + label_ids = serializers.PrimaryKeyRelatedField( + read_only=True, many=True, source="labels" + ) + assignee_ids = serializers.PrimaryKeyRelatedField( + read_only=True, many=True, source="assignees" + ) # Count items sub_issues_count = serializers.IntegerField(read_only=True) @@ -583,11 +619,17 @@ class IssueSerializer(DynamicBaseSerializer): class IssueLiteSerializer(DynamicBaseSerializer): - workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") + workspace_detail = WorkspaceLiteSerializer( + read_only=True, source="workspace" + ) project_detail = ProjectLiteSerializer(read_only=True, source="project") state_detail = StateLiteSerializer(read_only=True, source="state") - label_details = LabelLiteSerializer(read_only=True, source="labels", many=True) - assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True) + label_details = LabelLiteSerializer( + read_only=True, source="labels", many=True + ) + assignee_details = UserLiteSerializer( + read_only=True, source="assignees", many=True + ) sub_issues_count = serializers.IntegerField(read_only=True) cycle_id = serializers.UUIDField(read_only=True) module_id = serializers.UUIDField(read_only=True) @@ -614,7 +656,9 @@ class IssueLiteSerializer(DynamicBaseSerializer): class IssuePublicSerializer(BaseSerializer): project_detail = ProjectLiteSerializer(read_only=True, source="project") state_detail = StateLiteSerializer(read_only=True, source="state") - reactions = IssueReactionSerializer(read_only=True, many=True, source="issue_reactions") + reactions = IssueReactionSerializer( + read_only=True, many=True, source="issue_reactions" + ) votes = IssueVoteSerializer(read_only=True, many=True) class Meta: @@ -637,7 +681,6 @@ class IssuePublicSerializer(BaseSerializer): read_only_fields = fields - class IssueSubscriberSerializer(BaseSerializer): class Meta: model = IssueSubscriber diff --git a/apiserver/plane/app/serializers/module.py b/apiserver/plane/app/serializers/module.py index b38d05b2c..e94195671 100644 --- a/apiserver/plane/app/serializers/module.py +++ b/apiserver/plane/app/serializers/module.py @@ -26,7 +26,9 @@ class ModuleWriteSerializer(BaseSerializer): ) project_detail = ProjectLiteSerializer(source="project", read_only=True) - workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) + workspace_detail = WorkspaceLiteSerializer( + source="workspace", read_only=True + ) class Meta: model = Module @@ -39,16 +41,22 @@ class ModuleWriteSerializer(BaseSerializer): "created_at", "updated_at", ] - + def to_representation(self, instance): data = super().to_representation(instance) - data['members'] = [str(member.id) for member in instance.members.all()] + data["members"] = [str(member.id) for member in instance.members.all()] return data def validate(self, data): - if data.get("start_date", None) is not None and data.get("target_date", None) is not None and data.get("start_date", None) > data.get("target_date", None): - raise serializers.ValidationError("Start date cannot exceed target date") - return data + if ( + data.get("start_date", None) is not None + and data.get("target_date", None) is not None + and data.get("start_date", None) > data.get("target_date", None) + ): + raise serializers.ValidationError( + "Start date cannot exceed target date" + ) + return data def create(self, validated_data): members = validated_data.pop("members", None) @@ -152,7 +160,8 @@ class ModuleLinkSerializer(BaseSerializer): # Validation if url already exists def create(self, validated_data): if ModuleLink.objects.filter( - url=validated_data.get("url"), module_id=validated_data.get("module_id") + url=validated_data.get("url"), + module_id=validated_data.get("module_id"), ).exists(): raise serializers.ValidationError( {"error": "URL already exists for this Issue"} @@ -163,7 +172,9 @@ class ModuleLinkSerializer(BaseSerializer): class ModuleSerializer(DynamicBaseSerializer): project_detail = ProjectLiteSerializer(read_only=True, source="project") lead_detail = UserLiteSerializer(read_only=True, source="lead") - members_detail = UserLiteSerializer(read_only=True, many=True, source="members") + members_detail = UserLiteSerializer( + read_only=True, many=True, source="members" + ) link_module = ModuleLinkSerializer(read_only=True, many=True) is_favorite = serializers.BooleanField(read_only=True) total_issues = serializers.IntegerField(read_only=True) @@ -198,13 +209,9 @@ class ModuleFavoriteSerializer(BaseSerializer): "user", ] + class ModuleUserPropertiesSerializer(BaseSerializer): class Meta: model = ModuleUserProperties fields = "__all__" - read_only_fields = [ - "workspace", - "project", - "module", - "user" - ] \ No newline at end of file + read_only_fields = ["workspace", "project", "module", "user"] diff --git a/apiserver/plane/app/serializers/notification.py b/apiserver/plane/app/serializers/notification.py index b6a4f3e4a..70d876241 100644 --- a/apiserver/plane/app/serializers/notification.py +++ b/apiserver/plane/app/serializers/notification.py @@ -3,10 +3,12 @@ from .base import BaseSerializer from .user import UserLiteSerializer from plane.db.models import Notification + class NotificationSerializer(BaseSerializer): - triggered_by_details = UserLiteSerializer(read_only=True, source="triggered_by") + triggered_by_details = UserLiteSerializer( + read_only=True, source="triggered_by" + ) class Meta: model = Notification fields = "__all__" - diff --git a/apiserver/plane/app/serializers/page.py b/apiserver/plane/app/serializers/page.py index ff152627a..a0f5986d6 100644 --- a/apiserver/plane/app/serializers/page.py +++ b/apiserver/plane/app/serializers/page.py @@ -6,19 +6,31 @@ from .base import BaseSerializer from .issue import IssueFlatSerializer, LabelLiteSerializer from .workspace import WorkspaceLiteSerializer from .project import ProjectLiteSerializer -from plane.db.models import Page, PageLog, PageFavorite, PageLabel, Label, Issue, Module +from plane.db.models import ( + Page, + PageLog, + PageFavorite, + PageLabel, + Label, + Issue, + Module, +) class PageSerializer(BaseSerializer): is_favorite = serializers.BooleanField(read_only=True) - label_details = LabelLiteSerializer(read_only=True, source="labels", many=True) + label_details = LabelLiteSerializer( + read_only=True, source="labels", many=True + ) labels = serializers.ListField( child=serializers.PrimaryKeyRelatedField(queryset=Label.objects.all()), write_only=True, required=False, ) project_detail = ProjectLiteSerializer(source="project", read_only=True) - workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) + workspace_detail = WorkspaceLiteSerializer( + source="workspace", read_only=True + ) class Meta: model = Page @@ -28,9 +40,10 @@ class PageSerializer(BaseSerializer): "project", "owned_by", ] + def to_representation(self, instance): data = super().to_representation(instance) - data['labels'] = [str(label.id) for label in instance.labels.all()] + data["labels"] = [str(label.id) for label in instance.labels.all()] return data def create(self, validated_data): @@ -94,7 +107,7 @@ class SubPageSerializer(BaseSerializer): def get_entity_details(self, obj): entity_name = obj.entity_name - if entity_name == 'forward_link' or entity_name == 'back_link': + if entity_name == "forward_link" or entity_name == "back_link": try: page = Page.objects.get(pk=obj.entity_identifier) return PageSerializer(page).data @@ -104,7 +117,6 @@ class SubPageSerializer(BaseSerializer): class PageLogSerializer(BaseSerializer): - class Meta: model = PageLog fields = "__all__" diff --git a/apiserver/plane/app/serializers/project.py b/apiserver/plane/app/serializers/project.py index b3122962b..999233442 100644 --- a/apiserver/plane/app/serializers/project.py +++ b/apiserver/plane/app/serializers/project.py @@ -4,7 +4,10 @@ from rest_framework import serializers # Module imports from .base import BaseSerializer, DynamicBaseSerializer from plane.app.serializers.workspace import WorkspaceLiteSerializer -from plane.app.serializers.user import UserLiteSerializer, UserAdminLiteSerializer +from plane.app.serializers.user import ( + UserLiteSerializer, + UserAdminLiteSerializer, +) from plane.db.models import ( Project, ProjectMember, @@ -17,7 +20,9 @@ from plane.db.models import ( class ProjectSerializer(BaseSerializer): - workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) + workspace_detail = WorkspaceLiteSerializer( + source="workspace", read_only=True + ) class Meta: model = Project @@ -29,12 +34,16 @@ class ProjectSerializer(BaseSerializer): def create(self, validated_data): identifier = validated_data.get("identifier", "").strip().upper() if identifier == "": - raise serializers.ValidationError(detail="Project Identifier is required") + raise serializers.ValidationError( + detail="Project Identifier is required" + ) if ProjectIdentifier.objects.filter( name=identifier, workspace_id=self.context["workspace_id"] ).exists(): - raise serializers.ValidationError(detail="Project Identifier is taken") + raise serializers.ValidationError( + detail="Project Identifier is taken" + ) project = Project.objects.create( **validated_data, workspace_id=self.context["workspace_id"] ) @@ -73,7 +82,9 @@ class ProjectSerializer(BaseSerializer): return project # If not same fail update - raise serializers.ValidationError(detail="Project Identifier is already taken") + raise serializers.ValidationError( + detail="Project Identifier is already taken" + ) class ProjectLiteSerializer(BaseSerializer): @@ -159,12 +170,13 @@ class ProjectMemberAdminSerializer(BaseSerializer): model = ProjectMember fields = "__all__" -class ProjectMemberRoleSerializer(DynamicBaseSerializer): +class ProjectMemberRoleSerializer(DynamicBaseSerializer): class Meta: model = ProjectMember fields = ("id", "role", "member", "project") + class ProjectMemberInviteSerializer(BaseSerializer): project = ProjectLiteSerializer(read_only=True) workspace = WorkspaceLiteSerializer(read_only=True) @@ -202,7 +214,9 @@ class ProjectMemberLiteSerializer(BaseSerializer): class ProjectDeployBoardSerializer(BaseSerializer): project_details = ProjectLiteSerializer(read_only=True, source="project") - workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") + workspace_detail = WorkspaceLiteSerializer( + read_only=True, source="workspace" + ) class Meta: model = ProjectDeployBoard @@ -222,4 +236,4 @@ class ProjectPublicMemberSerializer(BaseSerializer): "workspace", "project", "member", - ] \ No newline at end of file + ] diff --git a/apiserver/plane/app/serializers/state.py b/apiserver/plane/app/serializers/state.py index 323254f26..25dded62d 100644 --- a/apiserver/plane/app/serializers/state.py +++ b/apiserver/plane/app/serializers/state.py @@ -6,7 +6,6 @@ from plane.db.models import State class StateSerializer(BaseSerializer): - class Meta: model = State fields = "__all__" @@ -25,4 +24,4 @@ class StateLiteSerializer(BaseSerializer): "color", "group", ] - read_only_fields = fields \ No newline at end of file + read_only_fields = fields diff --git a/apiserver/plane/app/serializers/user.py b/apiserver/plane/app/serializers/user.py index 1b94758e8..8cd48827e 100644 --- a/apiserver/plane/app/serializers/user.py +++ b/apiserver/plane/app/serializers/user.py @@ -99,7 +99,9 @@ class UserMeSettingsSerializer(BaseSerializer): ).first() return { "last_workspace_id": obj.last_workspace_id, - "last_workspace_slug": workspace.slug if workspace is not None else "", + "last_workspace_slug": workspace.slug + if workspace is not None + else "", "fallback_workspace_id": obj.last_workspace_id, "fallback_workspace_slug": workspace.slug if workspace is not None @@ -109,7 +111,8 @@ class UserMeSettingsSerializer(BaseSerializer): else: fallback_workspace = ( Workspace.objects.filter( - workspace_member__member_id=obj.id, workspace_member__is_active=True + workspace_member__member_id=obj.id, + workspace_member__is_active=True, ) .order_by("created_at") .first() @@ -180,7 +183,9 @@ class ChangePasswordSerializer(serializers.Serializer): if data.get("new_password") != data.get("confirm_password"): raise serializers.ValidationError( - {"error": "Confirm password should be same as the new password."} + { + "error": "Confirm password should be same as the new password." + } ) return data @@ -190,4 +195,5 @@ class ResetPasswordSerializer(serializers.Serializer): """ Serializer for password change endpoint. """ + new_password = serializers.CharField(required=True, min_length=8) diff --git a/apiserver/plane/app/serializers/view.py b/apiserver/plane/app/serializers/view.py index db44a2fc0..f864f2b6c 100644 --- a/apiserver/plane/app/serializers/view.py +++ b/apiserver/plane/app/serializers/view.py @@ -10,7 +10,9 @@ from plane.utils.issue_filters import issue_filters class GlobalViewSerializer(BaseSerializer): - workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) + workspace_detail = WorkspaceLiteSerializer( + source="workspace", read_only=True + ) class Meta: model = GlobalView @@ -41,7 +43,9 @@ class GlobalViewSerializer(BaseSerializer): class IssueViewSerializer(DynamicBaseSerializer): is_favorite = serializers.BooleanField(read_only=True) project_detail = ProjectLiteSerializer(source="project", read_only=True) - workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) + workspace_detail = WorkspaceLiteSerializer( + source="workspace", read_only=True + ) class Meta: model = IssueView @@ -80,4 +84,4 @@ class IssueViewFavoriteSerializer(BaseSerializer): "workspace", "project", "user", - ] \ No newline at end of file + ] diff --git a/apiserver/plane/app/serializers/webhook.py b/apiserver/plane/app/serializers/webhook.py index 961466d28..95ca149ff 100644 --- a/apiserver/plane/app/serializers/webhook.py +++ b/apiserver/plane/app/serializers/webhook.py @@ -10,78 +10,113 @@ from rest_framework import serializers # Module imports from .base import DynamicBaseSerializer from plane.db.models import Webhook, WebhookLog -from plane.db.models.webhook import validate_domain, validate_schema +from plane.db.models.webhook import validate_domain, validate_schema + class WebhookSerializer(DynamicBaseSerializer): url = serializers.URLField(validators=[validate_schema, validate_domain]) - + def create(self, validated_data): url = validated_data.get("url", None) # Extract the hostname from the URL hostname = urlparse(url).hostname if not hostname: - raise serializers.ValidationError({"url": "Invalid URL: No hostname found."}) + raise serializers.ValidationError( + {"url": "Invalid URL: No hostname found."} + ) # Resolve the hostname to IP addresses try: ip_addresses = socket.getaddrinfo(hostname, None) except socket.gaierror: - raise serializers.ValidationError({"url": "Hostname could not be resolved."}) + raise serializers.ValidationError( + {"url": "Hostname could not be resolved."} + ) if not ip_addresses: - raise serializers.ValidationError({"url": "No IP addresses found for the hostname."}) + raise serializers.ValidationError( + {"url": "No IP addresses found for the hostname."} + ) for addr in ip_addresses: ip = ipaddress.ip_address(addr[4][0]) if ip.is_private or ip.is_loopback: - raise serializers.ValidationError({"url": "URL resolves to a blocked IP address."}) + raise serializers.ValidationError( + {"url": "URL resolves to a blocked IP address."} + ) # Additional validation for multiple request domains and their subdomains - request = self.context.get('request') - disallowed_domains = ['plane.so',] # Add your disallowed domains here + request = self.context.get("request") + disallowed_domains = [ + "plane.so", + ] # Add your disallowed domains here if request: - request_host = request.get_host().split(':')[0] # Remove port if present + request_host = request.get_host().split(":")[ + 0 + ] # Remove port if present disallowed_domains.append(request_host) # Check if hostname is a subdomain or exact match of any disallowed domain - if any(hostname == domain or hostname.endswith('.' + domain) for domain in disallowed_domains): - raise serializers.ValidationError({"url": "URL domain or its subdomain is not allowed."}) + if any( + hostname == domain or hostname.endswith("." + domain) + for domain in disallowed_domains + ): + raise serializers.ValidationError( + {"url": "URL domain or its subdomain is not allowed."} + ) return Webhook.objects.create(**validated_data) - + def update(self, instance, validated_data): url = validated_data.get("url", None) if url: # Extract the hostname from the URL hostname = urlparse(url).hostname if not hostname: - raise serializers.ValidationError({"url": "Invalid URL: No hostname found."}) + raise serializers.ValidationError( + {"url": "Invalid URL: No hostname found."} + ) # Resolve the hostname to IP addresses try: ip_addresses = socket.getaddrinfo(hostname, None) except socket.gaierror: - raise serializers.ValidationError({"url": "Hostname could not be resolved."}) + raise serializers.ValidationError( + {"url": "Hostname could not be resolved."} + ) if not ip_addresses: - raise serializers.ValidationError({"url": "No IP addresses found for the hostname."}) + raise serializers.ValidationError( + {"url": "No IP addresses found for the hostname."} + ) for addr in ip_addresses: ip = ipaddress.ip_address(addr[4][0]) if ip.is_private or ip.is_loopback: - raise serializers.ValidationError({"url": "URL resolves to a blocked IP address."}) + raise serializers.ValidationError( + {"url": "URL resolves to a blocked IP address."} + ) # Additional validation for multiple request domains and their subdomains - request = self.context.get('request') - disallowed_domains = ['plane.so',] # Add your disallowed domains here + request = self.context.get("request") + disallowed_domains = [ + "plane.so", + ] # Add your disallowed domains here if request: - request_host = request.get_host().split(':')[0] # Remove port if present + request_host = request.get_host().split(":")[ + 0 + ] # Remove port if present disallowed_domains.append(request_host) # Check if hostname is a subdomain or exact match of any disallowed domain - if any(hostname == domain or hostname.endswith('.' + domain) for domain in disallowed_domains): - raise serializers.ValidationError({"url": "URL domain or its subdomain is not allowed."}) + if any( + hostname == domain or hostname.endswith("." + domain) + for domain in disallowed_domains + ): + raise serializers.ValidationError( + {"url": "URL domain or its subdomain is not allowed."} + ) return super().update(instance, validated_data) @@ -95,12 +130,7 @@ class WebhookSerializer(DynamicBaseSerializer): class WebhookLogSerializer(DynamicBaseSerializer): - class Meta: model = WebhookLog fields = "__all__" - read_only_fields = [ - "workspace", - "webhook" - ] - + read_only_fields = ["workspace", "webhook"] diff --git a/apiserver/plane/app/serializers/workspace.py b/apiserver/plane/app/serializers/workspace.py index fe014f364..69f827c24 100644 --- a/apiserver/plane/app/serializers/workspace.py +++ b/apiserver/plane/app/serializers/workspace.py @@ -51,6 +51,7 @@ class WorkSpaceSerializer(DynamicBaseSerializer): "owner", ] + class WorkspaceLiteSerializer(BaseSerializer): class Meta: model = Workspace @@ -62,7 +63,6 @@ class WorkspaceLiteSerializer(BaseSerializer): read_only_fields = fields - class WorkSpaceMemberSerializer(DynamicBaseSerializer): member = UserLiteSerializer(read_only=True) workspace = WorkspaceLiteSerializer(read_only=True) @@ -73,7 +73,6 @@ class WorkSpaceMemberSerializer(DynamicBaseSerializer): class WorkspaceMemberMeSerializer(BaseSerializer): - class Meta: model = WorkspaceMember fields = "__all__" @@ -109,7 +108,9 @@ class WorkSpaceMemberInviteSerializer(BaseSerializer): class TeamSerializer(BaseSerializer): - members_detail = UserLiteSerializer(read_only=True, source="members", many=True) + members_detail = UserLiteSerializer( + read_only=True, source="members", many=True + ) members = serializers.ListField( child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()), write_only=True, @@ -146,7 +147,9 @@ class TeamSerializer(BaseSerializer): members = validated_data.pop("members") TeamMember.objects.filter(team=instance).delete() team_members = [ - TeamMember(member=member, team=instance, workspace=instance.workspace) + TeamMember( + member=member, team=instance, workspace=instance.workspace + ) for member in members ] TeamMember.objects.bulk_create(team_members, batch_size=10) @@ -171,4 +174,4 @@ class WorkspaceUserPropertiesSerializer(BaseSerializer): read_only_fields = [ "workspace", "user", - ] \ No newline at end of file + ] diff --git a/apiserver/plane/app/urls/__init__.py b/apiserver/plane/app/urls/__init__.py index d8334ed57..845660807 100644 --- a/apiserver/plane/app/urls/__init__.py +++ b/apiserver/plane/app/urls/__init__.py @@ -45,4 +45,4 @@ urlpatterns = [ *workspace_urls, *api_urls, *webhook_urls, -] \ No newline at end of file +] diff --git a/apiserver/plane/app/urls/authentication.py b/apiserver/plane/app/urls/authentication.py index 39986f791..e91e5706b 100644 --- a/apiserver/plane/app/urls/authentication.py +++ b/apiserver/plane/app/urls/authentication.py @@ -31,8 +31,14 @@ urlpatterns = [ path("sign-in/", SignInEndpoint.as_view(), name="sign-in"), path("sign-out/", SignOutEndpoint.as_view(), name="sign-out"), # magic sign in - path("magic-generate/", MagicGenerateEndpoint.as_view(), name="magic-generate"), - path("magic-sign-in/", MagicSignInEndpoint.as_view(), name="magic-sign-in"), + path( + "magic-generate/", + MagicGenerateEndpoint.as_view(), + name="magic-generate", + ), + path( + "magic-sign-in/", MagicSignInEndpoint.as_view(), name="magic-sign-in" + ), path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), # Password Manipulation path( @@ -52,6 +58,8 @@ urlpatterns = [ ), # API Tokens path("api-tokens/", ApiTokenEndpoint.as_view(), name="api-tokens"), - path("api-tokens//", ApiTokenEndpoint.as_view(), name="api-tokens"), + path( + "api-tokens//", ApiTokenEndpoint.as_view(), name="api-tokens" + ), ## End API Tokens ] diff --git a/apiserver/plane/app/urls/config.py b/apiserver/plane/app/urls/config.py index d12984c87..3ea825eb2 100644 --- a/apiserver/plane/app/urls/config.py +++ b/apiserver/plane/app/urls/config.py @@ -14,4 +14,4 @@ urlpatterns = [ MobileConfigurationEndpoint.as_view(), name="configuration", ), -] \ No newline at end of file +] diff --git a/apiserver/plane/app/urls/cycle.py b/apiserver/plane/app/urls/cycle.py index 5fef437c6..740b0ab43 100644 --- a/apiserver/plane/app/urls/cycle.py +++ b/apiserver/plane/app/urls/cycle.py @@ -89,5 +89,5 @@ urlpatterns = [ "workspaces//projects//cycles//user-properties/", CycleUserPropertiesEndpoint.as_view(), name="cycle-user-filters", - ) + ), ] diff --git a/apiserver/plane/app/urls/module.py b/apiserver/plane/app/urls/module.py index 961fff0db..d81d32d3a 100644 --- a/apiserver/plane/app/urls/module.py +++ b/apiserver/plane/app/urls/module.py @@ -7,7 +7,7 @@ from plane.app.views import ( ModuleLinkViewSet, ModuleFavoriteViewSet, BulkImportModulesEndpoint, - ModuleUserPropertiesEndpoint + ModuleUserPropertiesEndpoint, ) @@ -106,5 +106,5 @@ urlpatterns = [ "workspaces//projects//modules//user-properties/", ModuleUserPropertiesEndpoint.as_view(), name="cycle-user-filters", - ) + ), ] diff --git a/apiserver/plane/app/urls/project.py b/apiserver/plane/app/urls/project.py index 39456a830..f8ecac4c0 100644 --- a/apiserver/plane/app/urls/project.py +++ b/apiserver/plane/app/urls/project.py @@ -175,4 +175,4 @@ urlpatterns = [ ), name="project-deploy-board", ), -] \ No newline at end of file +] diff --git a/apiserver/plane/app/urls/views.py b/apiserver/plane/app/urls/views.py index f78f17869..36372c03a 100644 --- a/apiserver/plane/app/urls/views.py +++ b/apiserver/plane/app/urls/views.py @@ -5,7 +5,7 @@ from plane.app.views import ( IssueViewViewSet, GlobalViewViewSet, GlobalViewIssuesViewSet, - IssueViewFavoriteViewSet, + IssueViewFavoriteViewSet, ) diff --git a/apiserver/plane/app/urls/workspace.py b/apiserver/plane/app/urls/workspace.py index cc78881f9..d2d8f5c06 100644 --- a/apiserver/plane/app/urls/workspace.py +++ b/apiserver/plane/app/urls/workspace.py @@ -206,5 +206,5 @@ urlpatterns = [ "workspaces//user-properties/", WorkspaceUserPropertiesEndpoint.as_view(), name="workspace-user-filters", - ) + ), ] diff --git a/apiserver/plane/app/urls_deprecated.py b/apiserver/plane/app/urls_deprecated.py index c6e6183fa..2a47285aa 100644 --- a/apiserver/plane/app/urls_deprecated.py +++ b/apiserver/plane/app/urls_deprecated.py @@ -192,7 +192,7 @@ from plane.app.views import ( ) -#TODO: Delete this file +# TODO: Delete this file # This url file has been deprecated use apiserver/plane/urls folder to create new urls urlpatterns = [ @@ -204,10 +204,14 @@ urlpatterns = [ path("sign-out/", SignOutEndpoint.as_view(), name="sign-out"), # Magic Sign In/Up path( - "magic-generate/", MagicSignInGenerateEndpoint.as_view(), name="magic-generate" + "magic-generate/", + MagicSignInGenerateEndpoint.as_view(), + name="magic-generate", ), - path("magic-sign-in/", MagicSignInEndpoint.as_view(), name="magic-sign-in"), - path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), + path( + "magic-sign-in/", MagicSignInEndpoint.as_view(), name="magic-sign-in" + ), + path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), # Email verification path("email-verify/", VerifyEmailEndpoint.as_view(), name="email-verify"), path( @@ -272,7 +276,9 @@ urlpatterns = [ # user workspace invitations path( "users/me/invitations/workspaces/", - UserWorkspaceInvitationsEndpoint.as_view({"get": "list", "post": "create"}), + UserWorkspaceInvitationsEndpoint.as_view( + {"get": "list", "post": "create"} + ), name="user-workspace-invitations", ), # user workspace invitation @@ -311,7 +317,9 @@ urlpatterns = [ # user project invitations path( "users/me/invitations/projects/", - UserProjectInvitationsViewset.as_view({"get": "list", "post": "create"}), + UserProjectInvitationsViewset.as_view( + {"get": "list", "post": "create"} + ), name="user-project-invitaions", ), ## Workspaces ## @@ -1238,7 +1246,7 @@ urlpatterns = [ "post": "unarchive", } ), - name="project-page-unarchive" + name="project-page-unarchive", ), path( "workspaces//projects//archived-pages/", @@ -1264,19 +1272,22 @@ urlpatterns = [ { "post": "unlock", } - ) + ), ), path( "workspaces//projects//pages//transactions/", - PageLogEndpoint.as_view(), name="page-transactions" + PageLogEndpoint.as_view(), + name="page-transactions", ), path( "workspaces//projects//pages//transactions//", - PageLogEndpoint.as_view(), name="page-transactions" + PageLogEndpoint.as_view(), + name="page-transactions", ), path( "workspaces//projects//pages//sub-pages/", - SubPagesEndpoint.as_view(), name="sub-page" + SubPagesEndpoint.as_view(), + name="sub-page", ), path( "workspaces//projects//estimates/", @@ -1326,7 +1337,9 @@ urlpatterns = [ ## End Pages # API Tokens path("api-tokens/", ApiTokenEndpoint.as_view(), name="api-tokens"), - path("api-tokens//", ApiTokenEndpoint.as_view(), name="api-tokens"), + path( + "api-tokens//", ApiTokenEndpoint.as_view(), name="api-tokens" + ), ## End API Tokens # Integrations path( diff --git a/apiserver/plane/app/views/__init__.py b/apiserver/plane/app/views/__init__.py index af7c60be0..dccf2bb79 100644 --- a/apiserver/plane/app/views/__init__.py +++ b/apiserver/plane/app/views/__init__.py @@ -140,7 +140,11 @@ from .page import ( from .search import GlobalSearchEndpoint, IssueSearchEndpoint -from .external import GPTIntegrationEndpoint, ReleaseNotesEndpoint, UnsplashEndpoint +from .external import ( + GPTIntegrationEndpoint, + ReleaseNotesEndpoint, + UnsplashEndpoint, +) from .estimate import ( ProjectEstimatePointEndpoint, diff --git a/apiserver/plane/app/views/analytic.py b/apiserver/plane/app/views/analytic.py index c1deb0d8f..04a77f789 100644 --- a/apiserver/plane/app/views/analytic.py +++ b/apiserver/plane/app/views/analytic.py @@ -61,7 +61,9 @@ class AnalyticsEndpoint(BaseAPIView): ) # If segment is present it cannot be same as x-axis - if segment and (segment not in valid_xaxis_segment or x_axis == segment): + if segment and ( + segment not in valid_xaxis_segment or x_axis == segment + ): return Response( { "error": "Both segment and x axis cannot be same and segment should be valid" @@ -110,7 +112,9 @@ class AnalyticsEndpoint(BaseAPIView): if x_axis in ["assignees__id"] or segment in ["assignees__id"]: assignee_details = ( Issue.issue_objects.filter( - workspace__slug=slug, **filters, assignees__avatar__isnull=False + workspace__slug=slug, + **filters, + assignees__avatar__isnull=False, ) .order_by("assignees__id") .distinct("assignees__id") @@ -124,7 +128,9 @@ class AnalyticsEndpoint(BaseAPIView): ) cycle_details = {} - if x_axis in ["issue_cycle__cycle_id"] or segment in ["issue_cycle__cycle_id"]: + if x_axis in ["issue_cycle__cycle_id"] or segment in [ + "issue_cycle__cycle_id" + ]: cycle_details = ( Issue.issue_objects.filter( workspace__slug=slug, @@ -186,7 +192,9 @@ class AnalyticViewViewset(BaseViewSet): def get_queryset(self): return self.filter_queryset( - super().get_queryset().filter(workspace__slug=self.kwargs.get("slug")) + super() + .get_queryset() + .filter(workspace__slug=self.kwargs.get("slug")) ) @@ -196,7 +204,9 @@ class SavedAnalyticEndpoint(BaseAPIView): ] def get(self, request, slug, analytic_id): - analytic_view = AnalyticView.objects.get(pk=analytic_id, workspace__slug=slug) + analytic_view = AnalyticView.objects.get( + pk=analytic_id, workspace__slug=slug + ) filter = analytic_view.query queryset = Issue.issue_objects.filter(**filter) @@ -266,7 +276,9 @@ class ExportAnalyticsEndpoint(BaseAPIView): ) # If segment is present it cannot be same as x-axis - if segment and (segment not in valid_xaxis_segment or x_axis == segment): + if segment and ( + segment not in valid_xaxis_segment or x_axis == segment + ): return Response( { "error": "Both segment and x axis cannot be same and segment should be valid" @@ -293,7 +305,9 @@ class DefaultAnalyticsEndpoint(BaseAPIView): def get(self, request, slug): filters = issue_filters(request.GET, "GET") - base_issues = Issue.issue_objects.filter(workspace__slug=slug, **filters) + base_issues = Issue.issue_objects.filter( + workspace__slug=slug, **filters + ) total_issues = base_issues.count() @@ -306,7 +320,9 @@ class DefaultAnalyticsEndpoint(BaseAPIView): ) open_issues_groups = ["backlog", "unstarted", "started"] - open_issues_queryset = state_groups.filter(state__group__in=open_issues_groups) + open_issues_queryset = state_groups.filter( + state__group__in=open_issues_groups + ) open_issues = open_issues_queryset.count() open_issues_classified = ( @@ -361,10 +377,12 @@ class DefaultAnalyticsEndpoint(BaseAPIView): .order_by("-count") ) - open_estimate_sum = open_issues_queryset.aggregate(sum=Sum("estimate_point"))[ + open_estimate_sum = open_issues_queryset.aggregate( + sum=Sum("estimate_point") + )["sum"] + total_estimate_sum = base_issues.aggregate(sum=Sum("estimate_point"))[ "sum" ] - total_estimate_sum = base_issues.aggregate(sum=Sum("estimate_point"))["sum"] return Response( { diff --git a/apiserver/plane/app/views/api.py b/apiserver/plane/app/views/api.py index ce2d4bd09..86a29c7fa 100644 --- a/apiserver/plane/app/views/api.py +++ b/apiserver/plane/app/views/api.py @@ -71,7 +71,9 @@ class ApiTokenEndpoint(BaseAPIView): user=request.user, pk=pk, ) - serializer = APITokenSerializer(api_token, data=request.data, partial=True) + serializer = APITokenSerializer( + api_token, data=request.data, partial=True + ) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/asset.py b/apiserver/plane/app/views/asset.py index 17d70d936..fb5590610 100644 --- a/apiserver/plane/app/views/asset.py +++ b/apiserver/plane/app/views/asset.py @@ -10,7 +10,11 @@ from plane.app.serializers import FileAssetSerializer class FileAssetEndpoint(BaseAPIView): - parser_classes = (MultiPartParser, FormParser, JSONParser,) + parser_classes = ( + MultiPartParser, + FormParser, + JSONParser, + ) """ A viewset for viewing and editing task instances. @@ -20,10 +24,18 @@ class FileAssetEndpoint(BaseAPIView): asset_key = str(workspace_id) + "/" + asset_key files = FileAsset.objects.filter(asset=asset_key) if files.exists(): - serializer = FileAssetSerializer(files, context={"request": request}, many=True) - return Response({"data": serializer.data, "status": True}, status=status.HTTP_200_OK) + serializer = FileAssetSerializer( + files, context={"request": request}, many=True + ) + return Response( + {"data": serializer.data, "status": True}, + status=status.HTTP_200_OK, + ) else: - return Response({"error": "Asset key does not exist", "status": False}, status=status.HTTP_200_OK) + return Response( + {"error": "Asset key does not exist", "status": False}, + status=status.HTTP_200_OK, + ) def post(self, request, slug): serializer = FileAssetSerializer(data=request.data) @@ -33,7 +45,7 @@ class FileAssetEndpoint(BaseAPIView): serializer.save(workspace_id=workspace.id) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - + def delete(self, request, workspace_id, asset_key): asset_key = str(workspace_id) + "/" + asset_key file_asset = FileAsset.objects.get(asset=asset_key) @@ -43,7 +55,6 @@ class FileAssetEndpoint(BaseAPIView): class FileAssetViewSet(BaseViewSet): - def restore(self, request, workspace_id, asset_key): asset_key = str(workspace_id) + "/" + asset_key file_asset = FileAsset.objects.get(asset=asset_key) @@ -56,12 +67,22 @@ class UserAssetsEndpoint(BaseAPIView): parser_classes = (MultiPartParser, FormParser) def get(self, request, asset_key): - files = FileAsset.objects.filter(asset=asset_key, created_by=request.user) + files = FileAsset.objects.filter( + asset=asset_key, created_by=request.user + ) if files.exists(): - serializer = FileAssetSerializer(files, context={"request": request}) - return Response({"data": serializer.data, "status": True}, status=status.HTTP_200_OK) + serializer = FileAssetSerializer( + files, context={"request": request} + ) + return Response( + {"data": serializer.data, "status": True}, + status=status.HTTP_200_OK, + ) else: - return Response({"error": "Asset key does not exist", "status": False}, status=status.HTTP_200_OK) + return Response( + {"error": "Asset key does not exist", "status": False}, + status=status.HTTP_200_OK, + ) def post(self, request): serializer = FileAssetSerializer(data=request.data) @@ -70,9 +91,10 @@ class UserAssetsEndpoint(BaseAPIView): return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - def delete(self, request, asset_key): - file_asset = FileAsset.objects.get(asset=asset_key, created_by=request.user) + file_asset = FileAsset.objects.get( + asset=asset_key, created_by=request.user + ) file_asset.is_deleted = True file_asset.save() return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apiserver/plane/app/views/auth_extended.py b/apiserver/plane/app/views/auth_extended.py index 049e5aab9..501f47657 100644 --- a/apiserver/plane/app/views/auth_extended.py +++ b/apiserver/plane/app/views/auth_extended.py @@ -128,7 +128,8 @@ class ForgotPasswordEndpoint(BaseAPIView): status=status.HTTP_200_OK, ) return Response( - {"error": "Please check the email"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Please check the email"}, + status=status.HTTP_400_BAD_REQUEST, ) @@ -167,7 +168,9 @@ class ResetPasswordEndpoint(BaseAPIView): } return Response(data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) except DjangoUnicodeDecodeError as indentifier: return Response( @@ -191,7 +194,8 @@ class ChangePasswordEndpoint(BaseAPIView): user.is_password_autoset = False user.save() return Response( - {"message": "Password updated successfully"}, status=status.HTTP_200_OK + {"message": "Password updated successfully"}, + status=status.HTTP_200_OK, ) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -213,7 +217,8 @@ class SetUserPasswordEndpoint(BaseAPIView): # Check password validation if not password and len(str(password)) < 8: return Response( - {"error": "Password is not valid"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Password is not valid"}, + status=status.HTTP_400_BAD_REQUEST, ) # Set the user password @@ -281,7 +286,9 @@ class MagicGenerateEndpoint(BaseAPIView): if data["current_attempt"] > 2: return Response( - {"error": "Max attempts exhausted. Please try again later."}, + { + "error": "Max attempts exhausted. Please try again later." + }, status=status.HTTP_400_BAD_REQUEST, ) @@ -339,7 +346,8 @@ class EmailCheckEndpoint(BaseAPIView): if not email: return Response( - {"error": "Email is required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Email is required"}, + status=status.HTTP_400_BAD_REQUEST, ) # validate the email @@ -347,7 +355,8 @@ class EmailCheckEndpoint(BaseAPIView): validate_email(email) except ValidationError: return Response( - {"error": "Email is not valid"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Email is not valid"}, + status=status.HTTP_400_BAD_REQUEST, ) # Check if the user exists @@ -399,13 +408,18 @@ class EmailCheckEndpoint(BaseAPIView): key, token, current_attempt = generate_magic_token(email=email) if not current_attempt: return Response( - {"error": "Max attempts exhausted. Please try again later."}, + { + "error": "Max attempts exhausted. Please try again later." + }, status=status.HTTP_400_BAD_REQUEST, ) # Trigger the email magic_link.delay(email, "magic_" + str(email), token, current_site) return Response( - {"is_password_autoset": user.is_password_autoset, "is_existing": False}, + { + "is_password_autoset": user.is_password_autoset, + "is_existing": False, + }, status=status.HTTP_200_OK, ) @@ -433,7 +447,9 @@ class EmailCheckEndpoint(BaseAPIView): key, token, current_attempt = generate_magic_token(email=email) if not current_attempt: return Response( - {"error": "Max attempts exhausted. Please try again later."}, + { + "error": "Max attempts exhausted. Please try again later." + }, status=status.HTTP_400_BAD_REQUEST, ) diff --git a/apiserver/plane/app/views/authentication.py b/apiserver/plane/app/views/authentication.py index 256446313..d9e4b4af2 100644 --- a/apiserver/plane/app/views/authentication.py +++ b/apiserver/plane/app/views/authentication.py @@ -73,7 +73,7 @@ class SignUpEndpoint(BaseAPIView): # get configuration values # Get configuration values - ENABLE_SIGNUP, = get_configuration_value( + (ENABLE_SIGNUP,) = get_configuration_value( [ { "key": "ENABLE_SIGNUP", @@ -173,7 +173,7 @@ class SignInEndpoint(BaseAPIView): # Create the user else: - ENABLE_SIGNUP, = get_configuration_value( + (ENABLE_SIGNUP,) = get_configuration_value( [ { "key": "ENABLE_SIGNUP", @@ -364,8 +364,10 @@ class MagicSignInEndpoint(BaseAPIView): user.save() # Check if user has any accepted invites for workspace and add them to workspace - workspace_member_invites = WorkspaceMemberInvite.objects.filter( - email=user.email, accepted=True + workspace_member_invites = ( + WorkspaceMemberInvite.objects.filter( + email=user.email, accepted=True + ) ) WorkspaceMember.objects.bulk_create( @@ -431,7 +433,9 @@ class MagicSignInEndpoint(BaseAPIView): else: return Response( - {"error": "Your login code was incorrect. Please try again."}, + { + "error": "Your login code was incorrect. Please try again." + }, status=status.HTTP_400_BAD_REQUEST, ) diff --git a/apiserver/plane/app/views/base.py b/apiserver/plane/app/views/base.py index eb3ade229..e07cb811c 100644 --- a/apiserver/plane/app/views/base.py +++ b/apiserver/plane/app/views/base.py @@ -46,7 +46,9 @@ class WebhookMixin: bulk = False def finalize_response(self, request, response, *args, **kwargs): - response = super().finalize_response(request, response, *args, **kwargs) + response = super().finalize_response( + request, response, *args, **kwargs + ) # Check for the case should webhook be sent if ( @@ -88,7 +90,9 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): return self.model.objects.all() except Exception as e: capture_exception(e) - raise APIException("Please check the view", status.HTTP_400_BAD_REQUEST) + raise APIException( + "Please check the view", status.HTTP_400_BAD_REQUEST + ) def handle_exception(self, exc): """ @@ -126,8 +130,10 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): ) capture_exception(e) - return Response({"error": "Something went wrong please try again later"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) def dispatch(self, request, *args, **kwargs): try: @@ -161,19 +167,22 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): @property def fields(self): fields = [ - field for field in self.request.GET.get("fields", "").split(",") if field + field + for field in self.request.GET.get("fields", "").split(",") + if field ] return fields if fields else None @property def expand(self): expand = [ - expand for expand in self.request.GET.get("expand", "").split(",") if expand + expand + for expand in self.request.GET.get("expand", "").split(",") + if expand ] return expand if expand else None - class BaseAPIView(TimezoneMixin, APIView, BasePaginator): permission_classes = [ IsAuthenticated, @@ -219,15 +228,20 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): {"error": f"The required object does not exist."}, status=status.HTTP_404_NOT_FOUND, ) - + if isinstance(e, KeyError): - return Response({"error": f"The required key does not exist."}, status=status.HTTP_400_BAD_REQUEST) + return Response( + {"error": f"The required key does not exist."}, + status=status.HTTP_400_BAD_REQUEST, + ) if settings.DEBUG: print(e) capture_exception(e) - return Response({"error": "Something went wrong please try again later"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) def dispatch(self, request, *args, **kwargs): try: @@ -256,13 +270,17 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): @property def fields(self): fields = [ - field for field in self.request.GET.get("fields", "").split(",") if field + field + for field in self.request.GET.get("fields", "").split(",") + if field ] return fields if fields else None @property def expand(self): expand = [ - expand for expand in self.request.GET.get("expand", "").split(",") if expand + expand + for expand in self.request.GET.get("expand", "").split(",") + if expand ] return expand if expand else None diff --git a/apiserver/plane/app/views/config.py b/apiserver/plane/app/views/config.py index e95eb407b..29b4bbf8b 100644 --- a/apiserver/plane/app/views/config.py +++ b/apiserver/plane/app/views/config.py @@ -90,10 +90,14 @@ class ConfigurationEndpoint(BaseAPIView): data = {} # Authentication data["google_client_id"] = ( - GOOGLE_CLIENT_ID if GOOGLE_CLIENT_ID and GOOGLE_CLIENT_ID != '""' else None + GOOGLE_CLIENT_ID + if GOOGLE_CLIENT_ID and GOOGLE_CLIENT_ID != '""' + else None ) data["github_client_id"] = ( - GITHUB_CLIENT_ID if GITHUB_CLIENT_ID and GITHUB_CLIENT_ID != '""' else None + GITHUB_CLIENT_ID + if GITHUB_CLIENT_ID and GITHUB_CLIENT_ID != '""' + else None ) data["github_app_name"] = GITHUB_APP_NAME data["magic_login"] = ( @@ -115,11 +119,13 @@ class ConfigurationEndpoint(BaseAPIView): data["has_openai_configured"] = bool(OPENAI_API_KEY) # File size settings - data["file_size_limit"] = float(os.environ.get("FILE_SIZE_LIMIT", 5242880)) + data["file_size_limit"] = float( + os.environ.get("FILE_SIZE_LIMIT", 5242880) + ) # is smtp configured - data["is_smtp_configured"] = ( - bool(EMAIL_HOST_USER) and bool(EMAIL_HOST_PASSWORD) + data["is_smtp_configured"] = bool(EMAIL_HOST_USER) and bool( + EMAIL_HOST_PASSWORD ) return Response(data, status=status.HTTP_200_OK) @@ -194,7 +200,9 @@ class MobileConfigurationEndpoint(BaseAPIView): data = {} # Authentication data["google_client_id"] = ( - GOOGLE_CLIENT_ID if GOOGLE_CLIENT_ID and GOOGLE_CLIENT_ID != '""' else None + GOOGLE_CLIENT_ID + if GOOGLE_CLIENT_ID and GOOGLE_CLIENT_ID != '""' + else None ) data["google_server_client_id"] = ( GOOGLE_SERVER_CLIENT_ID @@ -202,7 +210,9 @@ class MobileConfigurationEndpoint(BaseAPIView): else None ) data["google_ios_client_id"] = ( - (GOOGLE_IOS_CLIENT_ID)[::-1] if GOOGLE_IOS_CLIENT_ID is not None else None + (GOOGLE_IOS_CLIENT_ID)[::-1] + if GOOGLE_IOS_CLIENT_ID is not None + else None ) # Posthog data["posthog_api_key"] = POSTHOG_API_KEY @@ -225,7 +235,9 @@ class MobileConfigurationEndpoint(BaseAPIView): data["has_openai_configured"] = bool(OPENAI_API_KEY) # File size settings - data["file_size_limit"] = float(os.environ.get("FILE_SIZE_LIMIT", 5242880)) + data["file_size_limit"] = float( + os.environ.get("FILE_SIZE_LIMIT", 5242880) + ) # is smtp configured data["is_smtp_configured"] = not ( diff --git a/apiserver/plane/app/views/cycle.py b/apiserver/plane/app/views/cycle.py index 15a6c24b0..2d459d15b 100644 --- a/apiserver/plane/app/views/cycle.py +++ b/apiserver/plane/app/views/cycle.py @@ -36,7 +36,10 @@ from plane.app.serializers import ( CycleWriteSerializer, CycleUserPropertiesSerializer, ) -from plane.app.permissions import ProjectEntityPermission, ProjectLitePermission +from plane.app.permissions import ( + ProjectEntityPermission, + ProjectLitePermission, +) from plane.db.models import ( User, Cycle, @@ -64,7 +67,8 @@ class CycleViewSet(WebhookMixin, BaseViewSet): def perform_create(self, serializer): serializer.save( - project_id=self.kwargs.get("project_id"), owned_by=self.request.user + project_id=self.kwargs.get("project_id"), + owned_by=self.request.user, ) def get_queryset(self): @@ -143,7 +147,9 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ), ) ) - .annotate(total_estimates=Sum("issue_cycle__issue__estimate_point")) + .annotate( + total_estimates=Sum("issue_cycle__issue__estimate_point") + ) .annotate( completed_estimates=Sum( "issue_cycle__issue__estimate_point", @@ -171,7 +177,9 @@ class CycleViewSet(WebhookMixin, BaseViewSet): & Q(end_date__gte=timezone.now()), then=Value("CURRENT"), ), - When(start_date__gt=timezone.now(), then=Value("UPCOMING")), + When( + start_date__gt=timezone.now(), then=Value("UPCOMING") + ), When(end_date__lt=timezone.now(), then=Value("COMPLETED")), When( Q(start_date__isnull=True) & Q(end_date__isnull=True), @@ -184,13 +192,17 @@ class CycleViewSet(WebhookMixin, BaseViewSet): .prefetch_related( Prefetch( "issue_cycle__issue__assignees", - queryset=User.objects.only("avatar", "first_name", "id").distinct(), + queryset=User.objects.only( + "avatar", "first_name", "id" + ).distinct(), ) ) .prefetch_related( Prefetch( "issue_cycle__issue__labels", - queryset=Label.objects.only("name", "color", "id").distinct(), + queryset=Label.objects.only( + "name", "color", "id" + ).distinct(), ) ) .order_by("-is_favorite", "name") @@ -200,7 +212,11 @@ class CycleViewSet(WebhookMixin, BaseViewSet): def list(self, request, slug, project_id): queryset = self.get_queryset() cycle_view = request.GET.get("cycle_view", "all") - fields = [field for field in request.GET.get("fields", "").split(",") if field] + fields = [ + field + for field in request.GET.get("fields", "").split(",") + if field + ] queryset = queryset.order_by("-is_favorite", "-created_at") @@ -297,7 +313,9 @@ class CycleViewSet(WebhookMixin, BaseViewSet): "completion_chart": {}, } if data[0]["start_date"] and data[0]["end_date"]: - data[0]["distribution"]["completion_chart"] = burndown_plot( + data[0]["distribution"][ + "completion_chart" + ] = burndown_plot( queryset=queryset.first(), slug=slug, project_id=project_id, @@ -323,10 +341,18 @@ class CycleViewSet(WebhookMixin, BaseViewSet): project_id=project_id, owned_by=request.user, ) - cycle = self.get_queryset().filter(pk=serializer.data["id"]).first() + cycle = ( + self.get_queryset() + .filter(pk=serializer.data["id"]) + .first() + ) serializer = CycleSerializer(cycle) - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + serializer.data, status=status.HTTP_201_CREATED + ) + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) else: return Response( { @@ -336,15 +362,22 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) def partial_update(self, request, slug, project_id, pk): - cycle = Cycle.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) + cycle = Cycle.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk + ) request_data = request.data - if cycle.end_date is not None and cycle.end_date < timezone.now().date(): + if ( + cycle.end_date is not None + and cycle.end_date < timezone.now().date() + ): if "sort_order" in request_data: # Can only change sort order request_data = { - "sort_order": request_data.get("sort_order", cycle.sort_order) + "sort_order": request_data.get( + "sort_order", cycle.sort_order + ) } else: return Response( @@ -354,7 +387,9 @@ class CycleViewSet(WebhookMixin, BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) - serializer = CycleWriteSerializer(cycle, data=request.data, partial=True) + serializer = CycleWriteSerializer( + cycle, data=request.data, partial=True + ) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) @@ -375,7 +410,13 @@ class CycleViewSet(WebhookMixin, BaseViewSet): .annotate(assignee_id=F("assignees__id")) .annotate(avatar=F("assignees__avatar")) .annotate(display_name=F("assignees__display_name")) - .values("first_name", "last_name", "assignee_id", "avatar", "display_name") + .values( + "first_name", + "last_name", + "assignee_id", + "avatar", + "display_name", + ) .annotate( total_issues=Count( "assignee_id", @@ -454,7 +495,10 @@ class CycleViewSet(WebhookMixin, BaseViewSet): if queryset.start_date and queryset.end_date: data["distribution"]["completion_chart"] = burndown_plot( - queryset=queryset, slug=slug, project_id=project_id, cycle_id=pk + queryset=queryset, + slug=slug, + project_id=project_id, + cycle_id=pk, ) return Response( @@ -464,11 +508,13 @@ class CycleViewSet(WebhookMixin, BaseViewSet): def destroy(self, request, slug, project_id, pk): cycle_issues = list( - CycleIssue.objects.filter(cycle_id=self.kwargs.get("pk")).values_list( - "issue", flat=True - ) + CycleIssue.objects.filter( + cycle_id=self.kwargs.get("pk") + ).values_list("issue", flat=True) + ) + cycle = Cycle.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk ) - cycle = Cycle.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) issue_activity.delay( type="cycle.activity.deleted", @@ -511,7 +557,9 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet): super() .get_queryset() .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("issue_id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("issue_id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -530,13 +578,19 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet): @method_decorator(gzip_page) def list(self, request, slug, project_id, cycle_id): - fields = [field for field in request.GET.get("fields", "").split(",") if field] + fields = [ + field + for field in request.GET.get("fields", "").split(",") + if field + ] order_by = request.GET.get("order_by", "created_at") filters = issue_filters(request.query_params, "GET") issues = ( Issue.issue_objects.filter(issue_cycle__cycle_id=cycle_id) .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -560,7 +614,9 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -583,14 +639,18 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet): if not len(issues): return Response( - {"error": "Issues are required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Issues are required"}, + status=status.HTTP_400_BAD_REQUEST, ) cycle = Cycle.objects.get( workspace__slug=slug, project_id=project_id, pk=cycle_id ) - if cycle.end_date is not None and cycle.end_date < timezone.now().date(): + if ( + cycle.end_date is not None + and cycle.end_date < timezone.now().date() + ): return Response( { "error": "The Cycle has already been completed so no new issues can be added" @@ -667,7 +727,9 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet): issues = self.get_queryset().values_list("issue_id", flat=True) return Response( - IssueSerializer(Issue.objects.filter(pk__in=issues), many=True).data, + IssueSerializer( + Issue.objects.filter(pk__in=issues), many=True + ).data, status=status.HTTP_200_OK, ) @@ -824,7 +886,9 @@ class CycleUserPropertiesEndpoint(BaseAPIView): workspace__slug=slug, ) - cycle_properties.filters = request.data.get("filters", cycle_properties.filters) + cycle_properties.filters = request.data.get( + "filters", cycle_properties.filters + ) cycle_properties.display_filters = request.data.get( "display_filters", cycle_properties.display_filters ) diff --git a/apiserver/plane/app/views/estimate.py b/apiserver/plane/app/views/estimate.py index 8f14b230b..3402bb068 100644 --- a/apiserver/plane/app/views/estimate.py +++ b/apiserver/plane/app/views/estimate.py @@ -19,16 +19,16 @@ class ProjectEstimatePointEndpoint(BaseAPIView): ] def get(self, request, slug, project_id): - project = Project.objects.get(workspace__slug=slug, pk=project_id) - if project.estimate_id is not None: - estimate_points = EstimatePoint.objects.filter( - estimate_id=project.estimate_id, - project_id=project_id, - workspace__slug=slug, - ) - serializer = EstimatePointSerializer(estimate_points, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - return Response([], status=status.HTTP_200_OK) + project = Project.objects.get(workspace__slug=slug, pk=project_id) + if project.estimate_id is not None: + estimate_points = EstimatePoint.objects.filter( + estimate_id=project.estimate_id, + project_id=project_id, + workspace__slug=slug, + ) + serializer = EstimatePointSerializer(estimate_points, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) + return Response([], status=status.HTTP_200_OK) class BulkEstimatePointEndpoint(BaseViewSet): @@ -39,9 +39,13 @@ class BulkEstimatePointEndpoint(BaseViewSet): serializer_class = EstimateSerializer def list(self, request, slug, project_id): - estimates = Estimate.objects.filter( - workspace__slug=slug, project_id=project_id - ).prefetch_related("points").select_related("workspace", "project") + estimates = ( + Estimate.objects.filter( + workspace__slug=slug, project_id=project_id + ) + .prefetch_related("points") + .select_related("workspace", "project") + ) serializer = EstimateReadSerializer(estimates, many=True) return Response(serializer.data, status=status.HTTP_200_OK) @@ -53,14 +57,18 @@ class BulkEstimatePointEndpoint(BaseViewSet): ) estimate_points = request.data.get("estimate_points", []) - - serializer = EstimatePointSerializer(data=request.data.get("estimate_points"), many=True) + + serializer = EstimatePointSerializer( + data=request.data.get("estimate_points"), many=True + ) if not serializer.is_valid(): return Response( serializer.errors, status=status.HTTP_400_BAD_REQUEST ) - estimate_serializer = EstimateSerializer(data=request.data.get("estimate")) + estimate_serializer = EstimateSerializer( + data=request.data.get("estimate") + ) if not estimate_serializer.is_valid(): return Response( estimate_serializer.errors, status=status.HTTP_400_BAD_REQUEST @@ -135,7 +143,8 @@ class BulkEstimatePointEndpoint(BaseViewSet): estimate_points = EstimatePoint.objects.filter( pk__in=[ - estimate_point.get("id") for estimate_point in estimate_points_data + estimate_point.get("id") + for estimate_point in estimate_points_data ], workspace__slug=slug, project_id=project_id, @@ -157,10 +166,14 @@ class BulkEstimatePointEndpoint(BaseViewSet): updated_estimate_points.append(estimate_point) EstimatePoint.objects.bulk_update( - updated_estimate_points, ["value"], batch_size=10, + updated_estimate_points, + ["value"], + batch_size=10, ) - estimate_point_serializer = EstimatePointSerializer(estimate_points, many=True) + estimate_point_serializer = EstimatePointSerializer( + estimate_points, many=True + ) return Response( { "estimate": estimate_serializer.data, diff --git a/apiserver/plane/app/views/exporter.py b/apiserver/plane/app/views/exporter.py index b709a599d..179de81f9 100644 --- a/apiserver/plane/app/views/exporter.py +++ b/apiserver/plane/app/views/exporter.py @@ -21,11 +21,11 @@ class ExportIssuesEndpoint(BaseAPIView): def post(self, request, slug): # Get the workspace workspace = Workspace.objects.get(slug=slug) - + provider = request.data.get("provider", False) multiple = request.data.get("multiple", False) project_ids = request.data.get("project", []) - + if provider in ["csv", "xlsx", "json"]: if not project_ids: project_ids = Project.objects.filter( @@ -63,9 +63,11 @@ class ExportIssuesEndpoint(BaseAPIView): def get(self, request, slug): exporter_history = ExporterHistory.objects.filter( workspace__slug=slug - ).select_related("workspace","initiated_by") + ).select_related("workspace", "initiated_by") - if request.GET.get("per_page", False) and request.GET.get("cursor", False): + if request.GET.get("per_page", False) and request.GET.get( + "cursor", False + ): return self.paginate( request=request, queryset=exporter_history, diff --git a/apiserver/plane/app/views/external.py b/apiserver/plane/app/views/external.py index 97d509c1e..618c65e3c 100644 --- a/apiserver/plane/app/views/external.py +++ b/apiserver/plane/app/views/external.py @@ -14,7 +14,10 @@ from django.conf import settings from .base import BaseAPIView from plane.app.permissions import ProjectEntityPermission from plane.db.models import Workspace, Project -from plane.app.serializers import ProjectLiteSerializer, WorkspaceLiteSerializer +from plane.app.serializers import ( + ProjectLiteSerializer, + WorkspaceLiteSerializer, +) from plane.utils.integrations.github import get_release_notes from plane.license.utils.instance_value import get_configuration_value @@ -51,7 +54,8 @@ class GPTIntegrationEndpoint(BaseAPIView): if not task: return Response( - {"error": "Task is required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Task is required"}, + status=status.HTTP_400_BAD_REQUEST, ) final_text = task + "\n" + prompt @@ -89,7 +93,7 @@ class ReleaseNotesEndpoint(BaseAPIView): class UnsplashEndpoint(BaseAPIView): def get(self, request): - UNSPLASH_ACCESS_KEY, = get_configuration_value( + (UNSPLASH_ACCESS_KEY,) = get_configuration_value( [ { "key": "UNSPLASH_ACCESS_KEY", diff --git a/apiserver/plane/app/views/importer.py b/apiserver/plane/app/views/importer.py index 00d698ac5..a15ed36b7 100644 --- a/apiserver/plane/app/views/importer.py +++ b/apiserver/plane/app/views/importer.py @@ -35,7 +35,10 @@ from plane.app.serializers import ( ModuleSerializer, ) from plane.utils.integrations.github import get_github_repo_details -from plane.utils.importers.jira import jira_project_issue_summary, is_allowed_hostname +from plane.utils.importers.jira import ( + jira_project_issue_summary, + is_allowed_hostname, +) from plane.bgtasks.importer_task import service_importer from plane.utils.html_processor import strip_tags from plane.app.permissions import WorkSpaceAdminPermission @@ -93,7 +96,8 @@ class ServiceIssueImportSummaryEndpoint(BaseAPIView): for key, error_message in params.items(): if not request.GET.get(key, False): return Response( - {"error": error_message}, status=status.HTTP_400_BAD_REQUEST + {"error": error_message}, + status=status.HTTP_400_BAD_REQUEST, ) project_key = request.GET.get("project_key", "") @@ -236,7 +240,9 @@ class ImportServiceEndpoint(BaseAPIView): return Response(serializer.data) def delete(self, request, slug, service, pk): - importer = Importer.objects.get(pk=pk, service=service, workspace__slug=slug) + importer = Importer.objects.get( + pk=pk, service=service, workspace__slug=slug + ) if importer.imported_data is not None: # Delete all imported Issues @@ -254,8 +260,12 @@ class ImportServiceEndpoint(BaseAPIView): return Response(status=status.HTTP_204_NO_CONTENT) def patch(self, request, slug, service, pk): - importer = Importer.objects.get(pk=pk, service=service, workspace__slug=slug) - serializer = ImporterSerializer(importer, data=request.data, partial=True) + importer = Importer.objects.get( + pk=pk, service=service, workspace__slug=slug + ) + serializer = ImporterSerializer( + importer, data=request.data, partial=True + ) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) @@ -291,9 +301,9 @@ class BulkImportIssuesEndpoint(BaseAPIView): ).first() # Get the maximum sequence_id - last_id = IssueSequence.objects.filter(project_id=project_id).aggregate( - largest=Max("sequence") - )["largest"] + last_id = IssueSequence.objects.filter( + project_id=project_id + ).aggregate(largest=Max("sequence"))["largest"] last_id = 1 if last_id is None else last_id + 1 @@ -326,7 +336,9 @@ class BulkImportIssuesEndpoint(BaseAPIView): if issue_data.get("state", False) else default_state.id, name=issue_data.get("name", "Issue Created through Bulk"), - description_html=issue_data.get("description_html", "

"), + description_html=issue_data.get( + "description_html", "

" + ), description_stripped=( None if ( @@ -438,15 +450,21 @@ class BulkImportIssuesEndpoint(BaseAPIView): for comment in comments_list ] - _ = IssueComment.objects.bulk_create(bulk_issue_comments, batch_size=100) + _ = IssueComment.objects.bulk_create( + bulk_issue_comments, batch_size=100 + ) # Attach Links _ = IssueLink.objects.bulk_create( [ IssueLink( issue=issue, - url=issue_data.get("link", {}).get("url", "https://github.com"), - title=issue_data.get("link", {}).get("title", "Original Issue"), + url=issue_data.get("link", {}).get( + "url", "https://github.com" + ), + title=issue_data.get("link", {}).get( + "title", "Original Issue" + ), project_id=project_id, workspace_id=project.workspace_id, created_by=request.user, @@ -483,14 +501,18 @@ class BulkImportModulesEndpoint(BaseAPIView): ignore_conflicts=True, ) - modules = Module.objects.filter(id__in=[module.id for module in modules]) + modules = Module.objects.filter( + id__in=[module.id for module in modules] + ) if len(modules) == len(modules_data): _ = ModuleLink.objects.bulk_create( [ ModuleLink( module=module, - url=module_data.get("link", {}).get("url", "https://plane.so"), + url=module_data.get("link", {}).get( + "url", "https://plane.so" + ), title=module_data.get("link", {}).get( "title", "Original Issue" ), @@ -529,6 +551,8 @@ class BulkImportModulesEndpoint(BaseAPIView): else: return Response( - {"message": "Modules created but issues could not be imported"}, + { + "message": "Modules created but issues could not be imported" + }, status=status.HTTP_200_OK, ) diff --git a/apiserver/plane/app/views/inbox.py b/apiserver/plane/app/views/inbox.py index 32f38d97c..ff88bfdab 100644 --- a/apiserver/plane/app/views/inbox.py +++ b/apiserver/plane/app/views/inbox.py @@ -62,7 +62,9 @@ class InboxViewSet(BaseViewSet): serializer.save(project_id=self.kwargs.get("project_id")) def destroy(self, request, slug, project_id, pk): - inbox = Inbox.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) + inbox = Inbox.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk + ) # Handle default inbox delete if inbox.is_default: return Response( @@ -90,7 +92,8 @@ class InboxIssueViewSet(BaseViewSet): super() .get_queryset() .filter( - Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True), + Q(snoozed_till__gte=timezone.now()) + | Q(snoozed_till__isnull=True), workspace__slug=self.kwargs.get("slug"), project_id=self.kwargs.get("project_id"), inbox_id=self.kwargs.get("inbox_id"), @@ -111,7 +114,9 @@ class InboxIssueViewSet(BaseViewSet): .prefetch_related("assignees", "labels") .order_by("issue_inbox__snoozed_till", "issue_inbox__status") .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -123,7 +128,9 @@ class InboxIssueViewSet(BaseViewSet): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -146,7 +153,8 @@ class InboxIssueViewSet(BaseViewSet): def create(self, request, slug, project_id, inbox_id): if not request.data.get("issue", {}).get("name", False): return Response( - {"error": "Name is required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Name is required"}, + status=status.HTTP_400_BAD_REQUEST, ) # Check for valid priority @@ -158,7 +166,8 @@ class InboxIssueViewSet(BaseViewSet): "none", ]: return Response( - {"error": "Invalid priority"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Invalid priority"}, + status=status.HTTP_400_BAD_REQUEST, ) # Create or get state @@ -205,7 +214,10 @@ class InboxIssueViewSet(BaseViewSet): def partial_update(self, request, slug, project_id, inbox_id, issue_id): inbox_issue = InboxIssue.objects.get( - issue_id=issue_id, workspace__slug=slug, project_id=project_id, inbox_id=inbox_id + issue_id=issue_id, + workspace__slug=slug, + project_id=project_id, + inbox_id=inbox_id, ) # Get the project member project_member = ProjectMember.objects.get( @@ -228,7 +240,9 @@ class InboxIssueViewSet(BaseViewSet): if bool(issue_data): issue = Issue.objects.get( - pk=inbox_issue.issue_id, workspace__slug=slug, project_id=project_id + pk=inbox_issue.issue_id, + workspace__slug=slug, + project_id=project_id, ) # Only allow guests and viewers to edit name and description if project_member.role <= 10: @@ -238,7 +252,9 @@ class InboxIssueViewSet(BaseViewSet): "description_html": issue_data.get( "description_html", issue.description_html ), - "description": issue_data.get("description", issue.description), + "description": issue_data.get( + "description", issue.description + ), } issue_serializer = IssueCreateSerializer( @@ -284,7 +300,9 @@ class InboxIssueViewSet(BaseViewSet): project_id=project_id, ) state = State.objects.filter( - group="cancelled", workspace__slug=slug, project_id=project_id + group="cancelled", + workspace__slug=slug, + project_id=project_id, ).first() if state is not None: issue.state = state @@ -302,17 +320,22 @@ class InboxIssueViewSet(BaseViewSet): if issue.state.name == "Triage": # Move to default state state = State.objects.filter( - workspace__slug=slug, project_id=project_id, default=True + workspace__slug=slug, + project_id=project_id, + default=True, ).first() if state is not None: issue.state = state issue.save() return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) else: return Response( - InboxIssueSerializer(inbox_issue).data, status=status.HTTP_200_OK + InboxIssueSerializer(inbox_issue).data, + status=status.HTTP_200_OK, ) def retrieve(self, request, slug, project_id, inbox_id, issue_id): @@ -324,7 +347,10 @@ class InboxIssueViewSet(BaseViewSet): def destroy(self, request, slug, project_id, inbox_id, issue_id): inbox_issue = InboxIssue.objects.get( - issue_id=issue_id, workspace__slug=slug, project_id=project_id, inbox_id=inbox_id + issue_id=issue_id, + workspace__slug=slug, + project_id=project_id, + inbox_id=inbox_id, ) # Get the project member project_member = ProjectMember.objects.get( @@ -351,4 +377,3 @@ class InboxIssueViewSet(BaseViewSet): inbox_issue.delete() return Response(status=status.HTTP_204_NO_CONTENT) - diff --git a/apiserver/plane/app/views/integration/base.py b/apiserver/plane/app/views/integration/base.py index b82957dfb..d757fe471 100644 --- a/apiserver/plane/app/views/integration/base.py +++ b/apiserver/plane/app/views/integration/base.py @@ -1,6 +1,7 @@ # Python improts import uuid import requests + # Django imports from django.contrib.auth.hashers import make_password @@ -19,7 +20,10 @@ from plane.db.models import ( WorkspaceMember, APIToken, ) -from plane.app.serializers import IntegrationSerializer, WorkspaceIntegrationSerializer +from plane.app.serializers import ( + IntegrationSerializer, + WorkspaceIntegrationSerializer, +) from plane.utils.integrations.github import ( get_github_metadata, delete_github_installation, @@ -27,6 +31,7 @@ from plane.utils.integrations.github import ( from plane.app.permissions import WorkSpaceAdminPermission from plane.utils.integrations.slack import slack_oauth + class IntegrationViewSet(BaseViewSet): serializer_class = IntegrationSerializer model = Integration @@ -101,7 +106,10 @@ class WorkspaceIntegrationViewSet(BaseViewSet): code = request.data.get("code", False) if not code: - return Response({"error": "Code is required"}, status=status.HTTP_400_BAD_REQUEST) + return Response( + {"error": "Code is required"}, + status=status.HTTP_400_BAD_REQUEST, + ) slack_response = slack_oauth(code=code) @@ -110,7 +118,9 @@ class WorkspaceIntegrationViewSet(BaseViewSet): team_id = metadata.get("team", {}).get("id", False) if not metadata or not access_token or not team_id: return Response( - {"error": "Slack could not be installed. Please try again later"}, + { + "error": "Slack could not be installed. Please try again later" + }, status=status.HTTP_400_BAD_REQUEST, ) config = {"team_id": team_id, "access_token": access_token} diff --git a/apiserver/plane/app/views/integration/github.py b/apiserver/plane/app/views/integration/github.py index 29b7a9b2f..2d37c64b0 100644 --- a/apiserver/plane/app/views/integration/github.py +++ b/apiserver/plane/app/views/integration/github.py @@ -21,7 +21,10 @@ from plane.app.serializers import ( GithubCommentSyncSerializer, ) from plane.utils.integrations.github import get_github_repos -from plane.app.permissions import ProjectBasePermission, ProjectEntityPermission +from plane.app.permissions import ( + ProjectBasePermission, + ProjectEntityPermission, +) class GithubRepositoriesEndpoint(BaseAPIView): @@ -185,11 +188,10 @@ class BulkCreateGithubIssueSyncEndpoint(BaseAPIView): class GithubCommentSyncViewSet(BaseViewSet): - permission_classes = [ ProjectEntityPermission, ] - + serializer_class = GithubCommentSyncSerializer model = GithubCommentSync diff --git a/apiserver/plane/app/views/integration/slack.py b/apiserver/plane/app/views/integration/slack.py index 3f18a2ab2..410e6b332 100644 --- a/apiserver/plane/app/views/integration/slack.py +++ b/apiserver/plane/app/views/integration/slack.py @@ -8,9 +8,16 @@ from sentry_sdk import capture_exception # Module imports from plane.app.views import BaseViewSet, BaseAPIView -from plane.db.models import SlackProjectSync, WorkspaceIntegration, ProjectMember +from plane.db.models import ( + SlackProjectSync, + WorkspaceIntegration, + ProjectMember, +) from plane.app.serializers import SlackProjectSyncSerializer -from plane.app.permissions import ProjectBasePermission, ProjectEntityPermission +from plane.app.permissions import ( + ProjectBasePermission, + ProjectEntityPermission, +) from plane.utils.integrations.slack import slack_oauth @@ -38,7 +45,8 @@ class SlackProjectSyncViewSet(BaseViewSet): if not code: return Response( - {"error": "Code is required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Code is required"}, + status=status.HTTP_400_BAD_REQUEST, ) slack_response = slack_oauth(code=code) @@ -54,7 +62,9 @@ class SlackProjectSyncViewSet(BaseViewSet): access_token=slack_response.get("access_token"), scopes=slack_response.get("scope"), bot_user_id=slack_response.get("bot_user_id"), - webhook_url=slack_response.get("incoming_webhook", {}).get("url"), + webhook_url=slack_response.get("incoming_webhook", {}).get( + "url" + ), data=slack_response, team_id=slack_response.get("team", {}).get("id"), team_name=slack_response.get("team", {}).get("name"), @@ -62,7 +72,9 @@ class SlackProjectSyncViewSet(BaseViewSet): project_id=project_id, ) _ = ProjectMember.objects.get_or_create( - member=workspace_integration.actor, role=20, project_id=project_id + member=workspace_integration.actor, + role=20, + project_id=project_id, ) serializer = SlackProjectSyncSerializer(slack_project_sync) return Response(serializer.data, status=status.HTTP_200_OK) @@ -74,6 +86,8 @@ class SlackProjectSyncViewSet(BaseViewSet): ) capture_exception(e) return Response( - {"error": "Slack could not be installed. Please try again later"}, + { + "error": "Slack could not be installed. Please try again later" + }, status=status.HTTP_400_BAD_REQUEST, ) diff --git a/apiserver/plane/app/views/issue.py b/apiserver/plane/app/views/issue.py index 4a91c9fe1..837aa7e7b 100644 --- a/apiserver/plane/app/views/issue.py +++ b/apiserver/plane/app/views/issue.py @@ -110,8 +110,9 @@ class IssueViewSet(WebhookMixin, BaseViewSet): def get_queryset(self): return ( - Issue.issue_objects - .filter(project_id=self.kwargs.get("project_id")) + Issue.issue_objects.filter( + project_id=self.kwargs.get("project_id") + ) .filter(workspace__slug=self.kwargs.get("slug")) .select_related("project") .select_related("workspace") @@ -134,12 +135,17 @@ class IssueViewSet(WebhookMixin, BaseViewSet): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") - ).annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + ) + .annotate( + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -152,7 +158,13 @@ class IssueViewSet(WebhookMixin, BaseViewSet): # Custom ordering for priority and state priority_order = ["urgent", "high", "medium", "low", "none"] - state_order = ["backlog", "unstarted", "started", "completed", "cancelled"] + state_order = [ + "backlog", + "unstarted", + "started", + "completed", + "cancelled", + ] order_by_param = request.GET.get("order_by", "-created_at") @@ -161,7 +173,9 @@ class IssueViewSet(WebhookMixin, BaseViewSet): # Priority Ordering if order_by_param == "priority" or order_by_param == "-priority": priority_order = ( - priority_order if order_by_param == "priority" else priority_order[::-1] + priority_order + if order_by_param == "priority" + else priority_order[::-1] ) issue_queryset = issue_queryset.annotate( priority_order=Case( @@ -209,7 +223,9 @@ class IssueViewSet(WebhookMixin, BaseViewSet): else order_by_param ) ).order_by( - "-max_values" if order_by_param.startswith("-") else "max_values" + "-max_values" + if order_by_param.startswith("-") + else "max_values" ) else: issue_queryset = issue_queryset.order_by(order_by_param) @@ -237,14 +253,18 @@ class IssueViewSet(WebhookMixin, BaseViewSet): # Track the issue issue_activity.delay( type="issue.activity.created", - requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), + requested_data=json.dumps( + self.request.data, cls=DjangoJSONEncoder + ), actor_id=str(request.user.id), issue_id=str(serializer.data.get("id", None)), project_id=str(project_id), current_instance=None, epoch=int(timezone.now().timestamp()), ) - issue = self.get_queryset().filter(pk=serializer.data["id"]).first() + issue = ( + self.get_queryset().filter(pk=serializer.data["id"]).first() + ) serializer = IssueSerializer(issue) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -252,17 +272,23 @@ class IssueViewSet(WebhookMixin, BaseViewSet): def retrieve(self, request, slug, project_id, pk=None): issue = self.get_queryset().filter(pk=pk).first() return Response( - IssueSerializer(issue, fields=self.fields, expand=self.expand).data, + IssueSerializer( + issue, fields=self.fields, expand=self.expand + ).data, status=status.HTTP_200_OK, ) def partial_update(self, request, slug, project_id, pk=None): - issue = Issue.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) + issue = Issue.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk + ) current_instance = json.dumps( IssueSerializer(issue).data, cls=DjangoJSONEncoder ) requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder) - serializer = IssueCreateSerializer(issue, data=request.data, partial=True) + serializer = IssueCreateSerializer( + issue, data=request.data, partial=True + ) if serializer.is_valid(): serializer.save() issue_activity.delay( @@ -275,11 +301,15 @@ class IssueViewSet(WebhookMixin, BaseViewSet): epoch=int(timezone.now().timestamp()), ) issue = self.get_queryset().filter(pk=pk).first() - return Response(IssueSerializer(issue).data, status=status.HTTP_200_OK) + return Response( + IssueSerializer(issue).data, status=status.HTTP_200_OK + ) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def destroy(self, request, slug, project_id, pk=None): - issue = Issue.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) + issue = Issue.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk + ) current_instance = json.dumps( IssueSerializer(issue).data, cls=DjangoJSONEncoder ) @@ -302,7 +332,13 @@ class UserWorkSpaceIssues(BaseAPIView): filters = issue_filters(request.query_params, "GET") # Custom ordering for priority and state priority_order = ["urgent", "high", "medium", "low", "none"] - state_order = ["backlog", "unstarted", "started", "completed", "cancelled"] + state_order = [ + "backlog", + "unstarted", + "started", + "completed", + "cancelled", + ] order_by_param = request.GET.get("order_by", "-created_at") @@ -316,7 +352,9 @@ class UserWorkSpaceIssues(BaseAPIView): workspace__slug=slug, ) .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -335,7 +373,9 @@ class UserWorkSpaceIssues(BaseAPIView): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -352,7 +392,9 @@ class UserWorkSpaceIssues(BaseAPIView): # Priority Ordering if order_by_param == "priority" or order_by_param == "-priority": priority_order = ( - priority_order if order_by_param == "priority" else priority_order[::-1] + priority_order + if order_by_param == "priority" + else priority_order[::-1] ) issue_queryset = issue_queryset.annotate( priority_order=Case( @@ -400,7 +442,9 @@ class UserWorkSpaceIssues(BaseAPIView): else order_by_param ) ).order_by( - "-max_values" if order_by_param.startswith("-") else "max_values" + "-max_values" + if order_by_param.startswith("-") + else "max_values" ) else: issue_queryset = issue_queryset.order_by(order_by_param) @@ -469,7 +513,9 @@ class IssueActivityEndpoint(BaseAPIView): ) ) ) - issue_activities = IssueActivitySerializer(issue_activities, many=True).data + issue_activities = IssueActivitySerializer( + issue_activities, many=True + ).data issue_comments = IssueCommentSerializer(issue_comments, many=True).data result_list = sorted( @@ -527,7 +573,9 @@ class IssueCommentViewSet(WebhookMixin, BaseViewSet): ) issue_activity.delay( type="comment.activity.created", - requested_data=json.dumps(serializer.data, cls=DjangoJSONEncoder), + requested_data=json.dumps( + serializer.data, cls=DjangoJSONEncoder + ), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id")), project_id=str(self.kwargs.get("project_id")), @@ -539,7 +587,10 @@ class IssueCommentViewSet(WebhookMixin, BaseViewSet): def partial_update(self, request, slug, project_id, issue_id, pk): issue_comment = IssueComment.objects.get( - workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk + workspace__slug=slug, + project_id=project_id, + issue_id=issue_id, + pk=pk, ) requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder) current_instance = json.dumps( @@ -565,7 +616,10 @@ class IssueCommentViewSet(WebhookMixin, BaseViewSet): def destroy(self, request, slug, project_id, issue_id, pk): issue_comment = IssueComment.objects.get( - workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk + workspace__slug=slug, + project_id=project_id, + issue_id=issue_id, + pk=pk, ) current_instance = json.dumps( IssueCommentSerializer(issue_comment).data, @@ -595,7 +649,9 @@ class IssueUserDisplayPropertyEndpoint(BaseAPIView): project_id=project_id, ) - issue_property.filters = request.data.get("filters", issue_property.filters) + issue_property.filters = request.data.get( + "filters", issue_property.filters + ) issue_property.display_filters = request.data.get( "display_filters", issue_property.display_filters ) @@ -626,11 +682,17 @@ class LabelViewSet(BaseViewSet): serializer = LabelSerializer(data=request.data) if serializer.is_valid(): serializer.save(project_id=project_id) - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + serializer.data, status=status.HTTP_201_CREATED + ) + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) except IntegrityError: return Response( - {"error": "Label with the same name already exists in the project"}, + { + "error": "Label with the same name already exists in the project" + }, status=status.HTTP_400_BAD_REQUEST, ) @@ -685,7 +747,9 @@ class SubIssuesEndpoint(BaseAPIView): @method_decorator(gzip_page) def get(self, request, slug, project_id, issue_id): sub_issues = ( - Issue.issue_objects.filter(parent_id=issue_id, workspace__slug=slug) + Issue.issue_objects.filter( + parent_id=issue_id, workspace__slug=slug + ) .select_related("project") .select_related("workspace") .select_related("state") @@ -693,7 +757,9 @@ class SubIssuesEndpoint(BaseAPIView): .prefetch_related("assignees") .prefetch_related("labels") .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -705,7 +771,9 @@ class SubIssuesEndpoint(BaseAPIView): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -719,7 +787,9 @@ class SubIssuesEndpoint(BaseAPIView): ) state_distribution = ( - State.objects.filter(workspace__slug=slug, state_issue__parent_id=issue_id) + State.objects.filter( + workspace__slug=slug, state_issue__parent_id=issue_id + ) .annotate(state_group=F("group")) .values("state_group") .annotate(state_count=Count("state_group")) @@ -727,7 +797,8 @@ class SubIssuesEndpoint(BaseAPIView): ) result = { - item["state_group"]: item["state_count"] for item in state_distribution + item["state_group"]: item["state_count"] + for item in state_distribution } serializer = IssueSerializer( @@ -811,7 +882,9 @@ class IssueLinkViewSet(BaseViewSet): ) issue_activity.delay( type="link.activity.created", - requested_data=json.dumps(serializer.data, cls=DjangoJSONEncoder), + requested_data=json.dumps( + serializer.data, cls=DjangoJSONEncoder + ), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id")), project_id=str(self.kwargs.get("project_id")), @@ -823,14 +896,19 @@ class IssueLinkViewSet(BaseViewSet): def partial_update(self, request, slug, project_id, issue_id, pk): issue_link = IssueLink.objects.get( - workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk + workspace__slug=slug, + project_id=project_id, + issue_id=issue_id, + pk=pk, ) requested_data = json.dumps(request.data, cls=DjangoJSONEncoder) current_instance = json.dumps( IssueLinkSerializer(issue_link).data, cls=DjangoJSONEncoder, ) - serializer = IssueLinkSerializer(issue_link, data=request.data, partial=True) + serializer = IssueLinkSerializer( + issue_link, data=request.data, partial=True + ) if serializer.is_valid(): serializer.save() issue_activity.delay( @@ -847,7 +925,10 @@ class IssueLinkViewSet(BaseViewSet): def destroy(self, request, slug, project_id, issue_id, pk): issue_link = IssueLink.objects.get( - workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk + workspace__slug=slug, + project_id=project_id, + issue_id=issue_id, + pk=pk, ) current_instance = json.dumps( IssueLinkSerializer(issue_link).data, @@ -973,13 +1054,23 @@ class IssueArchiveViewSet(BaseViewSet): @method_decorator(gzip_page) def list(self, request, slug, project_id): - fields = [field for field in request.GET.get("fields", "").split(",") if field] + fields = [ + field + for field in request.GET.get("fields", "").split(",") + if field + ] filters = issue_filters(request.query_params, "GET") show_sub_issues = request.GET.get("show_sub_issues", "true") # Custom ordering for priority and state priority_order = ["urgent", "high", "medium", "low", "none"] - state_order = ["backlog", "unstarted", "started", "completed", "cancelled"] + state_order = [ + "backlog", + "unstarted", + "started", + "completed", + "cancelled", + ] order_by_param = request.GET.get("order_by", "-created_at") @@ -995,7 +1086,9 @@ class IssueArchiveViewSet(BaseViewSet): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -1005,7 +1098,9 @@ class IssueArchiveViewSet(BaseViewSet): # Priority Ordering if order_by_param == "priority" or order_by_param == "-priority": priority_order = ( - priority_order if order_by_param == "priority" else priority_order[::-1] + priority_order + if order_by_param == "priority" + else priority_order[::-1] ) issue_queryset = issue_queryset.annotate( priority_order=Case( @@ -1053,7 +1148,9 @@ class IssueArchiveViewSet(BaseViewSet): else order_by_param ) ).order_by( - "-max_values" if order_by_param.startswith("-") else "max_values" + "-max_values" + if order_by_param.startswith("-") + else "max_values" ) else: issue_queryset = issue_queryset.order_by(order_by_param) @@ -1141,14 +1238,11 @@ class IssueSubscriberViewSet(BaseViewSet): ) def list(self, request, slug, project_id, issue_id): - members = ( - ProjectMember.objects.filter( - workspace__slug=slug, - project_id=project_id, - is_active=True, - ) - .select_related("member") - ) + members = ProjectMember.objects.filter( + workspace__slug=slug, + project_id=project_id, + is_active=True, + ).select_related("member") serializer = ProjectMemberLiteSerializer(members, many=True) return Response(serializer.data, status=status.HTTP_200_OK) @@ -1203,7 +1297,9 @@ class IssueSubscriberViewSet(BaseViewSet): workspace__slug=slug, project=project_id, ).exists() - return Response({"subscribed": issue_subscriber}, status=status.HTTP_200_OK) + return Response( + {"subscribed": issue_subscriber}, status=status.HTTP_200_OK + ) class IssueReactionViewSet(BaseViewSet): @@ -1360,7 +1456,9 @@ class IssueRelationViewSet(BaseViewSet): def list(self, request, slug, project_id, issue_id): issue_relations = ( - IssueRelation.objects.filter(Q(issue_id=issue_id) | Q(related_issue=issue_id)) + IssueRelation.objects.filter( + Q(issue_id=issue_id) | Q(related_issue=issue_id) + ) .filter(workspace__slug=self.kwargs.get("slug")) .select_related("project") .select_related("workspace") @@ -1369,34 +1467,59 @@ class IssueRelationViewSet(BaseViewSet): .distinct() ) - blocking_issues = issue_relations.filter(relation_type="blocked_by", related_issue_id=issue_id) - blocked_by_issues = issue_relations.filter(relation_type="blocked_by", issue_id=issue_id) - duplicate_issues = issue_relations.filter(issue_id=issue_id, relation_type="duplicate") - duplicate_issues_related = issue_relations.filter(related_issue_id=issue_id, relation_type="duplicate") - relates_to_issues = issue_relations.filter(issue_id=issue_id, relation_type="relates_to") - relates_to_issues_related = issue_relations.filter(related_issue_id=issue_id, relation_type="relates_to") + blocking_issues = issue_relations.filter( + relation_type="blocked_by", related_issue_id=issue_id + ) + blocked_by_issues = issue_relations.filter( + relation_type="blocked_by", issue_id=issue_id + ) + duplicate_issues = issue_relations.filter( + issue_id=issue_id, relation_type="duplicate" + ) + duplicate_issues_related = issue_relations.filter( + related_issue_id=issue_id, relation_type="duplicate" + ) + relates_to_issues = issue_relations.filter( + issue_id=issue_id, relation_type="relates_to" + ) + relates_to_issues_related = issue_relations.filter( + related_issue_id=issue_id, relation_type="relates_to" + ) - blocked_by_issues_serialized = IssueRelationSerializer(blocked_by_issues, many=True).data - duplicate_issues_serialized = IssueRelationSerializer(duplicate_issues, many=True).data - relates_to_issues_serialized = IssueRelationSerializer(relates_to_issues, many=True).data + blocked_by_issues_serialized = IssueRelationSerializer( + blocked_by_issues, many=True + ).data + duplicate_issues_serialized = IssueRelationSerializer( + duplicate_issues, many=True + ).data + relates_to_issues_serialized = IssueRelationSerializer( + relates_to_issues, many=True + ).data # revere relation for blocked by issues - blocking_issues_serialized = RelatedIssueSerializer(blocking_issues, many=True).data + blocking_issues_serialized = RelatedIssueSerializer( + blocking_issues, many=True + ).data # reverse relation for duplicate issues - duplicate_issues_related_serialized = RelatedIssueSerializer(duplicate_issues_related, many=True).data + duplicate_issues_related_serialized = RelatedIssueSerializer( + duplicate_issues_related, many=True + ).data # reverse relation for related issues - relates_to_issues_related_serialized = RelatedIssueSerializer(relates_to_issues_related, many=True).data + relates_to_issues_related_serialized = RelatedIssueSerializer( + relates_to_issues_related, many=True + ).data response_data = { - 'blocking': blocking_issues_serialized, - 'blocked_by': blocked_by_issues_serialized, - 'duplicate': duplicate_issues_serialized + duplicate_issues_related_serialized, - 'relates_to': relates_to_issues_serialized + relates_to_issues_related_serialized, + "blocking": blocking_issues_serialized, + "blocked_by": blocked_by_issues_serialized, + "duplicate": duplicate_issues_serialized + + duplicate_issues_related_serialized, + "relates_to": relates_to_issues_serialized + + relates_to_issues_related_serialized, } return Response(response_data, status=status.HTTP_200_OK) - def create(self, request, slug, project_id, issue_id): relation_type = request.data.get("relation_type", None) issues = request.data.get("issues", []) @@ -1405,9 +1528,15 @@ class IssueRelationViewSet(BaseViewSet): issue_relation = IssueRelation.objects.bulk_create( [ IssueRelation( - issue_id=issue if relation_type == "blocking" else issue_id, - related_issue_id=issue_id if relation_type == "blocking" else issue, - relation_type="blocked_by" if relation_type == "blocking" else relation_type, + issue_id=issue + if relation_type == "blocking" + else issue_id, + related_issue_id=issue_id + if relation_type == "blocking" + else issue, + relation_type="blocked_by" + if relation_type == "blocking" + else relation_type, project_id=project_id, workspace_id=project.workspace_id, created_by=request.user, @@ -1446,11 +1575,17 @@ class IssueRelationViewSet(BaseViewSet): if relation_type == "blocking": issue_relation = IssueRelation.objects.get( - workspace__slug=slug, project_id=project_id, issue_id=related_issue, related_issue_id=issue_id + workspace__slug=slug, + project_id=project_id, + issue_id=related_issue, + related_issue_id=issue_id, ) else: issue_relation = IssueRelation.objects.get( - workspace__slug=slug, project_id=project_id, issue_id=issue_id, related_issue_id=related_issue + workspace__slug=slug, + project_id=project_id, + issue_id=issue_id, + related_issue_id=related_issue, ) current_instance = json.dumps( IssueRelationSerializer(issue_relation).data, @@ -1479,7 +1614,9 @@ class IssueDraftViewSet(BaseViewSet): def get_queryset(self): return ( Issue.objects.annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -1504,11 +1641,21 @@ class IssueDraftViewSet(BaseViewSet): @method_decorator(gzip_page) def list(self, request, slug, project_id): filters = issue_filters(request.query_params, "GET") - fields = [field for field in request.GET.get("fields", "").split(",") if field] + fields = [ + field + for field in request.GET.get("fields", "").split(",") + if field + ] # Custom ordering for priority and state priority_order = ["urgent", "high", "medium", "low", "none"] - state_order = ["backlog", "unstarted", "started", "completed", "cancelled"] + state_order = [ + "backlog", + "unstarted", + "started", + "completed", + "cancelled", + ] order_by_param = request.GET.get("order_by", "-created_at") @@ -1524,7 +1671,9 @@ class IssueDraftViewSet(BaseViewSet): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -1534,7 +1683,9 @@ class IssueDraftViewSet(BaseViewSet): # Priority Ordering if order_by_param == "priority" or order_by_param == "-priority": priority_order = ( - priority_order if order_by_param == "priority" else priority_order[::-1] + priority_order + if order_by_param == "priority" + else priority_order[::-1] ) issue_queryset = issue_queryset.annotate( priority_order=Case( @@ -1582,7 +1733,9 @@ class IssueDraftViewSet(BaseViewSet): else order_by_param ) ).order_by( - "-max_values" if order_by_param.startswith("-") else "max_values" + "-max_values" + if order_by_param.startswith("-") + else "max_values" ) else: issue_queryset = issue_queryset.order_by(order_by_param) @@ -1610,7 +1763,9 @@ class IssueDraftViewSet(BaseViewSet): # Track the issue issue_activity.delay( type="issue_draft.activity.created", - requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), + requested_data=json.dumps( + self.request.data, cls=DjangoJSONEncoder + ), actor_id=str(request.user.id), issue_id=str(serializer.data.get("id", None)), project_id=str(project_id), @@ -1621,14 +1776,18 @@ class IssueDraftViewSet(BaseViewSet): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def partial_update(self, request, slug, project_id, pk): - issue = Issue.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) + issue = Issue.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk + ) serializer = IssueSerializer(issue, data=request.data, partial=True) if serializer.is_valid(): - if request.data.get("is_draft") is not None and not request.data.get( + if request.data.get( "is_draft" - ): - serializer.save(created_at=timezone.now(), updated_at=timezone.now()) + ) is not None and not request.data.get("is_draft"): + serializer.save( + created_at=timezone.now(), updated_at=timezone.now() + ) else: serializer.save() issue_activity.delay( @@ -1653,7 +1812,9 @@ class IssueDraftViewSet(BaseViewSet): return Response(IssueSerializer(issue).data, status=status.HTTP_200_OK) def destroy(self, request, slug, project_id, pk=None): - issue = Issue.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) + issue = Issue.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk + ) current_instance = json.dumps( IssueSerializer(issue).data, cls=DjangoJSONEncoder ) diff --git a/apiserver/plane/app/views/module.py b/apiserver/plane/app/views/module.py index 576b763fd..09d763ab7 100644 --- a/apiserver/plane/app/views/module.py +++ b/apiserver/plane/app/views/module.py @@ -23,7 +23,10 @@ from plane.app.serializers import ( IssueSerializer, ModuleUserPropertiesSerializer, ) -from plane.app.permissions import ProjectEntityPermission, ProjectLitePermission +from plane.app.permissions import ( + ProjectEntityPermission, + ProjectLitePermission, +) from plane.db.models import ( Module, ModuleIssue, @@ -76,7 +79,9 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): .prefetch_related( Prefetch( "link_module", - queryset=ModuleLink.objects.select_related("module", "created_by"), + queryset=ModuleLink.objects.select_related( + "module", "created_by" + ), ) ) .annotate( @@ -157,7 +162,11 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): def list(self, request, slug, project_id): queryset = self.get_queryset() - fields = [field for field in request.GET.get("fields", "").split(",") if field] + fields = [ + field + for field in request.GET.get("fields", "").split(",") + if field + ] modules = ModuleSerializer( queryset, many=True, fields=fields if fields else None ).data @@ -177,7 +186,13 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): .annotate(assignee_id=F("assignees__id")) .annotate(display_name=F("assignees__display_name")) .annotate(avatar=F("assignees__avatar")) - .values("first_name", "last_name", "assignee_id", "avatar", "display_name") + .values( + "first_name", + "last_name", + "assignee_id", + "avatar", + "display_name", + ) .annotate( total_issues=Count( "assignee_id", @@ -261,7 +276,10 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): if queryset.start_date and queryset.target_date: data["distribution"]["completion_chart"] = burndown_plot( - queryset=queryset, slug=slug, project_id=project_id, module_id=pk + queryset=queryset, + slug=slug, + project_id=project_id, + module_id=pk, ) return Response( @@ -270,9 +288,13 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): ) def destroy(self, request, slug, project_id, pk): - module = Module.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) + module = Module.objects.get( + workspace__slug=slug, project_id=project_id, pk=pk + ) module_issues = list( - ModuleIssue.objects.filter(module_id=pk).values_list("issue", flat=True) + ModuleIssue.objects.filter(module_id=pk).values_list( + "issue", flat=True + ) ) issue_activity.delay( type="module.activity.deleted", @@ -313,7 +335,9 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet): super() .get_queryset() .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("issue")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("issue") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -333,13 +357,19 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet): @method_decorator(gzip_page) def list(self, request, slug, project_id, module_id): - fields = [field for field in request.GET.get("fields", "").split(",") if field] + fields = [ + field + for field in request.GET.get("fields", "").split(",") + if field + ] order_by = request.GET.get("order_by", "created_at") filters = issue_filters(request.query_params, "GET") issues = ( Issue.issue_objects.filter(issue_module__module_id=module_id) .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -363,7 +393,9 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -385,7 +417,8 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet): issues = request.data.get("issues", []) if not len(issues): return Response( - {"error": "Issues are required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Issues are required"}, + status=status.HTTP_400_BAD_REQUEST, ) module = Module.objects.get( workspace__slug=slug, project_id=project_id, pk=module_id @@ -460,7 +493,9 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet): issues = self.get_queryset().values_list("issue_id", flat=True) return Response( - IssueSerializer(Issue.objects.filter(pk__in=issues), many=True).data, + IssueSerializer( + Issue.objects.filter(pk__in=issues), many=True + ).data, status=status.HTTP_200_OK, ) diff --git a/apiserver/plane/app/views/notification.py b/apiserver/plane/app/views/notification.py index 9494ea86c..15eef9cf0 100644 --- a/apiserver/plane/app/views/notification.py +++ b/apiserver/plane/app/views/notification.py @@ -51,8 +51,10 @@ class NotificationViewSet(BaseViewSet, BasePaginator): # Filters based on query parameters snoozed_filters = { - "true": Q(snoozed_till__lt=timezone.now()) | Q(snoozed_till__isnull=False), - "false": Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True), + "true": Q(snoozed_till__lt=timezone.now()) + | Q(snoozed_till__isnull=False), + "false": Q(snoozed_till__gte=timezone.now()) + | Q(snoozed_till__isnull=True), } notifications = notifications.filter(snoozed_filters[snoozed]) @@ -72,14 +74,18 @@ class NotificationViewSet(BaseViewSet, BasePaginator): issue_ids = IssueSubscriber.objects.filter( workspace__slug=slug, subscriber_id=request.user.id ).values_list("issue_id", flat=True) - notifications = notifications.filter(entity_identifier__in=issue_ids) + notifications = notifications.filter( + entity_identifier__in=issue_ids + ) # Assigned Issues if type == "assigned": issue_ids = IssueAssignee.objects.filter( workspace__slug=slug, assignee_id=request.user.id ).values_list("issue_id", flat=True) - notifications = notifications.filter(entity_identifier__in=issue_ids) + notifications = notifications.filter( + entity_identifier__in=issue_ids + ) # Created issues if type == "created": @@ -94,10 +100,14 @@ class NotificationViewSet(BaseViewSet, BasePaginator): issue_ids = Issue.objects.filter( workspace__slug=slug, created_by=request.user ).values_list("pk", flat=True) - notifications = notifications.filter(entity_identifier__in=issue_ids) + notifications = notifications.filter( + entity_identifier__in=issue_ids + ) # Pagination - if request.GET.get("per_page", False) and request.GET.get("cursor", False): + if request.GET.get("per_page", False) and request.GET.get( + "cursor", False + ): return self.paginate( request=request, queryset=(notifications), @@ -227,11 +237,13 @@ class MarkAllReadNotificationViewSet(BaseViewSet): # Filter for snoozed notifications if snoozed: notifications = notifications.filter( - Q(snoozed_till__lt=timezone.now()) | Q(snoozed_till__isnull=False) + Q(snoozed_till__lt=timezone.now()) + | Q(snoozed_till__isnull=False) ) else: notifications = notifications.filter( - Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True), + Q(snoozed_till__gte=timezone.now()) + | Q(snoozed_till__isnull=True), ) # Filter for archived or unarchive @@ -245,14 +257,18 @@ class MarkAllReadNotificationViewSet(BaseViewSet): issue_ids = IssueSubscriber.objects.filter( workspace__slug=slug, subscriber_id=request.user.id ).values_list("issue_id", flat=True) - notifications = notifications.filter(entity_identifier__in=issue_ids) + notifications = notifications.filter( + entity_identifier__in=issue_ids + ) # Assigned Issues if type == "assigned": issue_ids = IssueAssignee.objects.filter( workspace__slug=slug, assignee_id=request.user.id ).values_list("issue_id", flat=True) - notifications = notifications.filter(entity_identifier__in=issue_ids) + notifications = notifications.filter( + entity_identifier__in=issue_ids + ) # Created issues if type == "created": @@ -267,7 +283,9 @@ class MarkAllReadNotificationViewSet(BaseViewSet): issue_ids = Issue.objects.filter( workspace__slug=slug, created_by=request.user ).values_list("pk", flat=True) - notifications = notifications.filter(entity_identifier__in=issue_ids) + notifications = notifications.filter( + entity_identifier__in=issue_ids + ) updated_notifications = [] for notification in notifications: diff --git a/apiserver/plane/app/views/page.py b/apiserver/plane/app/views/page.py index 482bdfbfe..1054b6af3 100644 --- a/apiserver/plane/app/views/page.py +++ b/apiserver/plane/app/views/page.py @@ -97,7 +97,9 @@ class PageViewSet(BaseViewSet): def partial_update(self, request, slug, project_id, pk): try: - page = Page.objects.get(pk=pk, workspace__slug=slug, project_id=project_id) + page = Page.objects.get( + pk=pk, workspace__slug=slug, project_id=project_id + ) if page.is_locked: return Response( @@ -127,7 +129,9 @@ class PageViewSet(BaseViewSet): if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) except Page.DoesNotExist: return Response( { @@ -161,12 +165,17 @@ class PageViewSet(BaseViewSet): return Response(pages, status=status.HTTP_200_OK) def archive(self, request, slug, project_id, page_id): - page = Page.objects.get(pk=page_id, workspace__slug=slug, project_id=project_id) + page = Page.objects.get( + pk=page_id, workspace__slug=slug, project_id=project_id + ) # only the owner and admin can archive the page if ( ProjectMember.objects.filter( - project_id=project_id, member=request.user, is_active=True, role__gt=20 + project_id=project_id, + member=request.user, + is_active=True, + role__gt=20, ).exists() or request.user.id != page.owned_by_id ): @@ -180,12 +189,17 @@ class PageViewSet(BaseViewSet): return Response(status=status.HTTP_204_NO_CONTENT) def unarchive(self, request, slug, project_id, page_id): - page = Page.objects.get(pk=page_id, workspace__slug=slug, project_id=project_id) + page = Page.objects.get( + pk=page_id, workspace__slug=slug, project_id=project_id + ) # only the owner and admin can un archive the page if ( ProjectMember.objects.filter( - project_id=project_id, member=request.user, is_active=True, role__gt=20 + project_id=project_id, + member=request.user, + is_active=True, + role__gt=20, ).exists() or request.user.id != page.owned_by_id ): @@ -212,14 +226,18 @@ class PageViewSet(BaseViewSet): pages = PageSerializer(pages, many=True).data return Response(pages, status=status.HTTP_200_OK) - def destroy(self, request, slug, project_id, pk): - page = Page.objects.get(pk=pk, workspace__slug=slug, project_id=project_id) + page = Page.objects.get( + pk=pk, workspace__slug=slug, project_id=project_id + ) # only the owner and admin can delete the page if ( ProjectMember.objects.filter( - project_id=project_id, member=request.user, is_active=True, role__gt=20 + project_id=project_id, + member=request.user, + is_active=True, + role__gt=20, ).exists() or request.user.id != page.owned_by_id ): diff --git a/apiserver/plane/app/views/project.py b/apiserver/plane/app/views/project.py index c5caac666..2895661f8 100644 --- a/apiserver/plane/app/views/project.py +++ b/apiserver/plane/app/views/project.py @@ -86,9 +86,15 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): super() .get_queryset() .filter(workspace__slug=self.kwargs.get("slug")) - .filter(Q(project_projectmember__member=self.request.user) | Q(network=2)) + .filter( + Q(project_projectmember__member=self.request.user) + | Q(network=2) + ) .select_related( - "workspace", "workspace__owner", "default_assignee", "project_lead" + "workspace", + "workspace__owner", + "default_assignee", + "project_lead", ) .annotate( is_favorite=Exists( @@ -160,7 +166,11 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): ) def list(self, request, slug): - fields = [field for field in request.GET.get("fields", "").split(",") if field] + fields = [ + field + for field in request.GET.get("fields", "").split(",") + if field + ] sort_order_query = ProjectMember.objects.filter( member=request.user, @@ -173,7 +183,9 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): .annotate(sort_order=Subquery(sort_order_query)) .order_by("sort_order", "name") ) - if request.GET.get("per_page", False) and request.GET.get("cursor", False): + if request.GET.get("per_page", False) and request.GET.get( + "cursor", False + ): return self.paginate( request=request, queryset=(projects), @@ -181,10 +193,11 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): projects, many=True ).data, ) - projects = ProjectListSerializer(projects, many=True, fields=fields if fields else None).data + projects = ProjectListSerializer( + projects, many=True, fields=fields if fields else None + ).data return Response(projects, status=status.HTTP_200_OK) - def create(self, request, slug): try: workspace = Workspace.objects.get(slug=slug) @@ -197,7 +210,9 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): # Add the user as Administrator to the project project_member = ProjectMember.objects.create( - project_id=serializer.data["id"], member=request.user, role=20 + project_id=serializer.data["id"], + member=request.user, + role=20, ) # Also create the issue property for the user _ = IssueProperty.objects.create( @@ -270,9 +285,15 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): ] ) - project = self.get_queryset().filter(pk=serializer.data["id"]).first() + project = ( + self.get_queryset() + .filter(pk=serializer.data["id"]) + .first() + ) serializer = ProjectListSerializer(project) - return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response( + serializer.data, status=status.HTTP_201_CREATED + ) return Response( serializer.errors, status=status.HTTP_400_BAD_REQUEST, @@ -285,7 +306,8 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): ) except Workspace.DoesNotExist as e: return Response( - {"error": "Workspace does not exist"}, status=status.HTTP_404_NOT_FOUND + {"error": "Workspace does not exist"}, + status=status.HTTP_404_NOT_FOUND, ) except serializers.ValidationError as e: return Response( @@ -310,7 +332,9 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): serializer.save() if serializer.data["inbox_view"]: Inbox.objects.get_or_create( - name=f"{project.name} Inbox", project=project, is_default=True + name=f"{project.name} Inbox", + project=project, + is_default=True, ) # Create the triage state in Backlog group @@ -322,10 +346,16 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): color="#ff7700", ) - project = self.get_queryset().filter(pk=serializer.data["id"]).first() + project = ( + self.get_queryset() + .filter(pk=serializer.data["id"]) + .first() + ) serializer = ProjectListSerializer(project) return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) except IntegrityError as e: if "already exists" in str(e): @@ -335,7 +365,8 @@ class ProjectViewSet(WebhookMixin, BaseViewSet): ) except (Project.DoesNotExist, Workspace.DoesNotExist): return Response( - {"error": "Project does not exist"}, status=status.HTTP_404_NOT_FOUND + {"error": "Project does not exist"}, + status=status.HTTP_404_NOT_FOUND, ) except serializers.ValidationError as e: return Response( @@ -370,11 +401,14 @@ class ProjectInvitationsViewset(BaseViewSet): # Check if email is provided if not emails: return Response( - {"error": "Emails are required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Emails are required"}, + status=status.HTTP_400_BAD_REQUEST, ) requesting_user = ProjectMember.objects.get( - workspace__slug=slug, project_id=project_id, member_id=request.user.id + workspace__slug=slug, + project_id=project_id, + member_id=request.user.id, ) # Check if any invited user has an higher role @@ -548,7 +582,9 @@ class ProjectJoinEndpoint(BaseAPIView): _ = WorkspaceMember.objects.create( workspace_id=project_invite.workspace_id, member=user, - role=15 if project_invite.role >= 15 else project_invite.role, + role=15 + if project_invite.role >= 15 + else project_invite.role, ) else: # Else make him active @@ -658,7 +694,8 @@ class ProjectMemberViewSet(BaseViewSet): sort_order = [ project_member.get("sort_order") for project_member in project_members - if str(project_member.get("member_id")) == str(member.get("member_id")) + if str(project_member.get("member_id")) + == str(member.get("member_id")) ] bulk_project_members.append( ProjectMember( @@ -666,7 +703,9 @@ class ProjectMemberViewSet(BaseViewSet): role=member.get("role", 10), project_id=project_id, workspace_id=project.workspace_id, - sort_order=sort_order[0] - 10000 if len(sort_order) else 65535, + sort_order=sort_order[0] - 10000 + if len(sort_order) + else 65535, ) ) bulk_issue_props.append( @@ -719,7 +758,9 @@ class ProjectMemberViewSet(BaseViewSet): is_active=True, ).select_related("project", "member", "workspace") - serializer = ProjectMemberRoleSerializer(project_members, fields=("id", "member", "role"), many=True) + serializer = ProjectMemberRoleSerializer( + project_members, fields=("id", "member", "role"), many=True + ) return Response(serializer.data, status=status.HTTP_200_OK) def partial_update(self, request, slug, project_id, pk): @@ -747,7 +788,9 @@ class ProjectMemberViewSet(BaseViewSet): > requested_project_member.role ): return Response( - {"error": "You cannot update a role that is higher than your own role"}, + { + "error": "You cannot update a role that is higher than your own role" + }, status=status.HTTP_400_BAD_REQUEST, ) @@ -786,7 +829,9 @@ class ProjectMemberViewSet(BaseViewSet): # User cannot deactivate higher role if requesting_project_member.role < project_member.role: return Response( - {"error": "You cannot remove a user having role higher than you"}, + { + "error": "You cannot remove a user having role higher than you" + }, status=status.HTTP_400_BAD_REQUEST, ) @@ -837,7 +882,8 @@ class AddTeamToProjectEndpoint(BaseAPIView): if len(team_members) == 0: return Response( - {"error": "No such team exists"}, status=status.HTTP_400_BAD_REQUEST + {"error": "No such team exists"}, + status=status.HTTP_400_BAD_REQUEST, ) workspace = Workspace.objects.get(slug=slug) @@ -884,7 +930,8 @@ class ProjectIdentifierEndpoint(BaseAPIView): if name == "": return Response( - {"error": "Name is required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Name is required"}, + status=status.HTTP_400_BAD_REQUEST, ) exists = ProjectIdentifier.objects.filter( @@ -901,16 +948,23 @@ class ProjectIdentifierEndpoint(BaseAPIView): if name == "": return Response( - {"error": "Name is required"}, status=status.HTTP_400_BAD_REQUEST - ) - - if Project.objects.filter(identifier=name, workspace__slug=slug).exists(): - return Response( - {"error": "Cannot delete an identifier of an existing project"}, + {"error": "Name is required"}, status=status.HTTP_400_BAD_REQUEST, ) - ProjectIdentifier.objects.filter(name=name, workspace__slug=slug).delete() + if Project.objects.filter( + identifier=name, workspace__slug=slug + ).exists(): + return Response( + { + "error": "Cannot delete an identifier of an existing project" + }, + status=status.HTTP_400_BAD_REQUEST, + ) + + ProjectIdentifier.objects.filter( + name=name, workspace__slug=slug + ).delete() return Response( status=status.HTTP_204_NO_CONTENT, @@ -928,7 +982,9 @@ class ProjectUserViewsEndpoint(BaseAPIView): ).first() if project_member is None: - return Response({"error": "Forbidden"}, status=status.HTTP_403_FORBIDDEN) + return Response( + {"error": "Forbidden"}, status=status.HTTP_403_FORBIDDEN + ) view_props = project_member.view_props default_props = project_member.default_props @@ -936,8 +992,12 @@ class ProjectUserViewsEndpoint(BaseAPIView): sort_order = project_member.sort_order project_member.view_props = request.data.get("view_props", view_props) - project_member.default_props = request.data.get("default_props", default_props) - project_member.preferences = request.data.get("preferences", preferences) + project_member.default_props = request.data.get( + "default_props", default_props + ) + project_member.preferences = request.data.get( + "preferences", preferences + ) project_member.sort_order = request.data.get("sort_order", sort_order) project_member.save() @@ -1085,6 +1145,7 @@ class UserProjectRolesEndpoint(BaseAPIView): ).values("project_id", "role") project_members = { - str(member["project_id"]): member["role"] for member in project_members + str(member["project_id"]): member["role"] + for member in project_members } return Response(project_members, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/search.py b/apiserver/plane/app/views/search.py index 4ecb71127..0455541c6 100644 --- a/apiserver/plane/app/views/search.py +++ b/apiserver/plane/app/views/search.py @@ -10,7 +10,15 @@ from rest_framework.response import Response # Module imports from .base import BaseAPIView -from plane.db.models import Workspace, Project, Issue, Cycle, Module, Page, IssueView +from plane.db.models import ( + Workspace, + Project, + Issue, + Cycle, + Module, + Page, + IssueView, +) from plane.utils.issue_search import search_issues @@ -25,7 +33,9 @@ class GlobalSearchEndpoint(BaseAPIView): for field in fields: q |= Q(**{f"{field}__icontains": query}) return ( - Workspace.objects.filter(q, workspace_member__member=self.request.user) + Workspace.objects.filter( + q, workspace_member__member=self.request.user + ) .distinct() .values("name", "id", "slug") ) @@ -38,7 +48,8 @@ class GlobalSearchEndpoint(BaseAPIView): return ( Project.objects.filter( q, - Q(project_projectmember__member=self.request.user) | Q(network=2), + Q(project_projectmember__member=self.request.user) + | Q(network=2), workspace__slug=slug, ) .distinct() @@ -169,7 +180,9 @@ class GlobalSearchEndpoint(BaseAPIView): def get(self, request, slug): query = request.query_params.get("search", False) - workspace_search = request.query_params.get("workspace_search", "false") + workspace_search = request.query_params.get( + "workspace_search", "false" + ) project_id = request.query_params.get("project_id", False) if not query: @@ -209,7 +222,9 @@ class GlobalSearchEndpoint(BaseAPIView): class IssueSearchEndpoint(BaseAPIView): def get(self, request, slug, project_id): query = request.query_params.get("search", False) - workspace_search = request.query_params.get("workspace_search", "false") + workspace_search = request.query_params.get( + "workspace_search", "false" + ) parent = request.query_params.get("parent", "false") issue_relation = request.query_params.get("issue_relation", "false") cycle = request.query_params.get("cycle", "false") @@ -234,9 +249,9 @@ class IssueSearchEndpoint(BaseAPIView): issues = issues.filter( ~Q(pk=issue_id), ~Q(pk=issue.parent_id), parent__isnull=True ).exclude( - pk__in=Issue.issue_objects.filter(parent__isnull=False).values_list( - "parent_id", flat=True - ) + pk__in=Issue.issue_objects.filter( + parent__isnull=False + ).values_list("parent_id", flat=True) ) if issue_relation == "true" and issue_id: issue = Issue.issue_objects.get(pk=issue_id) diff --git a/apiserver/plane/app/views/state.py b/apiserver/plane/app/views/state.py index f7226ba6e..9c83cf006 100644 --- a/apiserver/plane/app/views/state.py +++ b/apiserver/plane/app/views/state.py @@ -77,16 +77,21 @@ class StateViewSet(BaseViewSet): ) if state.default: - return Response({"error": "Default state cannot be deleted"}, status=status.HTTP_400_BAD_REQUEST) + return Response( + {"error": "Default state cannot be deleted"}, + status=status.HTTP_400_BAD_REQUEST, + ) # Check for any issues in the state issue_exist = Issue.issue_objects.filter(state=pk).exists() if issue_exist: return Response( - {"error": "The state is not empty, only empty states can be deleted"}, + { + "error": "The state is not empty, only empty states can be deleted" + }, status=status.HTTP_400_BAD_REQUEST, ) state.delete() - return Response(status=status.HTTP_204_NO_CONTENT) \ No newline at end of file + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apiserver/plane/app/views/user.py b/apiserver/plane/app/views/user.py index 008780526..7764e3b97 100644 --- a/apiserver/plane/app/views/user.py +++ b/apiserver/plane/app/views/user.py @@ -43,7 +43,9 @@ class UserEndpoint(BaseViewSet): is_admin = InstanceAdmin.objects.filter( instance=instance, user=request.user ).exists() - return Response({"is_instance_admin": is_admin}, status=status.HTTP_200_OK) + return Response( + {"is_instance_admin": is_admin}, status=status.HTTP_200_OK + ) def deactivate(self, request): # Check all workspace user is active @@ -51,7 +53,12 @@ class UserEndpoint(BaseViewSet): # Instance admin check if InstanceAdmin.objects.filter(user=user).exists(): - return Response({"error": "You cannot deactivate your account since you are an instance admin"}, status=status.HTTP_400_BAD_REQUEST) + return Response( + { + "error": "You cannot deactivate your account since you are an instance admin" + }, + status=status.HTTP_400_BAD_REQUEST, + ) projects_to_deactivate = [] workspaces_to_deactivate = [] @@ -61,7 +68,10 @@ class UserEndpoint(BaseViewSet): ).annotate( other_admin_exists=Count( Case( - When(Q(role=20, is_active=True) & ~Q(member=request.user), then=1), + When( + Q(role=20, is_active=True) & ~Q(member=request.user), + then=1, + ), default=0, output_field=IntegerField(), ) @@ -86,7 +96,10 @@ class UserEndpoint(BaseViewSet): ).annotate( other_admin_exists=Count( Case( - When(Q(role=20, is_active=True) & ~Q(member=request.user), then=1), + When( + Q(role=20, is_active=True) & ~Q(member=request.user), + then=1, + ), default=0, output_field=IntegerField(), ) @@ -95,7 +108,9 @@ class UserEndpoint(BaseViewSet): ) for workspace in workspaces: - if workspace.other_admin_exists > 0 or (workspace.total_members == 1): + if workspace.other_admin_exists > 0 or ( + workspace.total_members == 1 + ): workspace.is_active = False workspaces_to_deactivate.append(workspace) else: @@ -134,7 +149,9 @@ class UpdateUserOnBoardedEndpoint(BaseAPIView): user = User.objects.get(pk=request.user.id, is_active=True) user.is_onboarded = request.data.get("is_onboarded", False) user.save() - return Response({"message": "Updated successfully"}, status=status.HTTP_200_OK) + return Response( + {"message": "Updated successfully"}, status=status.HTTP_200_OK + ) class UpdateUserTourCompletedEndpoint(BaseAPIView): @@ -142,14 +159,16 @@ class UpdateUserTourCompletedEndpoint(BaseAPIView): user = User.objects.get(pk=request.user.id, is_active=True) user.is_tour_completed = request.data.get("is_tour_completed", False) user.save() - return Response({"message": "Updated successfully"}, status=status.HTTP_200_OK) + return Response( + {"message": "Updated successfully"}, status=status.HTTP_200_OK + ) class UserActivityEndpoint(BaseAPIView, BasePaginator): def get(self, request): - queryset = IssueActivity.objects.filter(actor=request.user).select_related( - "actor", "workspace", "issue", "project" - ) + queryset = IssueActivity.objects.filter( + actor=request.user + ).select_related("actor", "workspace", "issue", "project") return self.paginate( request=request, @@ -158,4 +177,3 @@ class UserActivityEndpoint(BaseAPIView, BasePaginator): issue_activities, many=True ).data, ) - diff --git a/apiserver/plane/app/views/view.py b/apiserver/plane/app/views/view.py index 0c9be5ae6..07bf1ad03 100644 --- a/apiserver/plane/app/views/view.py +++ b/apiserver/plane/app/views/view.py @@ -79,7 +79,9 @@ class GlobalViewIssuesViewSet(BaseViewSet): def get_queryset(self): return ( Issue.issue_objects.annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -102,11 +104,21 @@ class GlobalViewIssuesViewSet(BaseViewSet): @method_decorator(gzip_page) def list(self, request, slug): filters = issue_filters(request.query_params, "GET") - fields = [field for field in request.GET.get("fields", "").split(",") if field] + fields = [ + field + for field in request.GET.get("fields", "").split(",") + if field + ] # Custom ordering for priority and state priority_order = ["urgent", "high", "medium", "low", "none"] - state_order = ["backlog", "unstarted", "started", "completed", "cancelled"] + state_order = [ + "backlog", + "unstarted", + "started", + "completed", + "cancelled", + ] order_by_param = request.GET.get("order_by", "-created_at") @@ -123,13 +135,17 @@ class GlobalViewIssuesViewSet(BaseViewSet): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -146,7 +162,9 @@ class GlobalViewIssuesViewSet(BaseViewSet): # Priority Ordering if order_by_param == "priority" or order_by_param == "-priority": priority_order = ( - priority_order if order_by_param == "priority" else priority_order[::-1] + priority_order + if order_by_param == "priority" + else priority_order[::-1] ) issue_queryset = issue_queryset.annotate( priority_order=Case( @@ -194,7 +212,9 @@ class GlobalViewIssuesViewSet(BaseViewSet): else order_by_param ) ).order_by( - "-max_values" if order_by_param.startswith("-") else "max_values" + "-max_values" + if order_by_param.startswith("-") + else "max_values" ) else: issue_queryset = issue_queryset.order_by(order_by_param) @@ -237,7 +257,11 @@ class IssueViewViewSet(BaseViewSet): def list(self, request, slug, project_id): queryset = self.get_queryset() - fields = [field for field in request.GET.get("fields", "").split(",") if field] + fields = [ + field + for field in request.GET.get("fields", "").split(",") + if field + ] views = IssueViewSerializer( queryset, many=True, fields=fields if fields else None ).data diff --git a/apiserver/plane/app/views/webhook.py b/apiserver/plane/app/views/webhook.py index 48608d583..fe69cd7e6 100644 --- a/apiserver/plane/app/views/webhook.py +++ b/apiserver/plane/app/views/webhook.py @@ -26,8 +26,12 @@ class WebhookEndpoint(BaseAPIView): ) if serializer.is_valid(): serializer.save(workspace_id=workspace.id) - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + serializer.data, status=status.HTTP_201_CREATED + ) + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) except IntegrityError as e: if "already exists" in str(e): return Response( diff --git a/apiserver/plane/app/views/workspace.py b/apiserver/plane/app/views/workspace.py index f51e1ac1e..9cedff8f4 100644 --- a/apiserver/plane/app/views/workspace.py +++ b/apiserver/plane/app/views/workspace.py @@ -66,7 +66,7 @@ from plane.db.models import ( WorkspaceMember, CycleIssue, IssueReaction, - WorkspaceUserProperties + WorkspaceUserProperties, ) from plane.app.permissions import ( WorkSpaceBasePermission, @@ -116,7 +116,9 @@ class WorkSpaceViewSet(BaseViewSet): .values("count") ) return ( - self.filter_queryset(super().get_queryset().select_related("owner")) + self.filter_queryset( + super().get_queryset().select_related("owner") + ) .order_by("name") .filter( workspace_member__member=self.request.user, @@ -142,7 +144,9 @@ class WorkSpaceViewSet(BaseViewSet): if len(name) > 80 or len(slug) > 48: return Response( - {"error": "The maximum length for name is 80 and for slug is 48"}, + { + "error": "The maximum length for name is 80 and for slug is 48" + }, status=status.HTTP_400_BAD_REQUEST, ) @@ -155,7 +159,9 @@ class WorkSpaceViewSet(BaseViewSet): role=20, company_role=request.data.get("company_role", ""), ) - return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response( + serializer.data, status=status.HTTP_201_CREATED + ) return Response( [serializer.errors[error][0] for error in serializer.errors], status=status.HTTP_400_BAD_REQUEST, @@ -178,7 +184,11 @@ class UserWorkSpacesEndpoint(BaseAPIView): ] def get(self, request): - fields = [field for field in request.GET.get("fields", "").split(",") if field] + fields = [ + field + for field in request.GET.get("fields", "").split(",") + if field + ] member_count = ( WorkspaceMember.objects.filter( workspace=OuterRef("id"), @@ -210,7 +220,8 @@ class UserWorkSpacesEndpoint(BaseAPIView): .annotate(total_members=member_count) .annotate(total_issues=issue_count) .filter( - workspace_member__member=request.user, workspace_member__is_active=True + workspace_member__member=request.user, + workspace_member__is_active=True, ) .distinct() ) @@ -259,7 +270,8 @@ class WorkspaceInvitationsViewset(BaseViewSet): # Check if email is provided if not emails: return Response( - {"error": "Emails are required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Emails are required"}, + status=status.HTTP_400_BAD_REQUEST, ) # check for role level of the requesting user @@ -586,7 +598,9 @@ class WorkSpaceMemberViewSet(BaseViewSet): > requested_workspace_member.role ): return Response( - {"error": "You cannot update a role that is higher than your own role"}, + { + "error": "You cannot update a role that is higher than your own role" + }, status=status.HTTP_400_BAD_REQUEST, ) @@ -625,7 +639,9 @@ class WorkSpaceMemberViewSet(BaseViewSet): if requesting_workspace_member.role < workspace_member.role: return Response( - {"error": "You cannot remove a user having role higher than you"}, + { + "error": "You cannot remove a user having role higher than you" + }, status=status.HTTP_400_BAD_REQUEST, ) @@ -729,11 +745,15 @@ class WorkspaceProjectMemberEndpoint(BaseAPIView): def get(self, request, slug): # Fetch all project IDs where the user is involved - project_ids = ProjectMember.objects.filter( - member=request.user, - member__is_bot=False, - is_active=True, - ).values_list('project_id', flat=True).distinct() + project_ids = ( + ProjectMember.objects.filter( + member=request.user, + member__is_bot=False, + is_active=True, + ) + .values_list("project_id", flat=True) + .distinct() + ) # Get all the project members in which the user is involved project_members = ProjectMember.objects.filter( @@ -742,7 +762,9 @@ class WorkspaceProjectMemberEndpoint(BaseAPIView): project_id__in=project_ids, is_active=True, ).select_related("project", "member", "workspace") - project_members = ProjectMemberRoleSerializer(project_members, many=True).data + project_members = ProjectMemberRoleSerializer( + project_members, many=True + ).data project_members_dict = dict() @@ -790,7 +812,9 @@ class TeamMemberViewSet(BaseViewSet): ) if len(members) != len(request.data.get("members", [])): - users = list(set(request.data.get("members", [])).difference(members)) + users = list( + set(request.data.get("members", [])).difference(members) + ) users = User.objects.filter(pk__in=users) serializer = UserLiteSerializer(users, many=True) @@ -804,7 +828,9 @@ class TeamMemberViewSet(BaseViewSet): workspace = Workspace.objects.get(slug=slug) - serializer = TeamSerializer(data=request.data, context={"workspace": workspace}) + serializer = TeamSerializer( + data=request.data, context={"workspace": workspace} + ) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -833,7 +859,9 @@ class UserLastProjectWithWorkspaceEndpoint(BaseAPIView): workspace_id=last_workspace_id, member=request.user ).select_related("workspace", "project", "member", "workspace__owner") - project_member_serializer = ProjectMemberSerializer(project_member, many=True) + project_member_serializer = ProjectMemberSerializer( + project_member, many=True + ) return Response( { @@ -1017,7 +1045,11 @@ class WorkspaceThemeViewSet(BaseViewSet): serializer_class = WorkspaceThemeSerializer def get_queryset(self): - return super().get_queryset().filter(workspace__slug=self.kwargs.get("slug")) + return ( + super() + .get_queryset() + .filter(workspace__slug=self.kwargs.get("slug")) + ) def create(self, request, slug): workspace = Workspace.objects.get(slug=slug) @@ -1280,12 +1312,22 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): ] def get(self, request, slug, user_id): - fields = [field for field in request.GET.get("fields", "").split(",") if field] + fields = [ + field + for field in request.GET.get("fields", "").split(",") + if field + ] filters = issue_filters(request.query_params, "GET") # Custom ordering for priority and state priority_order = ["urgent", "high", "medium", "low", "none"] - state_order = ["backlog", "unstarted", "started", "completed", "cancelled"] + state_order = [ + "backlog", + "unstarted", + "started", + "completed", + "cancelled", + ] order_by_param = request.GET.get("order_by", "-created_at") issue_queryset = ( @@ -1298,7 +1340,9 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): ) .filter(**filters) .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -1319,7 +1363,9 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -1329,7 +1375,9 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): # Priority Ordering if order_by_param == "priority" or order_by_param == "-priority": priority_order = ( - priority_order if order_by_param == "priority" else priority_order[::-1] + priority_order + if order_by_param == "priority" + else priority_order[::-1] ) issue_queryset = issue_queryset.annotate( priority_order=Case( @@ -1377,7 +1425,9 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): else order_by_param ) ).order_by( - "-max_values" if order_by_param.startswith("-") else "max_values" + "-max_values" + if order_by_param.startswith("-") + else "max_values" ) else: issue_queryset = issue_queryset.order_by(order_by_param) @@ -1397,7 +1447,9 @@ class WorkspaceLabelsEndpoint(BaseAPIView): labels = Label.objects.filter( workspace__slug=slug, project__project_projectmember__member=request.user, - ).values("parent", "name", "color", "id", "project_id", "workspace__slug") + ).values( + "parent", "name", "color", "id", "project_id", "workspace__slug" + ) return Response(labels, status=status.HTTP_200_OK) @@ -1411,18 +1463,27 @@ class WorkspaceUserPropertiesEndpoint(BaseAPIView): user=request.user, workspace__slug=slug, ) - - workspace_properties.filters = request.data.get("filters", workspace_properties.filters) - workspace_properties.display_filters = request.data.get("display_filters", workspace_properties.display_filters) - workspace_properties.display_properties = request.data.get("display_properties", workspace_properties.display_properties) + + workspace_properties.filters = request.data.get( + "filters", workspace_properties.filters + ) + workspace_properties.display_filters = request.data.get( + "display_filters", workspace_properties.display_filters + ) + workspace_properties.display_properties = request.data.get( + "display_properties", workspace_properties.display_properties + ) workspace_properties.save() serializer = WorkspaceUserPropertiesSerializer(workspace_properties) return Response(serializer.data, status=status.HTTP_201_CREATED) def get(self, request, slug): - workspace_properties, _ = WorkspaceUserProperties.objects.get_or_create( + ( + workspace_properties, + _, + ) = WorkspaceUserProperties.objects.get_or_create( user=request.user, workspace__slug=slug ) serializer = WorkspaceUserPropertiesSerializer(workspace_properties) - return Response(serializer.data, status=status.HTTP_200_OK) \ No newline at end of file + return Response(serializer.data, status=status.HTTP_200_OK) diff --git a/apiserver/plane/bgtasks/analytic_plot_export.py b/apiserver/plane/bgtasks/analytic_plot_export.py index a4f5b194c..778956229 100644 --- a/apiserver/plane/bgtasks/analytic_plot_export.py +++ b/apiserver/plane/bgtasks/analytic_plot_export.py @@ -101,7 +101,9 @@ def get_assignee_details(slug, filters): def get_label_details(slug, filters): """Fetch label details if required""" return ( - Issue.objects.filter(workspace__slug=slug, **filters, labels__id__isnull=False) + Issue.objects.filter( + workspace__slug=slug, **filters, labels__id__isnull=False + ) .distinct("labels__id") .order_by("labels__id") .values("labels__id", "labels__color", "labels__name") @@ -174,7 +176,9 @@ def generate_segmented_rows( ): segment_zero = list( set( - item.get("segment") for sublist in distribution.values() for item in sublist + item.get("segment") + for sublist in distribution.values() + for item in sublist ) ) @@ -193,7 +197,9 @@ def generate_segmented_rows( ] for segment in segment_zero: - value = next((x.get(key) for x in data if x.get("segment") == segment), "0") + value = next( + (x.get(key) for x in data if x.get("segment") == segment), "0" + ) generated_row.append(value) if x_axis == ASSIGNEE_ID: @@ -212,7 +218,11 @@ def generate_segmented_rows( if x_axis == LABEL_ID: label = next( - (lab for lab in label_details if str(lab[LABEL_ID]) == str(item)), + ( + lab + for lab in label_details + if str(lab[LABEL_ID]) == str(item) + ), None, ) @@ -221,7 +231,11 @@ def generate_segmented_rows( if x_axis == STATE_ID: state = next( - (sta for sta in state_details if str(sta[STATE_ID]) == str(item)), + ( + sta + for sta in state_details + if str(sta[STATE_ID]) == str(item) + ), None, ) @@ -230,7 +244,11 @@ def generate_segmented_rows( if x_axis == CYCLE_ID: cycle = next( - (cyc for cyc in cycle_details if str(cyc[CYCLE_ID]) == str(item)), + ( + cyc + for cyc in cycle_details + if str(cyc[CYCLE_ID]) == str(item) + ), None, ) @@ -239,7 +257,11 @@ def generate_segmented_rows( if x_axis == MODULE_ID: module = next( - (mod for mod in module_details if str(mod[MODULE_ID]) == str(item)), + ( + mod + for mod in module_details + if str(mod[MODULE_ID]) == str(item) + ), None, ) @@ -266,7 +288,11 @@ def generate_segmented_rows( if segmented == LABEL_ID: for index, segm in enumerate(row_zero[2:]): label = next( - (lab for lab in label_details if str(lab[LABEL_ID]) == str(segm)), + ( + lab + for lab in label_details + if str(lab[LABEL_ID]) == str(segm) + ), None, ) if label: @@ -275,7 +301,11 @@ def generate_segmented_rows( if segmented == STATE_ID: for index, segm in enumerate(row_zero[2:]): state = next( - (sta for sta in state_details if str(sta[STATE_ID]) == str(segm)), + ( + sta + for sta in state_details + if str(sta[STATE_ID]) == str(segm) + ), None, ) if state: @@ -284,7 +314,11 @@ def generate_segmented_rows( if segmented == MODULE_ID: for index, segm in enumerate(row_zero[2:]): module = next( - (mod for mod in label_details if str(mod[MODULE_ID]) == str(segm)), + ( + mod + for mod in label_details + if str(mod[MODULE_ID]) == str(segm) + ), None, ) if module: @@ -293,7 +327,11 @@ def generate_segmented_rows( if segmented == CYCLE_ID: for index, segm in enumerate(row_zero[2:]): cycle = next( - (cyc for cyc in cycle_details if str(cyc[CYCLE_ID]) == str(segm)), + ( + cyc + for cyc in cycle_details + if str(cyc[CYCLE_ID]) == str(segm) + ), None, ) if cycle: @@ -315,7 +353,10 @@ def generate_non_segmented_rows( ): rows = [] for item, data in distribution.items(): - row = [item, data[0].get("count" if y_axis == "issue_count" else "estimate")] + row = [ + item, + data[0].get("count" if y_axis == "issue_count" else "estimate"), + ] if x_axis == ASSIGNEE_ID: assignee = next( @@ -333,7 +374,11 @@ def generate_non_segmented_rows( if x_axis == LABEL_ID: label = next( - (lab for lab in label_details if str(lab[LABEL_ID]) == str(item)), + ( + lab + for lab in label_details + if str(lab[LABEL_ID]) == str(item) + ), None, ) @@ -342,7 +387,11 @@ def generate_non_segmented_rows( if x_axis == STATE_ID: state = next( - (sta for sta in state_details if str(sta[STATE_ID]) == str(item)), + ( + sta + for sta in state_details + if str(sta[STATE_ID]) == str(item) + ), None, ) @@ -351,7 +400,11 @@ def generate_non_segmented_rows( if x_axis == CYCLE_ID: cycle = next( - (cyc for cyc in cycle_details if str(cyc[CYCLE_ID]) == str(item)), + ( + cyc + for cyc in cycle_details + if str(cyc[CYCLE_ID]) == str(item) + ), None, ) @@ -360,7 +413,11 @@ def generate_non_segmented_rows( if x_axis == MODULE_ID: module = next( - (mod for mod in module_details if str(mod[MODULE_ID]) == str(item)), + ( + mod + for mod in module_details + if str(mod[MODULE_ID]) == str(item) + ), None, ) @@ -369,7 +426,10 @@ def generate_non_segmented_rows( rows.append(tuple(row)) - row_zero = [row_mapping.get(x_axis, "X-Axis"), row_mapping.get(y_axis, "Y-Axis")] + row_zero = [ + row_mapping.get(x_axis, "X-Axis"), + row_mapping.get(y_axis, "Y-Axis"), + ] return [tuple(row_zero)] + rows diff --git a/apiserver/plane/bgtasks/apps.py b/apiserver/plane/bgtasks/apps.py index 03d29f3e0..7f6ca38f0 100644 --- a/apiserver/plane/bgtasks/apps.py +++ b/apiserver/plane/bgtasks/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class BgtasksConfig(AppConfig): - name = 'plane.bgtasks' + name = "plane.bgtasks" diff --git a/apiserver/plane/bgtasks/event_tracking_task.py b/apiserver/plane/bgtasks/event_tracking_task.py index 7d26dd4ab..82a8281a9 100644 --- a/apiserver/plane/bgtasks/event_tracking_task.py +++ b/apiserver/plane/bgtasks/event_tracking_task.py @@ -40,22 +40,24 @@ def auth_events(user, email, user_agent, ip, event_name, medium, first_time): email, event=event_name, properties={ - "event_id": uuid.uuid4().hex, - "user": {"email": email, "id": str(user)}, - "device_ctx": { - "ip": ip, - "user_agent": user_agent, - }, - "medium": medium, - "first_time": first_time - } + "event_id": uuid.uuid4().hex, + "user": {"email": email, "id": str(user)}, + "device_ctx": { + "ip": ip, + "user_agent": user_agent, + }, + "medium": medium, + "first_time": first_time, + }, ) except Exception as e: capture_exception(e) - + @shared_task -def workspace_invite_event(user, email, user_agent, ip, event_name, accepted_from): +def workspace_invite_event( + user, email, user_agent, ip, event_name, accepted_from +): try: POSTHOG_API_KEY, POSTHOG_HOST = posthogConfiguration() @@ -65,14 +67,14 @@ def workspace_invite_event(user, email, user_agent, ip, event_name, accepted_fro email, event=event_name, properties={ - "event_id": uuid.uuid4().hex, - "user": {"email": email, "id": str(user)}, - "device_ctx": { - "ip": ip, - "user_agent": user_agent, - }, - "accepted_from": accepted_from - } + "event_id": uuid.uuid4().hex, + "user": {"email": email, "id": str(user)}, + "device_ctx": { + "ip": ip, + "user_agent": user_agent, + }, + "accepted_from": accepted_from, + }, ) except Exception as e: - capture_exception(e) \ No newline at end of file + capture_exception(e) diff --git a/apiserver/plane/bgtasks/export_task.py b/apiserver/plane/bgtasks/export_task.py index e895b859d..f9e6c1ac8 100644 --- a/apiserver/plane/bgtasks/export_task.py +++ b/apiserver/plane/bgtasks/export_task.py @@ -68,7 +68,9 @@ def create_zip_file(files): def upload_to_s3(zip_file, workspace_id, token_id, slug): - file_name = f"{workspace_id}/export-{slug}-{token_id[:6]}-{timezone.now()}.zip" + file_name = ( + f"{workspace_id}/export-{slug}-{token_id[:6]}-{timezone.now()}.zip" + ) expires_in = 7 * 24 * 60 * 60 if settings.USE_MINIO: @@ -87,7 +89,10 @@ def upload_to_s3(zip_file, workspace_id, token_id, slug): ) presigned_url = s3.generate_presigned_url( "get_object", - Params={"Bucket": settings.AWS_STORAGE_BUCKET_NAME, "Key": file_name}, + Params={ + "Bucket": settings.AWS_STORAGE_BUCKET_NAME, + "Key": file_name, + }, ExpiresIn=expires_in, ) # Create the new url with updated domain and protocol @@ -112,7 +117,10 @@ def upload_to_s3(zip_file, workspace_id, token_id, slug): presigned_url = s3.generate_presigned_url( "get_object", - Params={"Bucket": settings.AWS_STORAGE_BUCKET_NAME, "Key": file_name}, + Params={ + "Bucket": settings.AWS_STORAGE_BUCKET_NAME, + "Key": file_name, + }, ExpiresIn=expires_in, ) @@ -172,11 +180,17 @@ def generate_json_row(issue): else "", "Labels": issue["labels__name"], "Cycle Name": issue["issue_cycle__cycle__name"], - "Cycle Start Date": dateConverter(issue["issue_cycle__cycle__start_date"]), + "Cycle Start Date": dateConverter( + issue["issue_cycle__cycle__start_date"] + ), "Cycle End Date": dateConverter(issue["issue_cycle__cycle__end_date"]), "Module Name": issue["issue_module__module__name"], - "Module Start Date": dateConverter(issue["issue_module__module__start_date"]), - "Module Target Date": dateConverter(issue["issue_module__module__target_date"]), + "Module Start Date": dateConverter( + issue["issue_module__module__start_date"] + ), + "Module Target Date": dateConverter( + issue["issue_module__module__target_date"] + ), "Created At": dateTimeConverter(issue["created_at"]), "Updated At": dateTimeConverter(issue["updated_at"]), "Completed At": dateTimeConverter(issue["completed_at"]), @@ -211,7 +225,11 @@ def update_json_row(rows, row): def update_table_row(rows, row): matched_index = next( - (index for index, existing_row in enumerate(rows) if existing_row[0] == row[0]), + ( + index + for index, existing_row in enumerate(rows) + if existing_row[0] == row[0] + ), None, ) @@ -260,7 +278,9 @@ def generate_xlsx(header, project_id, issues, files): @shared_task -def issue_export_task(provider, workspace_id, project_ids, token_id, multiple, slug): +def issue_export_task( + provider, workspace_id, project_ids, token_id, multiple, slug +): try: exporter_instance = ExporterHistory.objects.get(token=token_id) exporter_instance.status = "processing" @@ -273,9 +293,14 @@ def issue_export_task(provider, workspace_id, project_ids, token_id, multiple, s project_id__in=project_ids, project__project_projectmember__member=exporter_instance.initiated_by_id, ) - .select_related("project", "workspace", "state", "parent", "created_by") + .select_related( + "project", "workspace", "state", "parent", "created_by" + ) .prefetch_related( - "assignees", "labels", "issue_cycle__cycle", "issue_module__module" + "assignees", + "labels", + "issue_cycle__cycle", + "issue_module__module", ) .values( "id", diff --git a/apiserver/plane/bgtasks/exporter_expired_task.py b/apiserver/plane/bgtasks/exporter_expired_task.py index 30b638c84..d408c6476 100644 --- a/apiserver/plane/bgtasks/exporter_expired_task.py +++ b/apiserver/plane/bgtasks/exporter_expired_task.py @@ -19,7 +19,8 @@ from plane.db.models import ExporterHistory def delete_old_s3_link(): # Get a list of keys and IDs to process expired_exporter_history = ExporterHistory.objects.filter( - Q(url__isnull=False) & Q(created_at__lte=timezone.now() - timedelta(days=8)) + Q(url__isnull=False) + & Q(created_at__lte=timezone.now() - timedelta(days=8)) ).values_list("key", "id") if settings.USE_MINIO: s3 = boto3.client( @@ -42,8 +43,12 @@ def delete_old_s3_link(): # Delete object from S3 if file_name: if settings.USE_MINIO: - s3.delete_object(Bucket=settings.AWS_STORAGE_BUCKET_NAME, Key=file_name) + s3.delete_object( + Bucket=settings.AWS_STORAGE_BUCKET_NAME, Key=file_name + ) else: - s3.delete_object(Bucket=settings.AWS_STORAGE_BUCKET_NAME, Key=file_name) + s3.delete_object( + Bucket=settings.AWS_STORAGE_BUCKET_NAME, Key=file_name + ) ExporterHistory.objects.filter(id=exporter_id).update(url=None) diff --git a/apiserver/plane/bgtasks/file_asset_task.py b/apiserver/plane/bgtasks/file_asset_task.py index 339d24583..e372355ef 100644 --- a/apiserver/plane/bgtasks/file_asset_task.py +++ b/apiserver/plane/bgtasks/file_asset_task.py @@ -14,10 +14,10 @@ from plane.db.models import FileAsset @shared_task def delete_file_asset(): - # file assets to delete file_assets_to_delete = FileAsset.objects.filter( - Q(is_deleted=True) & Q(updated_at__lte=timezone.now() - timedelta(days=7)) + Q(is_deleted=True) + & Q(updated_at__lte=timezone.now() - timedelta(days=7)) ) # Delete the file from storage and the file object from the database @@ -26,4 +26,3 @@ def delete_file_asset(): file_asset.asset.delete(save=False) # Delete the file object file_asset.delete() - diff --git a/apiserver/plane/bgtasks/forgot_password_task.py b/apiserver/plane/bgtasks/forgot_password_task.py index d790f845d..6c966f342 100644 --- a/apiserver/plane/bgtasks/forgot_password_task.py +++ b/apiserver/plane/bgtasks/forgot_password_task.py @@ -42,7 +42,9 @@ def forgot_password(first_name, email, uidb64, token, current_site): "email": email, } - html_content = render_to_string("emails/auth/forgot_password.html", context) + html_content = render_to_string( + "emails/auth/forgot_password.html", context + ) text_content = strip_tags(html_content) diff --git a/apiserver/plane/bgtasks/importer_task.py b/apiserver/plane/bgtasks/importer_task.py index 84d10ecd3..54a681ddb 100644 --- a/apiserver/plane/bgtasks/importer_task.py +++ b/apiserver/plane/bgtasks/importer_task.py @@ -130,12 +130,17 @@ def service_importer(service, importer_id): repository_id = importer.metadata.get("repository_id", False) workspace_integration = WorkspaceIntegration.objects.get( - workspace_id=importer.workspace_id, integration__provider="github" + workspace_id=importer.workspace_id, + integration__provider="github", ) # Delete the old repository object - GithubRepositorySync.objects.filter(project_id=importer.project_id).delete() - GithubRepository.objects.filter(project_id=importer.project_id).delete() + GithubRepositorySync.objects.filter( + project_id=importer.project_id + ).delete() + GithubRepository.objects.filter( + project_id=importer.project_id + ).delete() # Create a Label for github label = Label.objects.filter( diff --git a/apiserver/plane/bgtasks/issue_activites_task.py b/apiserver/plane/bgtasks/issue_activites_task.py index 2552ffbc5..686f06a20 100644 --- a/apiserver/plane/bgtasks/issue_activites_task.py +++ b/apiserver/plane/bgtasks/issue_activites_task.py @@ -138,8 +138,12 @@ def track_parent( project_id=project_id, workspace_id=workspace_id, comment=f"updated the parent issue to", - old_identifier=old_parent.id if old_parent is not None else None, - new_identifier=new_parent.id if new_parent is not None else None, + old_identifier=old_parent.id + if old_parent is not None + else None, + new_identifier=new_parent.id + if new_parent is not None + else None, epoch=epoch, ) ) @@ -217,7 +221,9 @@ def track_target_date( issue_activities, epoch, ): - if current_instance.get("target_date") != requested_data.get("target_date"): + if current_instance.get("target_date") != requested_data.get( + "target_date" + ): issue_activities.append( IssueActivity( issue_id=issue_id, @@ -281,8 +287,12 @@ def track_labels( issue_activities, epoch, ): - requested_labels = set([str(lab) for lab in requested_data.get("labels", [])]) - current_labels = set([str(lab) for lab in current_instance.get("labels", [])]) + requested_labels = set( + [str(lab) for lab in requested_data.get("labels", [])] + ) + current_labels = set( + [str(lab) for lab in current_instance.get("labels", [])] + ) added_labels = requested_labels - current_labels dropped_labels = current_labels - requested_labels @@ -339,8 +349,12 @@ def track_assignees( issue_activities, epoch, ): - requested_assignees = set([str(asg) for asg in requested_data.get("assignees", [])]) - current_assignees = set([str(asg) for asg in current_instance.get("assignees", [])]) + requested_assignees = set( + [str(asg) for asg in requested_data.get("assignees", [])] + ) + current_assignees = set( + [str(asg) for asg in current_instance.get("assignees", [])] + ) added_assignees = requested_assignees - current_assignees dropped_assginees = current_assignees - requested_assignees @@ -392,7 +406,9 @@ def track_estimate_points( issue_activities, epoch, ): - if current_instance.get("estimate_point") != requested_data.get("estimate_point"): + if current_instance.get("estimate_point") != requested_data.get( + "estimate_point" + ): issue_activities.append( IssueActivity( issue_id=issue_id, @@ -423,7 +439,9 @@ def track_archive_at( issue_activities, epoch, ): - if current_instance.get("archived_at") != requested_data.get("archived_at"): + if current_instance.get("archived_at") != requested_data.get( + "archived_at" + ): if requested_data.get("archived_at") is None: issue_activities.append( IssueActivity( @@ -536,7 +554,9 @@ def update_issue_activity( "closed_to": track_closed_to, } - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -589,7 +609,9 @@ def create_comment_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -621,12 +643,16 @@ def update_comment_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) - if current_instance.get("comment_html") != requested_data.get("comment_html"): + if current_instance.get("comment_html") != requested_data.get( + "comment_html" + ): issue_activities.append( IssueActivity( issue_id=issue_id, @@ -680,14 +706,18 @@ def create_cycle_issue_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) # Updated Records: updated_records = current_instance.get("updated_cycle_issues", []) - created_records = json.loads(current_instance.get("created_cycle_issues", [])) + created_records = json.loads( + current_instance.get("created_cycle_issues", []) + ) for updated_record in updated_records: old_cycle = Cycle.objects.filter( @@ -756,7 +786,9 @@ def delete_cycle_issue_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -798,14 +830,18 @@ def create_module_issue_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) # Updated Records: updated_records = current_instance.get("updated_module_issues", []) - created_records = json.loads(current_instance.get("created_module_issues", [])) + created_records = json.loads( + current_instance.get("created_module_issues", []) + ) for updated_record in updated_records: old_module = Module.objects.filter( @@ -873,7 +909,9 @@ def delete_module_issue_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -915,7 +953,9 @@ def create_link_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -946,7 +986,9 @@ def update_link_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -1010,7 +1052,9 @@ def create_attachment_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -1065,7 +1109,9 @@ def create_issue_reaction_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) if requested_data and requested_data.get("reaction") is not None: issue_reaction = ( IssueReaction.objects.filter( @@ -1137,7 +1183,9 @@ def create_comment_reaction_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) if requested_data and requested_data.get("reaction") is not None: comment_reaction_id, comment_id = ( CommentReaction.objects.filter( @@ -1148,7 +1196,9 @@ def create_comment_reaction_activity( .values_list("id", "comment__id") .first() ) - comment = IssueComment.objects.get(pk=comment_id, project_id=project_id) + comment = IssueComment.objects.get( + pk=comment_id, project_id=project_id + ) if ( comment is not None and comment_reaction_id is not None @@ -1222,7 +1272,9 @@ def create_issue_vote_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) if requested_data and requested_data.get("vote") is not None: issue_activities.append( IssueActivity( @@ -1284,12 +1336,14 @@ def create_issue_relation_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) if current_instance is None and requested_data.get("issues") is not None: - for related_issue in requested_data.get("issues"): + for related_issue in requested_data.get("issues"): issue = Issue.objects.get(pk=related_issue) issue_activities.append( IssueActivity( @@ -1339,7 +1393,9 @@ def delete_issue_relation_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -1382,6 +1438,7 @@ def delete_issue_relation_activity( ) ) + def create_draft_issue_activity( requested_data, current_instance, @@ -1416,7 +1473,9 @@ def update_draft_issue_activity( issue_activities, epoch, ): - requested_data = json.loads(requested_data) if requested_data is not None else None + requested_data = ( + json.loads(requested_data) if requested_data is not None else None + ) current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -1543,7 +1602,9 @@ def issue_activity( ) # Save all the values to database - issue_activities_created = IssueActivity.objects.bulk_create(issue_activities) + issue_activities_created = IssueActivity.objects.bulk_create( + issue_activities + ) # Post the updates to segway for integrations and webhooks if len(issue_activities_created): # Don't send activities if the actor is a bot @@ -1570,7 +1631,9 @@ def issue_activity( project_id=project_id, subscriber=subscriber, issue_activities_created=json.dumps( - IssueActivitySerializer(issue_activities_created, many=True).data, + IssueActivitySerializer( + issue_activities_created, many=True + ).data, cls=DjangoJSONEncoder, ), requested_data=requested_data, diff --git a/apiserver/plane/bgtasks/issue_automation_task.py b/apiserver/plane/bgtasks/issue_automation_task.py index 6a09b08ba..718b60355 100644 --- a/apiserver/plane/bgtasks/issue_automation_task.py +++ b/apiserver/plane/bgtasks/issue_automation_task.py @@ -36,7 +36,9 @@ def archive_old_issues(): Q( project=project_id, archived_at__isnull=True, - updated_at__lte=(timezone.now() - timedelta(days=archive_in * 30)), + updated_at__lte=( + timezone.now() - timedelta(days=archive_in * 30) + ), state__group__in=["completed", "cancelled"], ), Q(issue_cycle__isnull=True) @@ -46,7 +48,9 @@ def archive_old_issues(): ), Q(issue_module__isnull=True) | ( - Q(issue_module__module__target_date__lt=timezone.now().date()) + Q( + issue_module__module__target_date__lt=timezone.now().date() + ) & Q(issue_module__isnull=False) ), ).filter( @@ -74,7 +78,9 @@ def archive_old_issues(): _ = [ issue_activity.delay( type="issue.activity.updated", - requested_data=json.dumps({"archived_at": str(archive_at)}), + requested_data=json.dumps( + {"archived_at": str(archive_at)} + ), actor_id=str(project.created_by_id), issue_id=issue.id, project_id=project_id, @@ -108,7 +114,9 @@ def close_old_issues(): Q( project=project_id, archived_at__isnull=True, - updated_at__lte=(timezone.now() - timedelta(days=close_in * 30)), + updated_at__lte=( + timezone.now() - timedelta(days=close_in * 30) + ), state__group__in=["backlog", "unstarted", "started"], ), Q(issue_cycle__isnull=True) @@ -118,7 +126,9 @@ def close_old_issues(): ), Q(issue_module__isnull=True) | ( - Q(issue_module__module__target_date__lt=timezone.now().date()) + Q( + issue_module__module__target_date__lt=timezone.now().date() + ) & Q(issue_module__isnull=False) ), ).filter( @@ -131,7 +141,9 @@ def close_old_issues(): # Check if Issues if issues: if project.default_state is None: - close_state = State.objects.filter(group="cancelled").first() + close_state = State.objects.filter( + group="cancelled" + ).first() else: close_state = project.default_state @@ -165,4 +177,4 @@ def close_old_issues(): if settings.DEBUG: print(e) capture_exception(e) - return \ No newline at end of file + return diff --git a/apiserver/plane/bgtasks/magic_link_code_task.py b/apiserver/plane/bgtasks/magic_link_code_task.py index bb61e0ada..b94ec4bfe 100644 --- a/apiserver/plane/bgtasks/magic_link_code_task.py +++ b/apiserver/plane/bgtasks/magic_link_code_task.py @@ -33,7 +33,9 @@ def magic_link(email, key, token, current_site): subject = f"Your unique Plane login code is {token}" context = {"code": token, "email": email} - html_content = render_to_string("emails/auth/magic_signin.html", context) + html_content = render_to_string( + "emails/auth/magic_signin.html", context + ) text_content = strip_tags(html_content) connection = get_connection( diff --git a/apiserver/plane/bgtasks/notification_task.py b/apiserver/plane/bgtasks/notification_task.py index d33b883bb..5649ad6b7 100644 --- a/apiserver/plane/bgtasks/notification_task.py +++ b/apiserver/plane/bgtasks/notification_task.py @@ -12,7 +12,7 @@ from plane.db.models import ( Issue, Notification, IssueComment, - IssueActivity + IssueActivity, ) # Third Party imports @@ -20,9 +20,9 @@ from celery import shared_task from bs4 import BeautifulSoup - # =========== Issue Description Html Parsing and Notification Functions ====================== + def update_mentions_for_issue(issue, project, new_mentions, removed_mention): aggregated_issue_mentions = [] @@ -32,14 +32,14 @@ def update_mentions_for_issue(issue, project, new_mentions, removed_mention): mention_id=mention_id, issue=issue, project=project, - workspace_id=project.workspace_id + workspace_id=project.workspace_id, ) ) - IssueMention.objects.bulk_create( - aggregated_issue_mentions, batch_size=100) + IssueMention.objects.bulk_create(aggregated_issue_mentions, batch_size=100) IssueMention.objects.filter( - issue=issue, mention__in=removed_mention).delete() + issue=issue, mention__in=removed_mention + ).delete() def get_new_mentions(requested_instance, current_instance): @@ -48,15 +48,17 @@ def get_new_mentions(requested_instance, current_instance): # extract mentions from both the instance of data mentions_older = extract_mentions(current_instance) - + mentions_newer = extract_mentions(requested_instance) # Getting Set Difference from mentions_newer new_mentions = [ - mention for mention in mentions_newer if mention not in mentions_older] + mention for mention in mentions_newer if mention not in mentions_older + ] return new_mentions + # Get Removed Mention @@ -70,10 +72,12 @@ def get_removed_mentions(requested_instance, current_instance): # Getting Set Difference from mentions_newer removed_mentions = [ - mention for mention in mentions_older if mention not in mentions_newer] + mention for mention in mentions_older if mention not in mentions_newer + ] return removed_mentions + # Adds mentions as subscribers @@ -84,27 +88,34 @@ def extract_mentions_as_subscribers(project_id, issue_id, mentions): for mention_id in mentions: # If the particular mention has not already been subscribed to the issue, he must be sent the mentioned notification - if not IssueSubscriber.objects.filter( - issue_id=issue_id, - subscriber_id=mention_id, - project_id=project_id, - ).exists() and not IssueAssignee.objects.filter( - project_id=project_id, issue_id=issue_id, - assignee_id=mention_id - ).exists() and not Issue.objects.filter( - project_id=project_id, pk=issue_id, created_by_id=mention_id - ).exists(): - - project = Project.objects.get(pk=project_id) - - bulk_mention_subscribers.append(IssueSubscriber( - workspace_id=project.workspace_id, - project_id=project_id, + if ( + not IssueSubscriber.objects.filter( issue_id=issue_id, subscriber_id=mention_id, - )) + project_id=project_id, + ).exists() + and not IssueAssignee.objects.filter( + project_id=project_id, + issue_id=issue_id, + assignee_id=mention_id, + ).exists() + and not Issue.objects.filter( + project_id=project_id, pk=issue_id, created_by_id=mention_id + ).exists() + ): + project = Project.objects.get(pk=project_id) + + bulk_mention_subscribers.append( + IssueSubscriber( + workspace_id=project.workspace_id, + project_id=project_id, + issue_id=issue_id, + subscriber_id=mention_id, + ) + ) return bulk_mention_subscribers + # Parse Issue Description & extracts mentions def extract_mentions(issue_instance): try: @@ -113,46 +124,56 @@ def extract_mentions(issue_instance): # Convert string to dictionary data = json.loads(issue_instance) html = data.get("description_html") - soup = BeautifulSoup(html, 'html.parser') + soup = BeautifulSoup(html, "html.parser") mention_tags = soup.find_all( - 'mention-component', attrs={'target': 'users'}) + "mention-component", attrs={"target": "users"} + ) - mentions = [mention_tag['id'] for mention_tag in mention_tags] + mentions = [mention_tag["id"] for mention_tag in mention_tags] return list(set(mentions)) except Exception as e: return [] - - + + # =========== Comment Parsing and Notification Functions ====================== def extract_comment_mentions(comment_value): try: mentions = [] - soup = BeautifulSoup(comment_value, 'html.parser') + soup = BeautifulSoup(comment_value, "html.parser") mentions_tags = soup.find_all( - 'mention-component', attrs={'target': 'users'} + "mention-component", attrs={"target": "users"} ) for mention_tag in mentions_tags: - mentions.append(mention_tag['id']) + mentions.append(mention_tag["id"]) return list(set(mentions)) except Exception as e: return [] - + + def get_new_comment_mentions(new_value, old_value): - mentions_newer = extract_comment_mentions(new_value) if old_value is None: return mentions_newer - + mentions_older = extract_comment_mentions(old_value) # Getting Set Difference from mentions_newer new_mentions = [ - mention for mention in mentions_newer if mention not in mentions_older] + mention for mention in mentions_newer if mention not in mentions_older + ] return new_mentions -def createMentionNotification(project, notification_comment, issue, actor_id, mention_id, issue_id, activity): +def createMentionNotification( + project, + notification_comment, + issue, + actor_id, + mention_id, + issue_id, + activity, +): return Notification( workspace=project.workspace, sender="in_app:issue_activities:mentioned", @@ -178,16 +199,26 @@ def createMentionNotification(project, notification_comment, issue, actor_id, me "actor": str(activity.get("actor_id")), "new_value": str(activity.get("new_value")), "old_value": str(activity.get("old_value")), - } + }, }, ) @shared_task -def notifications(type, issue_id, project_id, actor_id, subscriber, issue_activities_created, requested_data, current_instance): +def notifications( + type, + issue_id, + project_id, + actor_id, + subscriber, + issue_activities_created, + requested_data, + current_instance, +): issue_activities_created = ( - json.loads( - issue_activities_created) if issue_activities_created is not None else None + json.loads(issue_activities_created) + if issue_activities_created is not None + else None ) if type not in [ "issue.activity.deleted", @@ -216,76 +247,110 @@ def notifications(type, issue_id, project_id, actor_id, subscriber, issue_activi # Get new mentions from the newer instance new_mentions = get_new_mentions( - requested_instance=requested_data, current_instance=current_instance) + requested_instance=requested_data, + current_instance=current_instance, + ) removed_mention = get_removed_mentions( - requested_instance=requested_data, current_instance=current_instance) - + requested_instance=requested_data, + current_instance=current_instance, + ) + comment_mentions = [] all_comment_mentions = [] # Get New Subscribers from the mentions of the newer instance - requested_mentions = extract_mentions( - issue_instance=requested_data) + requested_mentions = extract_mentions(issue_instance=requested_data) mention_subscribers = extract_mentions_as_subscribers( - project_id=project_id, issue_id=issue_id, mentions=requested_mentions) - + project_id=project_id, + issue_id=issue_id, + mentions=requested_mentions, + ) + for issue_activity in issue_activities_created: issue_comment = issue_activity.get("issue_comment") issue_comment_new_value = issue_activity.get("new_value") issue_comment_old_value = issue_activity.get("old_value") if issue_comment is not None: # TODO: Maybe save the comment mentions, so that in future, we can filter out the issues based on comment mentions as well. - - all_comment_mentions = all_comment_mentions + extract_comment_mentions(issue_comment_new_value) - - new_comment_mentions = get_new_comment_mentions(old_value=issue_comment_old_value, new_value=issue_comment_new_value) + + all_comment_mentions = ( + all_comment_mentions + + extract_comment_mentions(issue_comment_new_value) + ) + + new_comment_mentions = get_new_comment_mentions( + old_value=issue_comment_old_value, + new_value=issue_comment_new_value, + ) comment_mentions = comment_mentions + new_comment_mentions - - comment_mention_subscribers = extract_mentions_as_subscribers( project_id=project_id, issue_id=issue_id, mentions=all_comment_mentions) + + comment_mention_subscribers = extract_mentions_as_subscribers( + project_id=project_id, + issue_id=issue_id, + mentions=all_comment_mentions, + ) """ We will not send subscription activity notification to the below mentioned user sets - Those who have been newly mentioned in the issue description, we will send mention notification to them. - When the activity is a comment_created and there exist a mention in the comment, then we have to send the "mention_in_comment" notification - When the activity is a comment_updated and there exist a mention change, then also we have to send the "mention_in_comment" notification """ - + issue_assignees = list( IssueAssignee.objects.filter( - project_id=project_id, issue_id=issue_id) + project_id=project_id, issue_id=issue_id + ) .exclude(assignee_id__in=list(new_mentions + comment_mentions)) .values_list("assignee", flat=True) ) - + issue_subscribers = list( IssueSubscriber.objects.filter( - project_id=project_id, issue_id=issue_id) - .exclude(subscriber_id__in=list(new_mentions + comment_mentions + [actor_id])) + project_id=project_id, issue_id=issue_id + ) + .exclude( + subscriber_id__in=list( + new_mentions + comment_mentions + [actor_id] + ) + ) .values_list("subscriber", flat=True) ) issue = Issue.objects.filter(pk=issue_id).first() - if (issue.created_by_id is not None and str(issue.created_by_id) != str(actor_id)): + if issue.created_by_id is not None and str(issue.created_by_id) != str( + actor_id + ): issue_subscribers = issue_subscribers + [issue.created_by_id] if subscriber: # add the user to issue subscriber try: - if str(issue.created_by_id) != str(actor_id) and uuid.UUID(actor_id) not in issue_assignees: + if ( + str(issue.created_by_id) != str(actor_id) + and uuid.UUID(actor_id) not in issue_assignees + ): _ = IssueSubscriber.objects.get_or_create( - project_id=project_id, issue_id=issue_id, subscriber_id=actor_id + project_id=project_id, + issue_id=issue_id, + subscriber_id=actor_id, ) except Exception as e: pass project = Project.objects.get(pk=project_id) - issue_subscribers = list(set(issue_subscribers + issue_assignees) - {uuid.UUID(actor_id)}) + issue_subscribers = list( + set(issue_subscribers + issue_assignees) - {uuid.UUID(actor_id)} + ) for subscriber in issue_subscribers: if subscriber in issue_subscribers: sender = "in_app:issue_activities:subscribed" - if issue.created_by_id is not None and subscriber == issue.created_by_id: + if ( + issue.created_by_id is not None + and subscriber == issue.created_by_id + ): sender = "in_app:issue_activities:created" if subscriber in issue_assignees: sender = "in_app:issue_activities:assigned" @@ -293,12 +358,16 @@ def notifications(type, issue_id, project_id, actor_id, subscriber, issue_activi for issue_activity in issue_activities_created: # Do not send notification for description update if issue_activity.get("field") == "description": - continue; + continue issue_comment = issue_activity.get("issue_comment") if issue_comment is not None: issue_comment = IssueComment.objects.get( - id=issue_comment, issue_id=issue_id, project_id=project_id, workspace_id=project.workspace_id) - + id=issue_comment, + issue_id=issue_id, + project_id=project_id, + workspace_id=project.workspace_id, + ) + bulk_notifications.append( Notification( workspace=project.workspace, @@ -323,11 +392,16 @@ def notifications(type, issue_id, project_id, actor_id, subscriber, issue_activi "verb": str(issue_activity.get("verb")), "field": str(issue_activity.get("field")), "actor": str(issue_activity.get("actor_id")), - "new_value": str(issue_activity.get("new_value")), - "old_value": str(issue_activity.get("old_value")), + "new_value": str( + issue_activity.get("new_value") + ), + "old_value": str( + issue_activity.get("old_value") + ), "issue_comment": str( issue_comment.comment_stripped - if issue_activity.get("issue_comment") is not None + if issue_activity.get("issue_comment") + is not None else "" ), }, @@ -337,7 +411,8 @@ def notifications(type, issue_id, project_id, actor_id, subscriber, issue_activi # Add Mentioned as Issue Subscribers IssueSubscriber.objects.bulk_create( - mention_subscribers + comment_mention_subscribers, batch_size=100) + mention_subscribers + comment_mention_subscribers, batch_size=100 + ) last_activity = ( IssueActivity.objects.filter(issue_id=issue_id) @@ -346,9 +421,9 @@ def notifications(type, issue_id, project_id, actor_id, subscriber, issue_activi ) actor = User.objects.get(pk=actor_id) - + for mention_id in comment_mentions: - if (mention_id != actor_id): + if mention_id != actor_id: for issue_activity in issue_activities_created: notification = createMentionNotification( project=project, @@ -357,21 +432,20 @@ def notifications(type, issue_id, project_id, actor_id, subscriber, issue_activi actor_id=actor_id, mention_id=mention_id, issue_id=issue_id, - activity=issue_activity + activity=issue_activity, ) bulk_notifications.append(notification) - for mention_id in new_mentions: - if (mention_id != actor_id): + if mention_id != actor_id: if ( last_activity is not None and last_activity.field == "description" and actor_id == str(last_activity.actor_id) ): bulk_notifications.append( - Notification( - workspace=project.workspace, + Notification( + workspace=project.workspace, sender="in_app:issue_activities:mentioned", triggered_by_id=actor_id, receiver_id=mention_id, @@ -383,22 +457,24 @@ def notifications(type, issue_id, project_id, actor_id, subscriber, issue_activi "issue": { "id": str(issue_id), "name": str(issue.name), - "identifier": str(issue.project.identifier), + "identifier": str( + issue.project.identifier + ), "sequence_id": issue.sequence_id, "state_name": issue.state.name, - "state_group": issue.state.group, - }, - "issue_activity": { - "id": str(last_activity.id), - "verb": str(last_activity.verb), - "field": str(last_activity.field), - "actor": str(last_activity.actor_id), - "new_value": str(last_activity.new_value), - "old_value": str(last_activity.old_value), - }, - }, - ) - ) + "state_group": issue.state.group, + }, + "issue_activity": { + "id": str(last_activity.id), + "verb": str(last_activity.verb), + "field": str(last_activity.field), + "actor": str(last_activity.actor_id), + "new_value": str(last_activity.new_value), + "old_value": str(last_activity.old_value), + }, + }, + ) + ) else: for issue_activity in issue_activities_created: notification = createMentionNotification( @@ -408,15 +484,17 @@ def notifications(type, issue_id, project_id, actor_id, subscriber, issue_activi actor_id=actor_id, mention_id=mention_id, issue_id=issue_id, - activity=issue_activity + activity=issue_activity, ) bulk_notifications.append(notification) # save new mentions for the particular issue and remove the mentions that has been deleted from the description - update_mentions_for_issue(issue=issue, project=project, new_mentions=new_mentions, - removed_mention=removed_mention) - + update_mentions_for_issue( + issue=issue, + project=project, + new_mentions=new_mentions, + removed_mention=removed_mention, + ) + # Bulk create notifications Notification.objects.bulk_create(bulk_notifications, batch_size=100) - - diff --git a/apiserver/plane/bgtasks/project_invitation_task.py b/apiserver/plane/bgtasks/project_invitation_task.py index b9221855b..a986de332 100644 --- a/apiserver/plane/bgtasks/project_invitation_task.py +++ b/apiserver/plane/bgtasks/project_invitation_task.py @@ -15,6 +15,7 @@ from sentry_sdk import capture_exception from plane.db.models import Project, User, ProjectMemberInvite from plane.license.utils.instance_value import get_email_configuration + @shared_task def project_invitation(email, project_id, token, current_site, invitor): try: diff --git a/apiserver/plane/bgtasks/webhook_task.py b/apiserver/plane/bgtasks/webhook_task.py index 3681f002d..34bba0cf8 100644 --- a/apiserver/plane/bgtasks/webhook_task.py +++ b/apiserver/plane/bgtasks/webhook_task.py @@ -189,7 +189,8 @@ def send_webhook(event, payload, kw, action, slug, bulk): pk__in=[ str(event.get("issue")) for event in payload ] - ).prefetch_related("issue_cycle", "issue_module"), many=True + ).prefetch_related("issue_cycle", "issue_module"), + many=True, ).data event = "issue" action = "PATCH" @@ -197,7 +198,9 @@ def send_webhook(event, payload, kw, action, slug, bulk): event_data = [ get_model_data( event=event, - event_id=payload.get("id") if isinstance(payload, dict) else None, + event_id=payload.get("id") + if isinstance(payload, dict) + else None, many=False, ) ] diff --git a/apiserver/plane/bgtasks/workspace_invitation_task.py b/apiserver/plane/bgtasks/workspace_invitation_task.py index 7039cb875..a88f1e39c 100644 --- a/apiserver/plane/bgtasks/workspace_invitation_task.py +++ b/apiserver/plane/bgtasks/workspace_invitation_task.py @@ -36,7 +36,6 @@ def workspace_invitation(email, workspace_id, token, current_site, invitor): # The complete url including the domain abs_url = str(current_site) + relative_link - ( EMAIL_HOST, EMAIL_HOST_USER, diff --git a/apiserver/plane/db/management/commands/create_bucket.py b/apiserver/plane/db/management/commands/create_bucket.py index 054523bf9..bdd0b7014 100644 --- a/apiserver/plane/db/management/commands/create_bucket.py +++ b/apiserver/plane/db/management/commands/create_bucket.py @@ -5,7 +5,8 @@ from botocore.exceptions import ClientError # Django imports from django.core.management import BaseCommand -from django.conf import settings +from django.conf import settings + class Command(BaseCommand): help = "Create the default bucket for the instance" @@ -13,23 +14,31 @@ class Command(BaseCommand): def set_bucket_public_policy(self, s3_client, bucket_name): public_policy = { "Version": "2012-10-17", - "Statement": [{ - "Effect": "Allow", - "Principal": "*", - "Action": ["s3:GetObject"], - "Resource": [f"arn:aws:s3:::{bucket_name}/*"] - }] + "Statement": [ + { + "Effect": "Allow", + "Principal": "*", + "Action": ["s3:GetObject"], + "Resource": [f"arn:aws:s3:::{bucket_name}/*"], + } + ], } try: s3_client.put_bucket_policy( - Bucket=bucket_name, - Policy=json.dumps(public_policy) + Bucket=bucket_name, Policy=json.dumps(public_policy) + ) + self.stdout.write( + self.style.SUCCESS( + f"Public read access policy set for bucket '{bucket_name}'." + ) ) - self.stdout.write(self.style.SUCCESS(f"Public read access policy set for bucket '{bucket_name}'.")) except ClientError as e: - self.stdout.write(self.style.ERROR(f"Error setting public read access policy: {e}")) - + self.stdout.write( + self.style.ERROR( + f"Error setting public read access policy: {e}" + ) + ) def handle(self, *args, **options): # Create a session using the credentials from Django settings @@ -39,7 +48,9 @@ class Command(BaseCommand): aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY, ) # Create an S3 client using the session - s3_client = session.client('s3', endpoint_url=settings.AWS_S3_ENDPOINT_URL) + s3_client = session.client( + "s3", endpoint_url=settings.AWS_S3_ENDPOINT_URL + ) bucket_name = settings.AWS_STORAGE_BUCKET_NAME self.stdout.write(self.style.NOTICE("Checking bucket...")) @@ -49,23 +60,41 @@ class Command(BaseCommand): self.set_bucket_public_policy(s3_client, bucket_name) except ClientError as e: - error_code = int(e.response['Error']['Code']) + error_code = int(e.response["Error"]["Code"]) bucket_name = settings.AWS_STORAGE_BUCKET_NAME if error_code == 404: # Bucket does not exist, create it - self.stdout.write(self.style.WARNING(f"Bucket '{bucket_name}' does not exist. Creating bucket...")) + self.stdout.write( + self.style.WARNING( + f"Bucket '{bucket_name}' does not exist. Creating bucket..." + ) + ) try: s3_client.create_bucket(Bucket=bucket_name) - self.stdout.write(self.style.SUCCESS(f"Bucket '{bucket_name}' created successfully.")) + self.stdout.write( + self.style.SUCCESS( + f"Bucket '{bucket_name}' created successfully." + ) + ) self.set_bucket_public_policy(s3_client, bucket_name) except ClientError as create_error: - self.stdout.write(self.style.ERROR(f"Failed to create bucket: {create_error}")) + self.stdout.write( + self.style.ERROR( + f"Failed to create bucket: {create_error}" + ) + ) elif error_code == 403: # Access to the bucket is forbidden - self.stdout.write(self.style.ERROR(f"Access to the bucket '{bucket_name}' is forbidden. Check permissions.")) + self.stdout.write( + self.style.ERROR( + f"Access to the bucket '{bucket_name}' is forbidden. Check permissions." + ) + ) else: # Another ClientError occurred - self.stdout.write(self.style.ERROR(f"Failed to check bucket: {e}")) + self.stdout.write( + self.style.ERROR(f"Failed to check bucket: {e}") + ) except Exception as ex: # Handle any other exception - self.stdout.write(self.style.ERROR(f"An error occurred: {ex}")) \ No newline at end of file + self.stdout.write(self.style.ERROR(f"An error occurred: {ex}")) diff --git a/apiserver/plane/db/management/commands/reset_password.py b/apiserver/plane/db/management/commands/reset_password.py index a5b4c9cc8..d48c24b1c 100644 --- a/apiserver/plane/db/management/commands/reset_password.py +++ b/apiserver/plane/db/management/commands/reset_password.py @@ -35,7 +35,7 @@ class Command(BaseCommand): # get password for the user password = getpass.getpass("Password: ") confirm_password = getpass.getpass("Password (again): ") - + # If the passwords doesn't match raise error if password != confirm_password: self.stderr.write("Error: Your passwords didn't match.") @@ -50,5 +50,7 @@ class Command(BaseCommand): user.set_password(password) user.is_password_autoset = False user.save() - - self.stdout.write(self.style.SUCCESS(f"User password updated succesfully")) + + self.stdout.write( + self.style.SUCCESS(f"User password updated succesfully") + ) diff --git a/apiserver/plane/db/management/commands/wait_for_db.py b/apiserver/plane/db/management/commands/wait_for_db.py index 365452a7a..ec971f83a 100644 --- a/apiserver/plane/db/management/commands/wait_for_db.py +++ b/apiserver/plane/db/management/commands/wait_for_db.py @@ -2,18 +2,19 @@ import time from django.db import connections from django.db.utils import OperationalError from django.core.management import BaseCommand - + + class Command(BaseCommand): """Django command to pause execution until db is available""" - + def handle(self, *args, **options): - self.stdout.write('Waiting for database...') + self.stdout.write("Waiting for database...") db_conn = None while not db_conn: try: - db_conn = connections['default'] + db_conn = connections["default"] except OperationalError: - self.stdout.write('Database unavailable, waititng 1 second...') + self.stdout.write("Database unavailable, waititng 1 second...") time.sleep(1) - - self.stdout.write(self.style.SUCCESS('Database available!')) + + self.stdout.write(self.style.SUCCESS("Database available!")) diff --git a/apiserver/plane/db/migrations/0001_initial.py b/apiserver/plane/db/migrations/0001_initial.py index dd158f0a8..936d33fa5 100644 --- a/apiserver/plane/db/migrations/0001_initial.py +++ b/apiserver/plane/db/migrations/0001_initial.py @@ -10,695 +10,2481 @@ import uuid class Migration(migrations.Migration): - initial = True dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), + ("auth", "0012_alter_user_first_name_max_length"), ] operations = [ migrations.CreateModel( - name='User', + name="User", fields=[ - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('username', models.CharField(max_length=128, unique=True)), - ('mobile_number', models.CharField(blank=True, max_length=255, null=True)), - ('email', models.CharField(blank=True, max_length=255, null=True, unique=True)), - ('first_name', models.CharField(blank=True, max_length=255)), - ('last_name', models.CharField(blank=True, max_length=255)), - ('avatar', models.CharField(blank=True, max_length=255)), - ('date_joined', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('last_location', models.CharField(blank=True, max_length=255)), - ('created_location', models.CharField(blank=True, max_length=255)), - ('is_superuser', models.BooleanField(default=False)), - ('is_managed', models.BooleanField(default=False)), - ('is_password_expired', models.BooleanField(default=False)), - ('is_active', models.BooleanField(default=True)), - ('is_staff', models.BooleanField(default=False)), - ('is_email_verified', models.BooleanField(default=False)), - ('is_password_autoset', models.BooleanField(default=False)), - ('is_onboarded', models.BooleanField(default=False)), - ('token', models.CharField(blank=True, max_length=64)), - ('billing_address_country', models.CharField(default='INDIA', max_length=255)), - ('billing_address', models.JSONField(null=True)), - ('has_billing_address', models.BooleanField(default=False)), - ('user_timezone', models.CharField(default='Asia/Kolkata', max_length=255)), - ('last_active', models.DateTimeField(default=django.utils.timezone.now, null=True)), - ('last_login_time', models.DateTimeField(null=True)), - ('last_logout_time', models.DateTimeField(null=True)), - ('last_login_ip', models.CharField(blank=True, max_length=255)), - ('last_logout_ip', models.CharField(blank=True, max_length=255)), - ('last_login_medium', models.CharField(default='email', max_length=20)), - ('last_login_uagent', models.TextField(blank=True)), - ('token_updated_at', models.DateTimeField(null=True)), - ('last_workspace_id', models.UUIDField(null=True)), - ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), - ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), + ( + "password", + models.CharField(max_length=128, verbose_name="password"), + ), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("username", models.CharField(max_length=128, unique=True)), + ( + "mobile_number", + models.CharField(blank=True, max_length=255, null=True), + ), + ( + "email", + models.CharField( + blank=True, max_length=255, null=True, unique=True + ), + ), + ("first_name", models.CharField(blank=True, max_length=255)), + ("last_name", models.CharField(blank=True, max_length=255)), + ("avatar", models.CharField(blank=True, max_length=255)), + ( + "date_joined", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "last_location", + models.CharField(blank=True, max_length=255), + ), + ( + "created_location", + models.CharField(blank=True, max_length=255), + ), + ("is_superuser", models.BooleanField(default=False)), + ("is_managed", models.BooleanField(default=False)), + ("is_password_expired", models.BooleanField(default=False)), + ("is_active", models.BooleanField(default=True)), + ("is_staff", models.BooleanField(default=False)), + ("is_email_verified", models.BooleanField(default=False)), + ("is_password_autoset", models.BooleanField(default=False)), + ("is_onboarded", models.BooleanField(default=False)), + ("token", models.CharField(blank=True, max_length=64)), + ( + "billing_address_country", + models.CharField(default="INDIA", max_length=255), + ), + ("billing_address", models.JSONField(null=True)), + ("has_billing_address", models.BooleanField(default=False)), + ( + "user_timezone", + models.CharField(default="Asia/Kolkata", max_length=255), + ), + ( + "last_active", + models.DateTimeField( + default=django.utils.timezone.now, null=True + ), + ), + ("last_login_time", models.DateTimeField(null=True)), + ("last_logout_time", models.DateTimeField(null=True)), + ( + "last_login_ip", + models.CharField(blank=True, max_length=255), + ), + ( + "last_logout_ip", + models.CharField(blank=True, max_length=255), + ), + ( + "last_login_medium", + models.CharField(default="email", max_length=20), + ), + ("last_login_uagent", models.TextField(blank=True)), + ("token_updated_at", models.DateTimeField(null=True)), + ("last_workspace_id", models.UUIDField(null=True)), + ( + "groups", + models.ManyToManyField( + blank=True, + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.Group", + verbose_name="groups", + ), + ), + ( + "user_permissions", + models.ManyToManyField( + blank=True, + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.Permission", + verbose_name="user permissions", + ), + ), ], options={ - 'verbose_name': 'User', - 'verbose_name_plural': 'Users', - 'db_table': 'user', - 'ordering': ('-created_at',), + "verbose_name": "User", + "verbose_name_plural": "Users", + "db_table": "user", + "ordering": ("-created_at",), }, managers=[ - ('objects', django.contrib.auth.models.UserManager()), + ("objects", django.contrib.auth.models.UserManager()), ], ), migrations.CreateModel( - name='Cycle', + name="Cycle", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255, verbose_name='Cycle Name')), - ('description', models.TextField(blank=True, verbose_name='Cycle Description')), - ('start_date', models.DateField(verbose_name='Start Date')), - ('end_date', models.DateField(verbose_name='End Date')), - ('status', models.CharField(choices=[('started', 'Started'), ('completed', 'Completed')], max_length=255, verbose_name='Cycle Status')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cycle_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('owned_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='owned_by_cycle', to=settings.AUTH_USER_MODEL)), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "name", + models.CharField( + max_length=255, verbose_name="Cycle Name" + ), + ), + ( + "description", + models.TextField( + blank=True, verbose_name="Cycle Description" + ), + ), + ("start_date", models.DateField(verbose_name="Start Date")), + ("end_date", models.DateField(verbose_name="End Date")), + ( + "status", + models.CharField( + choices=[ + ("started", "Started"), + ("completed", "Completed"), + ], + max_length=255, + verbose_name="Cycle Status", + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cycle_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "owned_by", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="owned_by_cycle", + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'verbose_name': 'Cycle', - 'verbose_name_plural': 'Cycles', - 'db_table': 'cycle', - 'ordering': ('-created_at',), + "verbose_name": "Cycle", + "verbose_name_plural": "Cycles", + "db_table": "cycle", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='Issue', + name="Issue", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255, verbose_name='Issue Name')), - ('description', models.JSONField(blank=True, verbose_name='Issue Description')), - ('priority', models.CharField(blank=True, choices=[('urgent', 'Urgent'), ('high', 'High'), ('medium', 'Medium'), ('low', 'Low')], max_length=30, null=True, verbose_name='Issue Priority')), - ('start_date', models.DateField(blank=True, null=True)), - ('target_date', models.DateField(blank=True, null=True)), - ('sequence_id', models.IntegerField(default=1, verbose_name='Issue Sequence ID')), - ('attachments', django.contrib.postgres.fields.ArrayField(base_field=models.URLField(), blank=True, default=list, size=10)), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "name", + models.CharField( + max_length=255, verbose_name="Issue Name" + ), + ), + ( + "description", + models.JSONField( + blank=True, verbose_name="Issue Description" + ), + ), + ( + "priority", + models.CharField( + blank=True, + choices=[ + ("urgent", "Urgent"), + ("high", "High"), + ("medium", "Medium"), + ("low", "Low"), + ], + max_length=30, + null=True, + verbose_name="Issue Priority", + ), + ), + ("start_date", models.DateField(blank=True, null=True)), + ("target_date", models.DateField(blank=True, null=True)), + ( + "sequence_id", + models.IntegerField( + default=1, verbose_name="Issue Sequence ID" + ), + ), + ( + "attachments", + django.contrib.postgres.fields.ArrayField( + base_field=models.URLField(), + blank=True, + default=list, + size=10, + ), + ), ], options={ - 'verbose_name': 'Issue', - 'verbose_name_plural': 'Issues', - 'db_table': 'issue', - 'ordering': ('-created_at',), + "verbose_name": "Issue", + "verbose_name_plural": "Issues", + "db_table": "issue", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='Project', + name="Project", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255, verbose_name='Project Name')), - ('description', models.TextField(blank=True, verbose_name='Project Description')), - ('description_rt', models.JSONField(blank=True, null=True, verbose_name='Project Description RT')), - ('description_html', models.JSONField(blank=True, null=True, verbose_name='Project Description HTML')), - ('network', models.PositiveSmallIntegerField(choices=[(0, 'Secret'), (2, 'Public')], default=2)), - ('identifier', models.CharField(blank=True, max_length=5, null=True, verbose_name='Project Identifier')), - ('slug', models.SlugField(blank=True, max_length=100)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='project_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('default_assignee', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='default_assignee', to=settings.AUTH_USER_MODEL)), - ('project_lead', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='project_lead', to=settings.AUTH_USER_MODEL)), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='project_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "name", + models.CharField( + max_length=255, verbose_name="Project Name" + ), + ), + ( + "description", + models.TextField( + blank=True, verbose_name="Project Description" + ), + ), + ( + "description_rt", + models.JSONField( + blank=True, + null=True, + verbose_name="Project Description RT", + ), + ), + ( + "description_html", + models.JSONField( + blank=True, + null=True, + verbose_name="Project Description HTML", + ), + ), + ( + "network", + models.PositiveSmallIntegerField( + choices=[(0, "Secret"), (2, "Public")], default=2 + ), + ), + ( + "identifier", + models.CharField( + blank=True, + max_length=5, + null=True, + verbose_name="Project Identifier", + ), + ), + ("slug", models.SlugField(blank=True, max_length=100)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="project_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "default_assignee", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="default_assignee", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "project_lead", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="project_lead", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="project_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), ], options={ - 'verbose_name': 'Project', - 'verbose_name_plural': 'Projects', - 'db_table': 'project', - 'ordering': ('-created_at',), + "verbose_name": "Project", + "verbose_name_plural": "Projects", + "db_table": "project", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='Team', + name="Team", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255, verbose_name='Team Name')), - ('description', models.TextField(blank=True, verbose_name='Team Description')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "name", + models.CharField(max_length=255, verbose_name="Team Name"), + ), + ( + "description", + models.TextField( + blank=True, verbose_name="Team Description" + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="team_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), ], options={ - 'verbose_name': 'Team', - 'verbose_name_plural': 'Teams', - 'db_table': 'team', - 'ordering': ('-created_at',), + "verbose_name": "Team", + "verbose_name_plural": "Teams", + "db_table": "team", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='Workspace', + name="Workspace", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255, verbose_name='Workspace Name')), - ('logo', models.URLField(blank=True, null=True, verbose_name='Logo')), - ('slug', models.SlugField(max_length=100, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workspace_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='owner_workspace', to=settings.AUTH_USER_MODEL)), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workspace_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "name", + models.CharField( + max_length=255, verbose_name="Workspace Name" + ), + ), + ( + "logo", + models.URLField( + blank=True, null=True, verbose_name="Logo" + ), + ), + ("slug", models.SlugField(max_length=100, unique=True)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="workspace_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "owner", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="owner_workspace", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="workspace_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), ], options={ - 'verbose_name': 'Workspace', - 'verbose_name_plural': 'Workspaces', - 'db_table': 'workspace', - 'ordering': ('-created_at',), - 'unique_together': {('name', 'owner')}, + "verbose_name": "Workspace", + "verbose_name_plural": "Workspaces", + "db_table": "workspace", + "ordering": ("-created_at",), + "unique_together": {("name", "owner")}, }, ), migrations.CreateModel( - name='WorkspaceMemberInvite', + name="WorkspaceMemberInvite", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('email', models.CharField(max_length=255)), - ('accepted', models.BooleanField(default=False)), - ('token', models.CharField(max_length=255)), - ('message', models.TextField(null=True)), - ('responded_at', models.DateTimeField(null=True)), - ('role', models.PositiveSmallIntegerField(choices=[(20, 'Owner'), (15, 'Admin'), (10, 'Member'), (5, 'Guest')], default=10)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workspacememberinvite_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workspacememberinvite_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_member_invite', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("email", models.CharField(max_length=255)), + ("accepted", models.BooleanField(default=False)), + ("token", models.CharField(max_length=255)), + ("message", models.TextField(null=True)), + ("responded_at", models.DateTimeField(null=True)), + ( + "role", + models.PositiveSmallIntegerField( + choices=[ + (20, "Owner"), + (15, "Admin"), + (10, "Member"), + (5, "Guest"), + ], + default=10, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="workspacememberinvite_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="workspacememberinvite_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_member_invite", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Workspace Member Invite', - 'verbose_name_plural': 'Workspace Member Invites', - 'db_table': 'workspace_member_invite', - 'ordering': ('-created_at',), + "verbose_name": "Workspace Member Invite", + "verbose_name_plural": "Workspace Member Invites", + "db_table": "workspace_member_invite", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='View', + name="View", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255, verbose_name='View Name')), - ('description', models.TextField(blank=True, verbose_name='View Description')), - ('query', models.JSONField(verbose_name='View Query')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='view_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_view', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='view_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_view', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "name", + models.CharField(max_length=255, verbose_name="View Name"), + ), + ( + "description", + models.TextField( + blank=True, verbose_name="View Description" + ), + ), + ("query", models.JSONField(verbose_name="View Query")), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="view_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_view", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="view_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_view", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'View', - 'verbose_name_plural': 'Views', - 'db_table': 'view', - 'ordering': ('-created_at',), + "verbose_name": "View", + "verbose_name_plural": "Views", + "db_table": "view", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='TimelineIssue', + name="TimelineIssue", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('sequence_id', models.FloatField(default=1.0)), - ('links', models.JSONField(blank=True, default=dict)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='timelineissue_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_timeline', to='db.issue')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_timelineissue', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='timelineissue_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_timelineissue', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("sequence_id", models.FloatField(default=1.0)), + ("links", models.JSONField(blank=True, default=dict)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="timelineissue_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_timeline", + to="db.issue", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_timelineissue", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="timelineissue_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_timelineissue", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Timeline Issue', - 'verbose_name_plural': 'Timeline Issues', - 'db_table': 'issue_timeline', - 'ordering': ('-created_at',), + "verbose_name": "Timeline Issue", + "verbose_name_plural": "Timeline Issues", + "db_table": "issue_timeline", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='TeamMember', + name="TeamMember", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='teammember_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='team_member', to=settings.AUTH_USER_MODEL)), - ('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='team_member', to='db.team')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='teammember_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='team_member', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="teammember_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "member", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="team_member", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "team", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="team_member", + to="db.team", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="teammember_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="team_member", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Team Member', - 'verbose_name_plural': 'Team Members', - 'db_table': 'team_member', - 'ordering': ('-created_at',), - 'unique_together': {('team', 'member')}, + "verbose_name": "Team Member", + "verbose_name_plural": "Team Members", + "db_table": "team_member", + "ordering": ("-created_at",), + "unique_together": {("team", "member")}, }, ), migrations.AddField( - model_name='team', - name='members', - field=models.ManyToManyField(blank=True, related_name='members', through='db.TeamMember', to=settings.AUTH_USER_MODEL), + model_name="team", + name="members", + field=models.ManyToManyField( + blank=True, + related_name="members", + through="db.TeamMember", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='team', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), + model_name="team", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="team_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), ), migrations.AddField( - model_name='team', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_team', to='db.workspace'), + model_name="team", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_team", + to="db.workspace", + ), ), migrations.CreateModel( - name='State', + name="State", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255, verbose_name='State Name')), - ('description', models.TextField(blank=True, verbose_name='State Description')), - ('color', models.CharField(max_length=255, verbose_name='State Color')), - ('slug', models.SlugField(blank=True, max_length=100)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='state_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_state', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='state_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_state', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "name", + models.CharField( + max_length=255, verbose_name="State Name" + ), + ), + ( + "description", + models.TextField( + blank=True, verbose_name="State Description" + ), + ), + ( + "color", + models.CharField( + max_length=255, verbose_name="State Color" + ), + ), + ("slug", models.SlugField(blank=True, max_length=100)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="state_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_state", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="state_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_state", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'State', - 'verbose_name_plural': 'States', - 'db_table': 'state', - 'ordering': ('-created_at',), - 'unique_together': {('name', 'project')}, + "verbose_name": "State", + "verbose_name_plural": "States", + "db_table": "state", + "ordering": ("-created_at",), + "unique_together": {("name", "project")}, }, ), migrations.CreateModel( - name='SocialLoginConnection', + name="SocialLoginConnection", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('medium', models.CharField(choices=[('Google', 'google'), ('Github', 'github')], default=None, max_length=20)), - ('last_login_at', models.DateTimeField(default=django.utils.timezone.now, null=True)), - ('last_received_at', models.DateTimeField(default=django.utils.timezone.now, null=True)), - ('token_data', models.JSONField(null=True)), - ('extra_data', models.JSONField(null=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='socialloginconnection_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='socialloginconnection_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_login_connections', to=settings.AUTH_USER_MODEL)), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "medium", + models.CharField( + choices=[("Google", "google"), ("Github", "github")], + default=None, + max_length=20, + ), + ), + ( + "last_login_at", + models.DateTimeField( + default=django.utils.timezone.now, null=True + ), + ), + ( + "last_received_at", + models.DateTimeField( + default=django.utils.timezone.now, null=True + ), + ), + ("token_data", models.JSONField(null=True)), + ("extra_data", models.JSONField(null=True)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="socialloginconnection_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="socialloginconnection_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="user_login_connections", + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'verbose_name': 'Social Login Connection', - 'verbose_name_plural': 'Social Login Connections', - 'db_table': 'social_login_connection', - 'ordering': ('-created_at',), + "verbose_name": "Social Login Connection", + "verbose_name_plural": "Social Login Connections", + "db_table": "social_login_connection", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='Shortcut', + name="Shortcut", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255, verbose_name='Cycle Name')), - ('description', models.TextField(blank=True, verbose_name='Cycle Description')), - ('type', models.CharField(choices=[('repo', 'Repo'), ('direct', 'Direct')], max_length=255, verbose_name='Shortcut Type')), - ('url', models.URLField(blank=True, null=True, verbose_name='URL')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='shortcut_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_shortcut', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='shortcut_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_shortcut', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "name", + models.CharField( + max_length=255, verbose_name="Cycle Name" + ), + ), + ( + "description", + models.TextField( + blank=True, verbose_name="Cycle Description" + ), + ), + ( + "type", + models.CharField( + choices=[("repo", "Repo"), ("direct", "Direct")], + max_length=255, + verbose_name="Shortcut Type", + ), + ), + ( + "url", + models.URLField(blank=True, null=True, verbose_name="URL"), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="shortcut_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_shortcut", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="shortcut_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_shortcut", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Shortcut', - 'verbose_name_plural': 'Shortcuts', - 'db_table': 'shortcut', - 'ordering': ('-created_at',), + "verbose_name": "Shortcut", + "verbose_name_plural": "Shortcuts", + "db_table": "shortcut", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='ProjectMemberInvite', + name="ProjectMemberInvite", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('email', models.CharField(max_length=255)), - ('accepted', models.BooleanField(default=False)), - ('token', models.CharField(max_length=255)), - ('message', models.TextField(null=True)), - ('responded_at', models.DateTimeField(null=True)), - ('role', models.PositiveSmallIntegerField(choices=[(20, 'Admin'), (15, 'Member'), (10, 'Viewer'), (5, 'Guest')], default=10)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projectmemberinvite_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_projectmemberinvite', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projectmemberinvite_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_projectmemberinvite', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("email", models.CharField(max_length=255)), + ("accepted", models.BooleanField(default=False)), + ("token", models.CharField(max_length=255)), + ("message", models.TextField(null=True)), + ("responded_at", models.DateTimeField(null=True)), + ( + "role", + models.PositiveSmallIntegerField( + choices=[ + (20, "Admin"), + (15, "Member"), + (10, "Viewer"), + (5, "Guest"), + ], + default=10, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="projectmemberinvite_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_projectmemberinvite", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="projectmemberinvite_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_projectmemberinvite", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Project Member Invite', - 'verbose_name_plural': 'Project Member Invites', - 'db_table': 'project_member_invite', - 'ordering': ('-created_at',), + "verbose_name": "Project Member Invite", + "verbose_name_plural": "Project Member Invites", + "db_table": "project_member_invite", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='ProjectIdentifier', + name="ProjectIdentifier", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('name', models.CharField(max_length=10)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projectidentifier_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='project_identifier', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projectidentifier_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ("name", models.CharField(max_length=10)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="projectidentifier_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_identifier", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="projectidentifier_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), ], options={ - 'verbose_name': 'Project Identifier', - 'verbose_name_plural': 'Project Identifiers', - 'db_table': 'project_identifier', - 'ordering': ('-created_at',), + "verbose_name": "Project Identifier", + "verbose_name_plural": "Project Identifiers", + "db_table": "project_identifier", + "ordering": ("-created_at",), }, ), migrations.AddField( - model_name='project', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_project', to='db.workspace'), + model_name="project", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_project", + to="db.workspace", + ), ), migrations.CreateModel( - name='Label', + name="Label", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255)), - ('description', models.TextField(blank=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='label_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_label', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='label_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_label', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("name", models.CharField(max_length=255)), + ("description", models.TextField(blank=True)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="label_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_label", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="label_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_label", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Label', - 'verbose_name_plural': 'Labels', - 'db_table': 'label', - 'ordering': ('-created_at',), + "verbose_name": "Label", + "verbose_name_plural": "Labels", + "db_table": "label", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='IssueSequence', + name="IssueSequence", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('sequence', models.PositiveBigIntegerField(default=1)), - ('deleted', models.BooleanField(default=False)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issuesequence_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issue_sequence', to='db.issue')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_issuesequence', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issuesequence_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_issuesequence', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("sequence", models.PositiveBigIntegerField(default=1)), + ("deleted", models.BooleanField(default=False)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issuesequence_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issue_sequence", + to="db.issue", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_issuesequence", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issuesequence_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_issuesequence", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Sequence', - 'verbose_name_plural': 'Issue Sequences', - 'db_table': 'issue_sequence', - 'ordering': ('-created_at',), + "verbose_name": "Issue Sequence", + "verbose_name_plural": "Issue Sequences", + "db_table": "issue_sequence", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='IssueProperty', + name="IssueProperty", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('properties', models.JSONField(default=dict)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueproperty_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_issueproperty', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueproperty_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='issue_property_user', to=settings.AUTH_USER_MODEL)), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_issueproperty', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("properties", models.JSONField(default=dict)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueproperty_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_issueproperty", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueproperty_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_property_user", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_issueproperty", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Property', - 'verbose_name_plural': 'Issue Properties', - 'db_table': 'issue_property', - 'ordering': ('-created_at',), + "verbose_name": "Issue Property", + "verbose_name_plural": "Issue Properties", + "db_table": "issue_property", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='IssueLabel', + name="IssueLabel", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issuelabel_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='label_issue', to='db.issue')), - ('label', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='label_issue', to='db.label')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_issuelabel', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issuelabel_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_issuelabel', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issuelabel_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="label_issue", + to="db.issue", + ), + ), + ( + "label", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="label_issue", + to="db.label", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_issuelabel", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issuelabel_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_issuelabel", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Label', - 'verbose_name_plural': 'Issue Labels', - 'db_table': 'issue_label', - 'ordering': ('-created_at',), + "verbose_name": "Issue Label", + "verbose_name_plural": "Issue Labels", + "db_table": "issue_label", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='IssueComment', + name="IssueComment", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('comment', models.TextField(blank=True, verbose_name='Comment')), - ('attachments', django.contrib.postgres.fields.ArrayField(base_field=models.URLField(), blank=True, default=list, size=10)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issuecomment_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='db.issue')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_issuecomment', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issuecomment_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_issuecomment', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "comment", + models.TextField(blank=True, verbose_name="Comment"), + ), + ( + "attachments", + django.contrib.postgres.fields.ArrayField( + base_field=models.URLField(), + blank=True, + default=list, + size=10, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issuecomment_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="db.issue", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_issuecomment", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issuecomment_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_issuecomment", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Comment', - 'verbose_name_plural': 'Issue Comments', - 'db_table': 'issue_comment', - 'ordering': ('-created_at',), + "verbose_name": "Issue Comment", + "verbose_name_plural": "Issue Comments", + "db_table": "issue_comment", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='IssueBlocker', + name="IssueBlocker", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('block', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='blocker_issues', to='db.issue')), - ('blocked_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='blocked_issues', to='db.issue')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueblocker_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_issueblocker', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueblocker_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_issueblocker', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "block", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="blocker_issues", + to="db.issue", + ), + ), + ( + "blocked_by", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="blocked_issues", + to="db.issue", + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueblocker_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_issueblocker", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueblocker_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_issueblocker", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Blocker', - 'verbose_name_plural': 'Issue Blockers', - 'db_table': 'issue_blocker', - 'ordering': ('-created_at',), + "verbose_name": "Issue Blocker", + "verbose_name_plural": "Issue Blockers", + "db_table": "issue_blocker", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='IssueAssignee', + name="IssueAssignee", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('assignee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_assignee', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueassignee_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_assignee', to='db.issue')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_issueassignee', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueassignee_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_issueassignee', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "assignee", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_assignee", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueassignee_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_assignee", + to="db.issue", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_issueassignee", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueassignee_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_issueassignee", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Assignee', - 'verbose_name_plural': 'Issue Assignees', - 'db_table': 'issue_assignee', - 'ordering': ('-created_at',), - 'unique_together': {('issue', 'assignee')}, + "verbose_name": "Issue Assignee", + "verbose_name_plural": "Issue Assignees", + "db_table": "issue_assignee", + "ordering": ("-created_at",), + "unique_together": {("issue", "assignee")}, }, ), migrations.CreateModel( - name='IssueActivity', + name="IssueActivity", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('verb', models.CharField(default='created', max_length=255, verbose_name='Action')), - ('field', models.CharField(blank=True, max_length=255, null=True, verbose_name='Field Name')), - ('old_value', models.CharField(blank=True, max_length=255, null=True, verbose_name='Old Value')), - ('new_value', models.CharField(blank=True, max_length=255, null=True, verbose_name='New Value')), - ('comment', models.TextField(blank=True, verbose_name='Comment')), - ('attachments', django.contrib.postgres.fields.ArrayField(base_field=models.URLField(), blank=True, default=list, size=10)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueactivity_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_activity', to='db.issue')), - ('issue_comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issue_comment', to='db.issuecomment')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_issueactivity', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueactivity_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_issueactivity', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "verb", + models.CharField( + default="created", + max_length=255, + verbose_name="Action", + ), + ), + ( + "field", + models.CharField( + blank=True, + max_length=255, + null=True, + verbose_name="Field Name", + ), + ), + ( + "old_value", + models.CharField( + blank=True, + max_length=255, + null=True, + verbose_name="Old Value", + ), + ), + ( + "new_value", + models.CharField( + blank=True, + max_length=255, + null=True, + verbose_name="New Value", + ), + ), + ( + "comment", + models.TextField(blank=True, verbose_name="Comment"), + ), + ( + "attachments", + django.contrib.postgres.fields.ArrayField( + base_field=models.URLField(), + blank=True, + default=list, + size=10, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueactivity_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_activity", + to="db.issue", + ), + ), + ( + "issue_comment", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issue_comment", + to="db.issuecomment", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_issueactivity", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueactivity_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_issueactivity", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Activity', - 'verbose_name_plural': 'Issue Activities', - 'db_table': 'issue_activity', - 'ordering': ('-created_at',), + "verbose_name": "Issue Activity", + "verbose_name_plural": "Issue Activities", + "db_table": "issue_activity", + "ordering": ("-created_at",), }, ), migrations.AddField( - model_name='issue', - name='assignees', - field=models.ManyToManyField(blank=True, related_name='assignee', through='db.IssueAssignee', to=settings.AUTH_USER_MODEL), + model_name="issue", + name="assignees", + field=models.ManyToManyField( + blank=True, + related_name="assignee", + through="db.IssueAssignee", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='issue', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issue_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), + model_name="issue", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issue_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), ), migrations.AddField( - model_name='issue', - name='labels', - field=models.ManyToManyField(blank=True, related_name='labels', through='db.IssueLabel', to='db.Label'), + model_name="issue", + name="labels", + field=models.ManyToManyField( + blank=True, + related_name="labels", + through="db.IssueLabel", + to="db.Label", + ), ), migrations.AddField( - model_name='issue', - name='parent', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='parent_issue', to='db.issue'), + model_name="issue", + name="parent", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="parent_issue", + to="db.issue", + ), ), migrations.AddField( - model_name='issue', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_issue', to='db.project'), + model_name="issue", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_issue", + to="db.project", + ), ), migrations.AddField( - model_name='issue', - name='state', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='state_issue', to='db.state'), + model_name="issue", + name="state", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="state_issue", + to="db.state", + ), ), migrations.AddField( - model_name='issue', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issue_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), + model_name="issue", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issue_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), ), migrations.AddField( - model_name='issue', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_issue', to='db.workspace'), + model_name="issue", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_issue", + to="db.workspace", + ), ), migrations.CreateModel( - name='FileAsset', + name="FileAsset", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('attributes', models.JSONField(default=dict)), - ('asset', models.FileField(upload_to='library-assets')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='fileasset_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='fileasset_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("attributes", models.JSONField(default=dict)), + ("asset", models.FileField(upload_to="library-assets")), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="fileasset_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="fileasset_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), ], options={ - 'verbose_name': 'File Asset', - 'verbose_name_plural': 'File Assets', - 'db_table': 'file_asset', - 'ordering': ('-created_at',), + "verbose_name": "File Asset", + "verbose_name_plural": "File Assets", + "db_table": "file_asset", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='CycleIssue', + name="CycleIssue", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cycleissue_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('cycle', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_cycle', to='db.cycle')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_cycle', to='db.issue')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_cycleissue', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cycleissue_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_cycleissue', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cycleissue_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "cycle", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_cycle", + to="db.cycle", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_cycle", + to="db.issue", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_cycleissue", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cycleissue_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_cycleissue", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Cycle Issue', - 'verbose_name_plural': 'Cycle Issues', - 'db_table': 'cycle_issue', - 'ordering': ('-created_at',), + "verbose_name": "Cycle Issue", + "verbose_name_plural": "Cycle Issues", + "db_table": "cycle_issue", + "ordering": ("-created_at",), }, ), migrations.AddField( - model_name='cycle', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_cycle', to='db.project'), + model_name="cycle", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_cycle", + to="db.project", + ), ), migrations.AddField( - model_name='cycle', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cycle_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), + model_name="cycle", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cycle_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), ), migrations.AddField( - model_name='cycle', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_cycle', to='db.workspace'), + model_name="cycle", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_cycle", + to="db.workspace", + ), ), migrations.CreateModel( - name='WorkspaceMember', + name="WorkspaceMember", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('role', models.PositiveSmallIntegerField(choices=[(20, 'Owner'), (15, 'Admin'), (10, 'Member'), (5, 'Guest')], default=10)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workspacemember_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='member_workspace', to=settings.AUTH_USER_MODEL)), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workspacemember_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_member', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "role", + models.PositiveSmallIntegerField( + choices=[ + (20, "Owner"), + (15, "Admin"), + (10, "Member"), + (5, "Guest"), + ], + default=10, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="workspacemember_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "member", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="member_workspace", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="workspacemember_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_member", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Workspace Member', - 'verbose_name_plural': 'Workspace Members', - 'db_table': 'workspace_member', - 'ordering': ('-created_at',), - 'unique_together': {('workspace', 'member')}, + "verbose_name": "Workspace Member", + "verbose_name_plural": "Workspace Members", + "db_table": "workspace_member", + "ordering": ("-created_at",), + "unique_together": {("workspace", "member")}, }, ), migrations.AlterUniqueTogether( - name='team', - unique_together={('name', 'workspace')}, + name="team", + unique_together={("name", "workspace")}, ), migrations.CreateModel( - name='ProjectMember', + name="ProjectMember", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('comment', models.TextField(blank=True, null=True)), - ('role', models.PositiveSmallIntegerField(choices=[(20, 'Admin'), (15, 'Member'), (10, 'Viewer'), (5, 'Guest')], default=10)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projectmember_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('member', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='member_project', to=settings.AUTH_USER_MODEL)), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_projectmember', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projectmember_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_projectmember', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("comment", models.TextField(blank=True, null=True)), + ( + "role", + models.PositiveSmallIntegerField( + choices=[ + (20, "Admin"), + (15, "Member"), + (10, "Viewer"), + (5, "Guest"), + ], + default=10, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="projectmember_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "member", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="member_project", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_projectmember", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="projectmember_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_projectmember", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Project Member', - 'verbose_name_plural': 'Project Members', - 'db_table': 'project_member', - 'ordering': ('-created_at',), - 'unique_together': {('project', 'member')}, + "verbose_name": "Project Member", + "verbose_name_plural": "Project Members", + "db_table": "project_member", + "ordering": ("-created_at",), + "unique_together": {("project", "member")}, }, ), migrations.AlterUniqueTogether( - name='project', - unique_together={('name', 'workspace')}, + name="project", + unique_together={("name", "workspace")}, ), ] diff --git a/apiserver/plane/db/migrations/0002_auto_20221104_2239.py b/apiserver/plane/db/migrations/0002_auto_20221104_2239.py index 9c25c4518..d69ef1a71 100644 --- a/apiserver/plane/db/migrations/0002_auto_20221104_2239.py +++ b/apiserver/plane/db/migrations/0002_auto_20221104_2239.py @@ -6,49 +6,66 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('db', '0001_initial'), + ("db", "0001_initial"), ] operations = [ migrations.AlterModelOptions( - name='state', - options={'ordering': ('sequence',), 'verbose_name': 'State', 'verbose_name_plural': 'States'}, + name="state", + options={ + "ordering": ("sequence",), + "verbose_name": "State", + "verbose_name_plural": "States", + }, ), migrations.RenameField( - model_name='project', - old_name='description_rt', - new_name='description_text', + model_name="project", + old_name="description_rt", + new_name="description_text", ), migrations.AddField( - model_name='issueactivity', - name='actor', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issue_activities', to=settings.AUTH_USER_MODEL), + model_name="issueactivity", + name="actor", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issue_activities", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='issuecomment', - name='actor', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='comments', to=settings.AUTH_USER_MODEL), + model_name="issuecomment", + name="actor", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="comments", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='state', - name='sequence', + model_name="state", + name="sequence", field=models.PositiveIntegerField(default=65535), ), migrations.AddField( - model_name='workspace', - name='company_size', + model_name="workspace", + name="company_size", field=models.PositiveIntegerField(default=10), ), migrations.AddField( - model_name='workspacemember', - name='company_role', + model_name="workspacemember", + name="company_role", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='cycleissue', - name='issue', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='issue_cycle', to='db.issue'), + model_name="cycleissue", + name="issue", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_cycle", + to="db.issue", + ), ), ] diff --git a/apiserver/plane/db/migrations/0003_auto_20221109_2320.py b/apiserver/plane/db/migrations/0003_auto_20221109_2320.py index 3adac35a7..763d52eb6 100644 --- a/apiserver/plane/db/migrations/0003_auto_20221109_2320.py +++ b/apiserver/plane/db/migrations/0003_auto_20221109_2320.py @@ -6,19 +6,22 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('db', '0002_auto_20221104_2239'), + ("db", "0002_auto_20221104_2239"), ] operations = [ migrations.AlterField( - model_name='issueproperty', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_property_user', to=settings.AUTH_USER_MODEL), + model_name="issueproperty", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_property_user", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterUniqueTogether( - name='issueproperty', - unique_together={('user', 'project')}, + name="issueproperty", + unique_together={("user", "project")}, ), ] diff --git a/apiserver/plane/db/migrations/0004_alter_state_sequence.py b/apiserver/plane/db/migrations/0004_alter_state_sequence.py index 0d4616aea..f3489449c 100644 --- a/apiserver/plane/db/migrations/0004_alter_state_sequence.py +++ b/apiserver/plane/db/migrations/0004_alter_state_sequence.py @@ -4,15 +4,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('db', '0003_auto_20221109_2320'), + ("db", "0003_auto_20221109_2320"), ] operations = [ migrations.AlterField( - model_name='state', - name='sequence', + model_name="state", + name="sequence", field=models.FloatField(default=65535), ), ] diff --git a/apiserver/plane/db/migrations/0005_auto_20221114_2127.py b/apiserver/plane/db/migrations/0005_auto_20221114_2127.py index 14c280e26..8ab63a22a 100644 --- a/apiserver/plane/db/migrations/0005_auto_20221114_2127.py +++ b/apiserver/plane/db/migrations/0005_auto_20221114_2127.py @@ -4,20 +4,23 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('db', '0004_alter_state_sequence'), + ("db", "0004_alter_state_sequence"), ] operations = [ migrations.AlterField( - model_name='cycle', - name='end_date', - field=models.DateField(blank=True, null=True, verbose_name='End Date'), + model_name="cycle", + name="end_date", + field=models.DateField( + blank=True, null=True, verbose_name="End Date" + ), ), migrations.AlterField( - model_name='cycle', - name='start_date', - field=models.DateField(blank=True, null=True, verbose_name='Start Date'), + model_name="cycle", + name="start_date", + field=models.DateField( + blank=True, null=True, verbose_name="Start Date" + ), ), ] diff --git a/apiserver/plane/db/migrations/0006_alter_cycle_status.py b/apiserver/plane/db/migrations/0006_alter_cycle_status.py index f49e263fb..3121f4fe5 100644 --- a/apiserver/plane/db/migrations/0006_alter_cycle_status.py +++ b/apiserver/plane/db/migrations/0006_alter_cycle_status.py @@ -4,15 +4,23 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('db', '0005_auto_20221114_2127'), + ("db", "0005_auto_20221114_2127"), ] operations = [ migrations.AlterField( - model_name='cycle', - name='status', - field=models.CharField(choices=[('draft', 'Draft'), ('started', 'Started'), ('completed', 'Completed')], default='draft', max_length=255, verbose_name='Cycle Status'), + model_name="cycle", + name="status", + field=models.CharField( + choices=[ + ("draft", "Draft"), + ("started", "Started"), + ("completed", "Completed"), + ], + default="draft", + max_length=255, + verbose_name="Cycle Status", + ), ), ] diff --git a/apiserver/plane/db/migrations/0007_label_parent.py b/apiserver/plane/db/migrations/0007_label_parent.py index 03e660473..6e67a3c94 100644 --- a/apiserver/plane/db/migrations/0007_label_parent.py +++ b/apiserver/plane/db/migrations/0007_label_parent.py @@ -5,15 +5,20 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('db', '0006_alter_cycle_status'), + ("db", "0006_alter_cycle_status"), ] operations = [ migrations.AddField( - model_name='label', - name='parent', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='parent_label', to='db.label'), + model_name="label", + name="parent", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="parent_label", + to="db.label", + ), ), ] diff --git a/apiserver/plane/db/migrations/0008_label_colour.py b/apiserver/plane/db/migrations/0008_label_colour.py index 9e630969d..3ca6b91c1 100644 --- a/apiserver/plane/db/migrations/0008_label_colour.py +++ b/apiserver/plane/db/migrations/0008_label_colour.py @@ -4,15 +4,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('db', '0007_label_parent'), + ("db", "0007_label_parent"), ] operations = [ migrations.AddField( - model_name='label', - name='colour', + model_name="label", + name="colour", field=models.CharField(blank=True, max_length=255), ), ] diff --git a/apiserver/plane/db/migrations/0009_auto_20221208_0310.py b/apiserver/plane/db/migrations/0009_auto_20221208_0310.py index 077ab7e82..829baaa62 100644 --- a/apiserver/plane/db/migrations/0009_auto_20221208_0310.py +++ b/apiserver/plane/db/migrations/0009_auto_20221208_0310.py @@ -4,20 +4,29 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('db', '0008_label_colour'), + ("db", "0008_label_colour"), ] operations = [ migrations.AddField( - model_name='projectmember', - name='view_props', + model_name="projectmember", + name="view_props", field=models.JSONField(null=True), ), migrations.AddField( - model_name='state', - name='group', - field=models.CharField(choices=[('backlog', 'Backlog'), ('unstarted', 'Unstarted'), ('started', 'Started'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='backlog', max_length=20), + model_name="state", + name="group", + field=models.CharField( + choices=[ + ("backlog", "Backlog"), + ("unstarted", "Unstarted"), + ("started", "Started"), + ("completed", "Completed"), + ("cancelled", "Cancelled"), + ], + default="backlog", + max_length=20, + ), ), ] diff --git a/apiserver/plane/db/migrations/0010_auto_20221213_0037.py b/apiserver/plane/db/migrations/0010_auto_20221213_0037.py index e8579b5ff..1672a10ab 100644 --- a/apiserver/plane/db/migrations/0010_auto_20221213_0037.py +++ b/apiserver/plane/db/migrations/0010_auto_20221213_0037.py @@ -5,28 +5,37 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('db', '0009_auto_20221208_0310'), + ("db", "0009_auto_20221208_0310"), ] operations = [ migrations.AddField( - model_name='projectidentifier', - name='workspace', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='project_identifiers', to='db.workspace'), + model_name="projectidentifier", + name="workspace", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="project_identifiers", + to="db.workspace", + ), ), migrations.AlterField( - model_name='project', - name='identifier', - field=models.CharField(max_length=5, verbose_name='Project Identifier'), + model_name="project", + name="identifier", + field=models.CharField( + max_length=5, verbose_name="Project Identifier" + ), ), migrations.AlterUniqueTogether( - name='project', - unique_together={('name', 'workspace'), ('identifier', 'workspace')}, + name="project", + unique_together={ + ("name", "workspace"), + ("identifier", "workspace"), + }, ), migrations.AlterUniqueTogether( - name='projectidentifier', - unique_together={('name', 'workspace')}, + name="projectidentifier", + unique_together={("name", "workspace")}, ), ] diff --git a/apiserver/plane/db/migrations/0011_auto_20221222_2357.py b/apiserver/plane/db/migrations/0011_auto_20221222_2357.py index deeb1cc2f..b52df3012 100644 --- a/apiserver/plane/db/migrations/0011_auto_20221222_2357.py +++ b/apiserver/plane/db/migrations/0011_auto_20221222_2357.py @@ -8,122 +8,341 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0010_auto_20221213_0037'), + ("db", "0010_auto_20221213_0037"), ] operations = [ migrations.CreateModel( - name='Module', + name="Module", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255, verbose_name='Module Name')), - ('description', models.TextField(blank=True, verbose_name='Module Description')), - ('description_text', models.JSONField(blank=True, null=True, verbose_name='Module Description RT')), - ('description_html', models.JSONField(blank=True, null=True, verbose_name='Module Description HTML')), - ('start_date', models.DateField(null=True)), - ('target_date', models.DateField(null=True)), - ('status', models.CharField(choices=[('backlog', 'Backlog'), ('planned', 'Planned'), ('in-progress', 'In Progress'), ('paused', 'Paused'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='planned', max_length=20)), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "name", + models.CharField( + max_length=255, verbose_name="Module Name" + ), + ), + ( + "description", + models.TextField( + blank=True, verbose_name="Module Description" + ), + ), + ( + "description_text", + models.JSONField( + blank=True, + null=True, + verbose_name="Module Description RT", + ), + ), + ( + "description_html", + models.JSONField( + blank=True, + null=True, + verbose_name="Module Description HTML", + ), + ), + ("start_date", models.DateField(null=True)), + ("target_date", models.DateField(null=True)), + ( + "status", + models.CharField( + choices=[ + ("backlog", "Backlog"), + ("planned", "Planned"), + ("in-progress", "In Progress"), + ("paused", "Paused"), + ("completed", "Completed"), + ("cancelled", "Cancelled"), + ], + default="planned", + max_length=20, + ), + ), ], options={ - 'verbose_name': 'Module', - 'verbose_name_plural': 'Modules', - 'db_table': 'module', - 'ordering': ('-created_at',), + "verbose_name": "Module", + "verbose_name_plural": "Modules", + "db_table": "module", + "ordering": ("-created_at",), }, ), migrations.AddField( - model_name='project', - name='icon', + model_name="project", + name="icon", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='projectmember', - name='default_props', - field=models.JSONField(default=plane.db.models.project.get_default_props), + model_name="projectmember", + name="default_props", + field=models.JSONField( + default=plane.db.models.project.get_default_props + ), ), migrations.AddField( - model_name='user', - name='my_issues_prop', + model_name="user", + name="my_issues_prop", field=models.JSONField(null=True), ), migrations.CreateModel( - name='ModuleMember', + name="ModuleMember", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='modulemember_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ('module', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='db.module')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_modulemember', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='modulemember_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_modulemember', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="modulemember_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "member", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "module", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="db.module", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_modulemember", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="modulemember_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_modulemember", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Module Member', - 'verbose_name_plural': 'Module Members', - 'db_table': 'module_member', - 'ordering': ('-created_at',), - 'unique_together': {('module', 'member')}, + "verbose_name": "Module Member", + "verbose_name_plural": "Module Members", + "db_table": "module_member", + "ordering": ("-created_at",), + "unique_together": {("module", "member")}, }, ), migrations.AddField( - model_name='module', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='module_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), + model_name="module", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="module_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), ), migrations.AddField( - model_name='module', - name='lead', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='module_leads', to=settings.AUTH_USER_MODEL), + model_name="module", + name="lead", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="module_leads", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='module', - name='members', - field=models.ManyToManyField(blank=True, related_name='module_members', through='db.ModuleMember', to=settings.AUTH_USER_MODEL), + model_name="module", + name="members", + field=models.ManyToManyField( + blank=True, + related_name="module_members", + through="db.ModuleMember", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='module', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_module', to='db.project'), + model_name="module", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_module", + to="db.project", + ), ), migrations.AddField( - model_name='module', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='module_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), + model_name="module", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="module_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), ), migrations.AddField( - model_name='module', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_module', to='db.workspace'), + model_name="module", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_module", + to="db.workspace", + ), ), migrations.CreateModel( - name='ModuleIssue', + name="ModuleIssue", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='moduleissue_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_module', to='db.issue')), - ('module', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_module', to='db.module')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_moduleissue', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='moduleissue_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_moduleissue', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="moduleissue_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_module", + to="db.issue", + ), + ), + ( + "module", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_module", + to="db.module", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_moduleissue", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="moduleissue_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_moduleissue", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Module Issue', - 'verbose_name_plural': 'Module Issues', - 'db_table': 'module_issues', - 'ordering': ('-created_at',), - 'unique_together': {('module', 'issue')}, + "verbose_name": "Module Issue", + "verbose_name_plural": "Module Issues", + "db_table": "module_issues", + "ordering": ("-created_at",), + "unique_together": {("module", "issue")}, }, ), migrations.AlterUniqueTogether( - name='module', - unique_together={('name', 'project')}, + name="module", + unique_together={("name", "project")}, ), ] diff --git a/apiserver/plane/db/migrations/0012_auto_20230104_0117.py b/apiserver/plane/db/migrations/0012_auto_20230104_0117.py index b1ff63fe1..bc767dd5d 100644 --- a/apiserver/plane/db/migrations/0012_auto_20230104_0117.py +++ b/apiserver/plane/db/migrations/0012_auto_20230104_0117.py @@ -7,166 +7,228 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0011_auto_20221222_2357'), + ("db", "0011_auto_20221222_2357"), ] operations = [ migrations.AddField( - model_name='issueactivity', - name='new_identifier', + model_name="issueactivity", + name="new_identifier", field=models.UUIDField(null=True), ), migrations.AddField( - model_name='issueactivity', - name='old_identifier', + model_name="issueactivity", + name="old_identifier", field=models.UUIDField(null=True), ), migrations.AlterField( - model_name='moduleissue', - name='issue', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='issue_module', to='db.issue'), + model_name="moduleissue", + name="issue", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_module", + to="db.issue", + ), ), migrations.AlterUniqueTogether( - name='moduleissue', + name="moduleissue", unique_together=set(), ), migrations.AlterModelTable( - name='cycle', - table='cycles', + name="cycle", + table="cycles", ), migrations.AlterModelTable( - name='cycleissue', - table='cycle_issues', + name="cycleissue", + table="cycle_issues", ), migrations.AlterModelTable( - name='fileasset', - table='file_assets', + name="fileasset", + table="file_assets", ), migrations.AlterModelTable( - name='issue', - table='issues', + name="issue", + table="issues", ), migrations.AlterModelTable( - name='issueactivity', - table='issue_activities', + name="issueactivity", + table="issue_activities", ), migrations.AlterModelTable( - name='issueassignee', - table='issue_assignees', + name="issueassignee", + table="issue_assignees", ), migrations.AlterModelTable( - name='issueblocker', - table='issue_blockers', + name="issueblocker", + table="issue_blockers", ), migrations.AlterModelTable( - name='issuecomment', - table='issue_comments', + name="issuecomment", + table="issue_comments", ), migrations.AlterModelTable( - name='issuelabel', - table='issue_labels', + name="issuelabel", + table="issue_labels", ), migrations.AlterModelTable( - name='issueproperty', - table='issue_properties', + name="issueproperty", + table="issue_properties", ), migrations.AlterModelTable( - name='issuesequence', - table='issue_sequences', + name="issuesequence", + table="issue_sequences", ), migrations.AlterModelTable( - name='label', - table='labels', + name="label", + table="labels", ), migrations.AlterModelTable( - name='module', - table='modules', + name="module", + table="modules", ), migrations.AlterModelTable( - name='modulemember', - table='module_members', + name="modulemember", + table="module_members", ), migrations.AlterModelTable( - name='project', - table='projects', + name="project", + table="projects", ), migrations.AlterModelTable( - name='projectidentifier', - table='project_identifiers', + name="projectidentifier", + table="project_identifiers", ), migrations.AlterModelTable( - name='projectmember', - table='project_members', + name="projectmember", + table="project_members", ), migrations.AlterModelTable( - name='projectmemberinvite', - table='project_member_invites', + name="projectmemberinvite", + table="project_member_invites", ), migrations.AlterModelTable( - name='shortcut', - table='shortcuts', + name="shortcut", + table="shortcuts", ), migrations.AlterModelTable( - name='socialloginconnection', - table='social_login_connections', + name="socialloginconnection", + table="social_login_connections", ), migrations.AlterModelTable( - name='state', - table='states', + name="state", + table="states", ), migrations.AlterModelTable( - name='team', - table='teams', + name="team", + table="teams", ), migrations.AlterModelTable( - name='teammember', - table='team_members', + name="teammember", + table="team_members", ), migrations.AlterModelTable( - name='timelineissue', - table='issue_timelines', + name="timelineissue", + table="issue_timelines", ), migrations.AlterModelTable( - name='user', - table='users', + name="user", + table="users", ), migrations.AlterModelTable( - name='view', - table='views', + name="view", + table="views", ), migrations.AlterModelTable( - name='workspace', - table='workspaces', + name="workspace", + table="workspaces", ), migrations.AlterModelTable( - name='workspacemember', - table='workspace_members', + name="workspacemember", + table="workspace_members", ), migrations.AlterModelTable( - name='workspacememberinvite', - table='workspace_member_invites', + name="workspacememberinvite", + table="workspace_member_invites", ), migrations.CreateModel( - name='ModuleLink', + name="ModuleLink", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('title', models.CharField(max_length=255, null=True)), - ('url', models.URLField()), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='modulelink_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('module', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='link_module', to='db.module')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_modulelink', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='modulelink_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_modulelink', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("title", models.CharField(max_length=255, null=True)), + ("url", models.URLField()), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="modulelink_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "module", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="link_module", + to="db.module", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_modulelink", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="modulelink_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_modulelink", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Module Link', - 'verbose_name_plural': 'Module Links', - 'db_table': 'module_links', - 'ordering': ('-created_at',), + "verbose_name": "Module Link", + "verbose_name_plural": "Module Links", + "db_table": "module_links", + "ordering": ("-created_at",), }, ), ] diff --git a/apiserver/plane/db/migrations/0013_auto_20230107_0041.py b/apiserver/plane/db/migrations/0013_auto_20230107_0041.py index c75537fc1..786e6cb5d 100644 --- a/apiserver/plane/db/migrations/0013_auto_20230107_0041.py +++ b/apiserver/plane/db/migrations/0013_auto_20230107_0041.py @@ -4,35 +4,34 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('db', '0012_auto_20230104_0117'), + ("db", "0012_auto_20230104_0117"), ] operations = [ migrations.AddField( - model_name='issue', - name='description_html', + model_name="issue", + name="description_html", field=models.TextField(blank=True), ), migrations.AddField( - model_name='issue', - name='description_stripped', + model_name="issue", + name="description_stripped", field=models.TextField(blank=True), ), migrations.AddField( - model_name='user', - name='role', + model_name="user", + name="role", field=models.CharField(blank=True, max_length=300, null=True), ), migrations.AddField( - model_name='workspacemember', - name='view_props', + model_name="workspacemember", + name="view_props", field=models.JSONField(blank=True, null=True), ), migrations.AlterField( - model_name='issue', - name='description', + model_name="issue", + name="description", field=models.JSONField(blank=True), ), ] diff --git a/apiserver/plane/db/migrations/0014_alter_workspacememberinvite_unique_together.py b/apiserver/plane/db/migrations/0014_alter_workspacememberinvite_unique_together.py index b1786c9c1..5642ae15d 100644 --- a/apiserver/plane/db/migrations/0014_alter_workspacememberinvite_unique_together.py +++ b/apiserver/plane/db/migrations/0014_alter_workspacememberinvite_unique_together.py @@ -4,14 +4,13 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('db', '0013_auto_20230107_0041'), + ("db", "0013_auto_20230107_0041"), ] operations = [ migrations.AlterUniqueTogether( - name='workspacememberinvite', - unique_together={('email', 'workspace')}, + name="workspacememberinvite", + unique_together={("email", "workspace")}, ), ] diff --git a/apiserver/plane/db/migrations/0015_auto_20230107_1636.py b/apiserver/plane/db/migrations/0015_auto_20230107_1636.py index e3f5dc26a..903c78b05 100644 --- a/apiserver/plane/db/migrations/0015_auto_20230107_1636.py +++ b/apiserver/plane/db/migrations/0015_auto_20230107_1636.py @@ -4,25 +4,24 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('db', '0014_alter_workspacememberinvite_unique_together'), + ("db", "0014_alter_workspacememberinvite_unique_together"), ] operations = [ migrations.RenameField( - model_name='issuecomment', - old_name='comment', - new_name='comment_stripped', + model_name="issuecomment", + old_name="comment", + new_name="comment_stripped", ), migrations.AddField( - model_name='issuecomment', - name='comment_html', + model_name="issuecomment", + name="comment_html", field=models.TextField(blank=True), ), migrations.AddField( - model_name='issuecomment', - name='comment_json', + model_name="issuecomment", + name="comment_json", field=models.JSONField(blank=True, null=True), ), ] diff --git a/apiserver/plane/db/migrations/0016_auto_20230107_1735.py b/apiserver/plane/db/migrations/0016_auto_20230107_1735.py index 073c1e117..a22dc9a62 100644 --- a/apiserver/plane/db/migrations/0016_auto_20230107_1735.py +++ b/apiserver/plane/db/migrations/0016_auto_20230107_1735.py @@ -6,20 +6,27 @@ import plane.db.models.asset class Migration(migrations.Migration): - dependencies = [ - ('db', '0015_auto_20230107_1636'), + ("db", "0015_auto_20230107_1636"), ] operations = [ migrations.AddField( - model_name='fileasset', - name='workspace', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='assets', to='db.workspace'), + model_name="fileasset", + name="workspace", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="assets", + to="db.workspace", + ), ), migrations.AlterField( - model_name='fileasset', - name='asset', - field=models.FileField(upload_to=plane.db.models.asset.get_upload_path, validators=[plane.db.models.asset.file_size]), + model_name="fileasset", + name="asset", + field=models.FileField( + upload_to=plane.db.models.asset.get_upload_path, + validators=[plane.db.models.asset.file_size], + ), ), ] diff --git a/apiserver/plane/db/migrations/0017_alter_workspace_unique_together.py b/apiserver/plane/db/migrations/0017_alter_workspace_unique_together.py index c6bfc2145..1ab721a3e 100644 --- a/apiserver/plane/db/migrations/0017_alter_workspace_unique_together.py +++ b/apiserver/plane/db/migrations/0017_alter_workspace_unique_together.py @@ -4,14 +4,13 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('db', '0016_auto_20230107_1735'), + ("db", "0016_auto_20230107_1735"), ] operations = [ migrations.AlterUniqueTogether( - name='workspace', + name="workspace", unique_together=set(), ), ] diff --git a/apiserver/plane/db/migrations/0018_auto_20230130_0119.py b/apiserver/plane/db/migrations/0018_auto_20230130_0119.py index 03eaeacd7..32f886539 100644 --- a/apiserver/plane/db/migrations/0018_auto_20230130_0119.py +++ b/apiserver/plane/db/migrations/0018_auto_20230130_0119.py @@ -8,50 +8,112 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0017_alter_workspace_unique_together'), + ("db", "0017_alter_workspace_unique_together"), ] operations = [ migrations.AddField( - model_name='user', - name='is_bot', + model_name="user", + name="is_bot", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='issue', - name='description', + model_name="issue", + name="description", field=models.JSONField(blank=True, null=True), ), migrations.AlterField( - model_name='issue', - name='description_html', + model_name="issue", + name="description_html", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='issue', - name='description_stripped', + model_name="issue", + name="description_stripped", field=models.TextField(blank=True, null=True), ), migrations.CreateModel( - name='APIToken', + name="APIToken", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('token', models.CharField(default=plane.db.models.api.generate_token, max_length=255, unique=True)), - ('label', models.CharField(default=plane.db.models.api.generate_label_token, max_length=255)), - ('user_type', models.PositiveSmallIntegerField(choices=[(0, 'Human'), (1, 'Bot')], default=0)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='apitoken_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='apitoken_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bot_tokens', to=settings.AUTH_USER_MODEL)), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "token", + models.CharField( + default=plane.db.models.api.generate_token, + max_length=255, + unique=True, + ), + ), + ( + "label", + models.CharField( + default=plane.db.models.api.generate_label_token, + max_length=255, + ), + ), + ( + "user_type", + models.PositiveSmallIntegerField( + choices=[(0, "Human"), (1, "Bot")], default=0 + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="apitoken_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="apitoken_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="bot_tokens", + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'verbose_name': 'API Token', - 'verbose_name_plural': 'API Tokems', - 'db_table': 'api_tokens', - 'ordering': ('-created_at',), + "verbose_name": "API Token", + "verbose_name_plural": "API Tokems", + "db_table": "api_tokens", + "ordering": ("-created_at",), }, ), ] diff --git a/apiserver/plane/db/migrations/0019_auto_20230131_0049.py b/apiserver/plane/db/migrations/0019_auto_20230131_0049.py index 38412aa9e..63545f497 100644 --- a/apiserver/plane/db/migrations/0019_auto_20230131_0049.py +++ b/apiserver/plane/db/migrations/0019_auto_20230131_0049.py @@ -4,20 +4,23 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('db', '0018_auto_20230130_0119'), + ("db", "0018_auto_20230130_0119"), ] operations = [ migrations.AlterField( - model_name='issueactivity', - name='new_value', - field=models.TextField(blank=True, null=True, verbose_name='New Value'), + model_name="issueactivity", + name="new_value", + field=models.TextField( + blank=True, null=True, verbose_name="New Value" + ), ), migrations.AlterField( - model_name='issueactivity', - name='old_value', - field=models.TextField(blank=True, null=True, verbose_name='Old Value'), + model_name="issueactivity", + name="old_value", + field=models.TextField( + blank=True, null=True, verbose_name="Old Value" + ), ), ] diff --git a/apiserver/plane/db/migrations/0020_auto_20230214_0118.py b/apiserver/plane/db/migrations/0020_auto_20230214_0118.py index 192764078..4269f53b3 100644 --- a/apiserver/plane/db/migrations/0020_auto_20230214_0118.py +++ b/apiserver/plane/db/migrations/0020_auto_20230214_0118.py @@ -5,65 +5,69 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('db', '0019_auto_20230131_0049'), + ("db", "0019_auto_20230131_0049"), ] operations = [ migrations.RenameField( - model_name='label', - old_name='colour', - new_name='color', + model_name="label", + old_name="colour", + new_name="color", ), migrations.AddField( - model_name='apitoken', - name='workspace', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='api_tokens', to='db.workspace'), + model_name="apitoken", + name="workspace", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="api_tokens", + to="db.workspace", + ), ), migrations.AddField( - model_name='issue', - name='completed_at', + model_name="issue", + name="completed_at", field=models.DateTimeField(null=True), ), migrations.AddField( - model_name='issue', - name='sort_order', + model_name="issue", + name="sort_order", field=models.FloatField(default=65535), ), migrations.AddField( - model_name='project', - name='cycle_view', + model_name="project", + name="cycle_view", field=models.BooleanField(default=True), ), migrations.AddField( - model_name='project', - name='module_view', + model_name="project", + name="module_view", field=models.BooleanField(default=True), ), migrations.AddField( - model_name='state', - name='default', + model_name="state", + name="default", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='issue', - name='description', + model_name="issue", + name="description", field=models.JSONField(blank=True, default=dict), ), migrations.AlterField( - model_name='issue', - name='description_html', - field=models.TextField(blank=True, default='

'), + model_name="issue", + name="description_html", + field=models.TextField(blank=True, default="

"), ), migrations.AlterField( - model_name='issuecomment', - name='comment_html', - field=models.TextField(blank=True, default='

'), + model_name="issuecomment", + name="comment_html", + field=models.TextField(blank=True, default="

"), ), migrations.AlterField( - model_name='issuecomment', - name='comment_json', + model_name="issuecomment", + name="comment_json", field=models.JSONField(blank=True, default=dict), ), ] diff --git a/apiserver/plane/db/migrations/0021_auto_20230223_0104.py b/apiserver/plane/db/migrations/0021_auto_20230223_0104.py index bae6a086a..0dc052c28 100644 --- a/apiserver/plane/db/migrations/0021_auto_20230223_0104.py +++ b/apiserver/plane/db/migrations/0021_auto_20230223_0104.py @@ -7,179 +7,616 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0020_auto_20230214_0118'), + ("db", "0020_auto_20230214_0118"), ] operations = [ migrations.CreateModel( - name='GithubRepository', + name="GithubRepository", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=500)), - ('url', models.URLField(null=True)), - ('config', models.JSONField(default=dict)), - ('repository_id', models.BigIntegerField()), - ('owner', models.CharField(max_length=500)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='githubrepository_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_githubrepository', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='githubrepository_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_githubrepository', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("name", models.CharField(max_length=500)), + ("url", models.URLField(null=True)), + ("config", models.JSONField(default=dict)), + ("repository_id", models.BigIntegerField()), + ("owner", models.CharField(max_length=500)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="githubrepository_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_githubrepository", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="githubrepository_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_githubrepository", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Repository', - 'verbose_name_plural': 'Repositories', - 'db_table': 'github_repositories', - 'ordering': ('-created_at',), + "verbose_name": "Repository", + "verbose_name_plural": "Repositories", + "db_table": "github_repositories", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='Integration', + name="Integration", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('title', models.CharField(max_length=400)), - ('provider', models.CharField(max_length=400, unique=True)), - ('network', models.PositiveIntegerField(choices=[(1, 'Private'), (2, 'Public')], default=1)), - ('description', models.JSONField(default=dict)), - ('author', models.CharField(blank=True, max_length=400)), - ('webhook_url', models.TextField(blank=True)), - ('webhook_secret', models.TextField(blank=True)), - ('redirect_url', models.TextField(blank=True)), - ('metadata', models.JSONField(default=dict)), - ('verified', models.BooleanField(default=False)), - ('avatar_url', models.URLField(blank=True, null=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='integration_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='integration_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("title", models.CharField(max_length=400)), + ("provider", models.CharField(max_length=400, unique=True)), + ( + "network", + models.PositiveIntegerField( + choices=[(1, "Private"), (2, "Public")], default=1 + ), + ), + ("description", models.JSONField(default=dict)), + ("author", models.CharField(blank=True, max_length=400)), + ("webhook_url", models.TextField(blank=True)), + ("webhook_secret", models.TextField(blank=True)), + ("redirect_url", models.TextField(blank=True)), + ("metadata", models.JSONField(default=dict)), + ("verified", models.BooleanField(default=False)), + ("avatar_url", models.URLField(blank=True, null=True)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="integration_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="integration_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), ], options={ - 'verbose_name': 'Integration', - 'verbose_name_plural': 'Integrations', - 'db_table': 'integrations', - 'ordering': ('-created_at',), + "verbose_name": "Integration", + "verbose_name_plural": "Integrations", + "db_table": "integrations", + "ordering": ("-created_at",), }, ), migrations.AlterField( - model_name='issueactivity', - name='issue', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issue_activity', to='db.issue'), + model_name="issueactivity", + name="issue", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issue_activity", + to="db.issue", + ), ), migrations.CreateModel( - name='WorkspaceIntegration', + name="WorkspaceIntegration", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('metadata', models.JSONField(default=dict)), - ('config', models.JSONField(default=dict)), - ('actor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='integrations', to=settings.AUTH_USER_MODEL)), - ('api_token', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='integrations', to='db.apitoken')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workspaceintegration_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('integration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='integrated_workspaces', to='db.integration')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workspaceintegration_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_integrations', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("metadata", models.JSONField(default=dict)), + ("config", models.JSONField(default=dict)), + ( + "actor", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="integrations", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "api_token", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="integrations", + to="db.apitoken", + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="workspaceintegration_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "integration", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="integrated_workspaces", + to="db.integration", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="workspaceintegration_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_integrations", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Workspace Integration', - 'verbose_name_plural': 'Workspace Integrations', - 'db_table': 'workspace_integrations', - 'ordering': ('-created_at',), - 'unique_together': {('workspace', 'integration')}, + "verbose_name": "Workspace Integration", + "verbose_name_plural": "Workspace Integrations", + "db_table": "workspace_integrations", + "ordering": ("-created_at",), + "unique_together": {("workspace", "integration")}, }, ), migrations.CreateModel( - name='IssueLink', + name="IssueLink", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('title', models.CharField(max_length=255, null=True)), - ('url', models.URLField()), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issuelink_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_link', to='db.issue')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_issuelink', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issuelink_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_issuelink', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("title", models.CharField(max_length=255, null=True)), + ("url", models.URLField()), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issuelink_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_link", + to="db.issue", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_issuelink", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issuelink_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_issuelink", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Link', - 'verbose_name_plural': 'Issue Links', - 'db_table': 'issue_links', - 'ordering': ('-created_at',), + "verbose_name": "Issue Link", + "verbose_name_plural": "Issue Links", + "db_table": "issue_links", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='GithubRepositorySync', + name="GithubRepositorySync", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('credentials', models.JSONField(default=dict)), - ('actor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_syncs', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='githubrepositorysync_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('label', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='repo_syncs', to='db.label')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_githubrepositorysync', to='db.project')), - ('repository', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='syncs', to='db.githubrepository')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='githubrepositorysync_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_githubrepositorysync', to='db.workspace')), - ('workspace_integration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='github_syncs', to='db.workspaceintegration')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("credentials", models.JSONField(default=dict)), + ( + "actor", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="user_syncs", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="githubrepositorysync_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "label", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="repo_syncs", + to="db.label", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_githubrepositorysync", + to="db.project", + ), + ), + ( + "repository", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="syncs", + to="db.githubrepository", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="githubrepositorysync_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_githubrepositorysync", + to="db.workspace", + ), + ), + ( + "workspace_integration", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="github_syncs", + to="db.workspaceintegration", + ), + ), ], options={ - 'verbose_name': 'Github Repository Sync', - 'verbose_name_plural': 'Github Repository Syncs', - 'db_table': 'github_repository_syncs', - 'ordering': ('-created_at',), - 'unique_together': {('project', 'repository')}, + "verbose_name": "Github Repository Sync", + "verbose_name_plural": "Github Repository Syncs", + "db_table": "github_repository_syncs", + "ordering": ("-created_at",), + "unique_together": {("project", "repository")}, }, ), migrations.CreateModel( - name='GithubIssueSync', + name="GithubIssueSync", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('repo_issue_id', models.BigIntegerField()), - ('github_issue_id', models.BigIntegerField()), - ('issue_url', models.URLField()), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='githubissuesync_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='github_syncs', to='db.issue')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_githubissuesync', to='db.project')), - ('repository_sync', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_syncs', to='db.githubrepositorysync')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='githubissuesync_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_githubissuesync', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("repo_issue_id", models.BigIntegerField()), + ("github_issue_id", models.BigIntegerField()), + ("issue_url", models.URLField()), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="githubissuesync_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="github_syncs", + to="db.issue", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_githubissuesync", + to="db.project", + ), + ), + ( + "repository_sync", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_syncs", + to="db.githubrepositorysync", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="githubissuesync_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_githubissuesync", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Github Issue Sync', - 'verbose_name_plural': 'Github Issue Syncs', - 'db_table': 'github_issue_syncs', - 'ordering': ('-created_at',), - 'unique_together': {('repository_sync', 'issue')}, + "verbose_name": "Github Issue Sync", + "verbose_name_plural": "Github Issue Syncs", + "db_table": "github_issue_syncs", + "ordering": ("-created_at",), + "unique_together": {("repository_sync", "issue")}, }, ), migrations.CreateModel( - name='GithubCommentSync', + name="GithubCommentSync", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('repo_comment_id', models.BigIntegerField()), - ('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comment_syncs', to='db.issuecomment')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='githubcommentsync_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue_sync', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comment_syncs', to='db.githubissuesync')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_githubcommentsync', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='githubcommentsync_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_githubcommentsync', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("repo_comment_id", models.BigIntegerField()), + ( + "comment", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="comment_syncs", + to="db.issuecomment", + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="githubcommentsync_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue_sync", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="comment_syncs", + to="db.githubissuesync", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_githubcommentsync", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="githubcommentsync_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_githubcommentsync", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Github Comment Sync', - 'verbose_name_plural': 'Github Comment Syncs', - 'db_table': 'github_comment_syncs', - 'ordering': ('-created_at',), - 'unique_together': {('issue_sync', 'comment')}, + "verbose_name": "Github Comment Sync", + "verbose_name_plural": "Github Comment Syncs", + "db_table": "github_comment_syncs", + "ordering": ("-created_at",), + "unique_together": {("issue_sync", "comment")}, }, ), ] diff --git a/apiserver/plane/db/migrations/0022_auto_20230307_0304.py b/apiserver/plane/db/migrations/0022_auto_20230307_0304.py index 25a8eef61..69bd577d7 100644 --- a/apiserver/plane/db/migrations/0022_auto_20230307_0304.py +++ b/apiserver/plane/db/migrations/0022_auto_20230307_0304.py @@ -7,95 +7,285 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0021_auto_20230223_0104'), + ("db", "0021_auto_20230223_0104"), ] operations = [ migrations.RemoveField( - model_name='cycle', - name='status', + model_name="cycle", + name="status", ), migrations.RemoveField( - model_name='project', - name='slug', + model_name="project", + name="slug", ), migrations.AddField( - model_name='issuelink', - name='metadata', + model_name="issuelink", + name="metadata", field=models.JSONField(default=dict), ), migrations.AddField( - model_name='modulelink', - name='metadata', + model_name="modulelink", + name="metadata", field=models.JSONField(default=dict), ), migrations.AddField( - model_name='project', - name='cover_image', + model_name="project", + name="cover_image", field=models.URLField(blank=True, null=True), ), migrations.CreateModel( - name='ProjectFavorite', + name="ProjectFavorite", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projectfavorite_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_projectfavorite', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projectfavorite_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_favorites', to=settings.AUTH_USER_MODEL)), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_projectfavorite', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="projectfavorite_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_projectfavorite", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="projectfavorite_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_favorites", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_projectfavorite", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Project Favorite', - 'verbose_name_plural': 'Project Favorites', - 'db_table': 'project_favorites', - 'ordering': ('-created_at',), - 'unique_together': {('project', 'user')}, + "verbose_name": "Project Favorite", + "verbose_name_plural": "Project Favorites", + "db_table": "project_favorites", + "ordering": ("-created_at",), + "unique_together": {("project", "user")}, }, ), migrations.CreateModel( - name='ModuleFavorite', + name="ModuleFavorite", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='modulefavorite_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('module', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='module_favorites', to='db.module')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_modulefavorite', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='modulefavorite_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='module_favorites', to=settings.AUTH_USER_MODEL)), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_modulefavorite', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="modulefavorite_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "module", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="module_favorites", + to="db.module", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_modulefavorite", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="modulefavorite_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="module_favorites", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_modulefavorite", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Module Favorite', - 'verbose_name_plural': 'Module Favorites', - 'db_table': 'module_favorites', - 'ordering': ('-created_at',), - 'unique_together': {('module', 'user')}, + "verbose_name": "Module Favorite", + "verbose_name_plural": "Module Favorites", + "db_table": "module_favorites", + "ordering": ("-created_at",), + "unique_together": {("module", "user")}, }, ), migrations.CreateModel( - name='CycleFavorite', + name="CycleFavorite", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cyclefavorite_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('cycle', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cycle_favorites', to='db.cycle')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_cyclefavorite', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cyclefavorite_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cycle_favorites', to=settings.AUTH_USER_MODEL)), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_cyclefavorite', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cyclefavorite_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "cycle", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="cycle_favorites", + to="db.cycle", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_cyclefavorite", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cyclefavorite_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="cycle_favorites", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_cyclefavorite", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Cycle Favorite', - 'verbose_name_plural': 'Cycle Favorites', - 'db_table': 'cycle_favorites', - 'ordering': ('-created_at',), - 'unique_together': {('cycle', 'user')}, + "verbose_name": "Cycle Favorite", + "verbose_name_plural": "Cycle Favorites", + "db_table": "cycle_favorites", + "ordering": ("-created_at",), + "unique_together": {("cycle", "user")}, }, ), ] diff --git a/apiserver/plane/db/migrations/0023_auto_20230316_0040.py b/apiserver/plane/db/migrations/0023_auto_20230316_0040.py index c6985866c..6f6103cae 100644 --- a/apiserver/plane/db/migrations/0023_auto_20230316_0040.py +++ b/apiserver/plane/db/migrations/0023_auto_20230316_0040.py @@ -7,86 +7,299 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0022_auto_20230307_0304'), + ("db", "0022_auto_20230307_0304"), ] operations = [ migrations.CreateModel( - name='Importer', + name="Importer", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('service', models.CharField(choices=[('github', 'GitHub')], max_length=50)), - ('status', models.CharField(choices=[('queued', 'Queued'), ('processing', 'Processing'), ('completed', 'Completed'), ('failed', 'Failed')], default='queued', max_length=50)), - ('metadata', models.JSONField(default=dict)), - ('config', models.JSONField(default=dict)), - ('data', models.JSONField(default=dict)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='importer_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('initiated_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='imports', to=settings.AUTH_USER_MODEL)), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_importer', to='db.project')), - ('token', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='importer', to='db.apitoken')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='importer_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_importer', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "service", + models.CharField( + choices=[("github", "GitHub")], max_length=50 + ), + ), + ( + "status", + models.CharField( + choices=[ + ("queued", "Queued"), + ("processing", "Processing"), + ("completed", "Completed"), + ("failed", "Failed"), + ], + default="queued", + max_length=50, + ), + ), + ("metadata", models.JSONField(default=dict)), + ("config", models.JSONField(default=dict)), + ("data", models.JSONField(default=dict)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="importer_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "initiated_by", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="imports", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_importer", + to="db.project", + ), + ), + ( + "token", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="importer", + to="db.apitoken", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="importer_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_importer", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Importer', - 'verbose_name_plural': 'Importers', - 'db_table': 'importers', - 'ordering': ('-created_at',), + "verbose_name": "Importer", + "verbose_name_plural": "Importers", + "db_table": "importers", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='IssueView', + name="IssueView", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255, verbose_name='View Name')), - ('description', models.TextField(blank=True, verbose_name='View Description')), - ('query', models.JSONField(verbose_name='View Query')), - ('access', models.PositiveSmallIntegerField(choices=[(0, 'Private'), (1, 'Public')], default=1)), - ('query_data', models.JSONField(default=dict)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueview_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_issueview', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueview_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_issueview', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "name", + models.CharField(max_length=255, verbose_name="View Name"), + ), + ( + "description", + models.TextField( + blank=True, verbose_name="View Description" + ), + ), + ("query", models.JSONField(verbose_name="View Query")), + ( + "access", + models.PositiveSmallIntegerField( + choices=[(0, "Private"), (1, "Public")], default=1 + ), + ), + ("query_data", models.JSONField(default=dict)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueview_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_issueview", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueview_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_issueview", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue View', - 'verbose_name_plural': 'Issue Views', - 'db_table': 'issue_views', - 'ordering': ('-created_at',), + "verbose_name": "Issue View", + "verbose_name_plural": "Issue Views", + "db_table": "issue_views", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='IssueViewFavorite', + name="IssueViewFavorite", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueviewfavorite_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_issueviewfavorite', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueviewfavorite_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_view_favorites', to=settings.AUTH_USER_MODEL)), - ('view', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='view_favorites', to='db.issueview')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_issueviewfavorite', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueviewfavorite_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_issueviewfavorite", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueviewfavorite_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="user_view_favorites", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "view", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="view_favorites", + to="db.issueview", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_issueviewfavorite", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'View Favorite', - 'verbose_name_plural': 'View Favorites', - 'db_table': 'view_favorites', - 'ordering': ('-created_at',), - 'unique_together': {('view', 'user')}, + "verbose_name": "View Favorite", + "verbose_name_plural": "View Favorites", + "db_table": "view_favorites", + "ordering": ("-created_at",), + "unique_together": {("view", "user")}, }, ), migrations.AlterUniqueTogether( - name='label', - unique_together={('name', 'project')}, + name="label", + unique_together={("name", "project")}, ), migrations.DeleteModel( - name='View', + name="View", ), ] diff --git a/apiserver/plane/db/migrations/0024_auto_20230322_0138.py b/apiserver/plane/db/migrations/0024_auto_20230322_0138.py index 65880891a..7a95d519e 100644 --- a/apiserver/plane/db/migrations/0024_auto_20230322_0138.py +++ b/apiserver/plane/db/migrations/0024_auto_20230322_0138.py @@ -7,107 +7,308 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0023_auto_20230316_0040'), + ("db", "0023_auto_20230316_0040"), ] operations = [ migrations.CreateModel( - name='Page', + name="Page", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255)), - ('description', models.JSONField(blank=True, default=dict)), - ('description_html', models.TextField(blank=True, default='

')), - ('description_stripped', models.TextField(blank=True, null=True)), - ('access', models.PositiveSmallIntegerField(choices=[(0, 'Public'), (1, 'Private')], default=0)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='page_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('owned_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pages', to=settings.AUTH_USER_MODEL)), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("name", models.CharField(max_length=255)), + ("description", models.JSONField(blank=True, default=dict)), + ( + "description_html", + models.TextField(blank=True, default="

"), + ), + ( + "description_stripped", + models.TextField(blank=True, null=True), + ), + ( + "access", + models.PositiveSmallIntegerField( + choices=[(0, "Public"), (1, "Private")], default=0 + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="page_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "owned_by", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="pages", + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'verbose_name': 'Page', - 'verbose_name_plural': 'Pages', - 'db_table': 'pages', - 'ordering': ('-created_at',), + "verbose_name": "Page", + "verbose_name_plural": "Pages", + "db_table": "pages", + "ordering": ("-created_at",), }, ), migrations.AddField( - model_name='project', - name='issue_views_view', + model_name="project", + name="issue_views_view", field=models.BooleanField(default=True), ), migrations.AlterField( - model_name='importer', - name='service', - field=models.CharField(choices=[('github', 'GitHub'), ('jira', 'Jira')], max_length=50), + model_name="importer", + name="service", + field=models.CharField( + choices=[("github", "GitHub"), ("jira", "Jira")], max_length=50 + ), ), migrations.AlterField( - model_name='project', - name='cover_image', + model_name="project", + name="cover_image", field=models.URLField(blank=True, max_length=800, null=True), ), migrations.CreateModel( - name='PageBlock', + name="PageBlock", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255)), - ('description', models.JSONField(blank=True, default=dict)), - ('description_html', models.TextField(blank=True, default='

')), - ('description_stripped', models.TextField(blank=True, null=True)), - ('completed_at', models.DateTimeField(null=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pageblock_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='blocks', to='db.issue')), - ('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='blocks', to='db.page')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_pageblock', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pageblock_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_pageblock', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("name", models.CharField(max_length=255)), + ("description", models.JSONField(blank=True, default=dict)), + ( + "description_html", + models.TextField(blank=True, default="

"), + ), + ( + "description_stripped", + models.TextField(blank=True, null=True), + ), + ("completed_at", models.DateTimeField(null=True)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="pageblock_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="blocks", + to="db.issue", + ), + ), + ( + "page", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="blocks", + to="db.page", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_pageblock", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="pageblock_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_pageblock", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Page Block', - 'verbose_name_plural': 'Page Blocks', - 'db_table': 'page_blocks', - 'ordering': ('-created_at',), + "verbose_name": "Page Block", + "verbose_name_plural": "Page Blocks", + "db_table": "page_blocks", + "ordering": ("-created_at",), }, ), migrations.AddField( - model_name='page', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_page', to='db.project'), + model_name="page", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_page", + to="db.project", + ), ), migrations.AddField( - model_name='page', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='page_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), + model_name="page", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="page_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), ), migrations.AddField( - model_name='page', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_page', to='db.workspace'), + model_name="page", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_page", + to="db.workspace", + ), ), migrations.CreateModel( - name='PageFavorite', + name="PageFavorite", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pagefavorite_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='page_favorites', to='db.page')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_pagefavorite', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pagefavorite_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='page_favorites', to=settings.AUTH_USER_MODEL)), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_pagefavorite', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="pagefavorite_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "page", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="page_favorites", + to="db.page", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_pagefavorite", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="pagefavorite_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="page_favorites", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_pagefavorite", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Page Favorite', - 'verbose_name_plural': 'Page Favorites', - 'db_table': 'page_favorites', - 'ordering': ('-created_at',), - 'unique_together': {('page', 'user')}, + "verbose_name": "Page Favorite", + "verbose_name_plural": "Page Favorites", + "db_table": "page_favorites", + "ordering": ("-created_at",), + "unique_together": {("page", "user")}, }, ), ] diff --git a/apiserver/plane/db/migrations/0025_auto_20230331_0203.py b/apiserver/plane/db/migrations/0025_auto_20230331_0203.py index 1097a4612..702d74cfc 100644 --- a/apiserver/plane/db/migrations/0025_auto_20230331_0203.py +++ b/apiserver/plane/db/migrations/0025_auto_20230331_0203.py @@ -7,55 +7,125 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0024_auto_20230322_0138'), + ("db", "0024_auto_20230322_0138"), ] operations = [ migrations.AddField( - model_name='page', - name='color', + model_name="page", + name="color", field=models.CharField(blank=True, max_length=255), ), migrations.AddField( - model_name='pageblock', - name='sort_order', + model_name="pageblock", + name="sort_order", field=models.FloatField(default=65535), ), migrations.AddField( - model_name='pageblock', - name='sync', + model_name="pageblock", + name="sync", field=models.BooleanField(default=True), ), migrations.AddField( - model_name='project', - name='page_view', + model_name="project", + name="page_view", field=models.BooleanField(default=True), ), migrations.CreateModel( - name='PageLabel', + name="PageLabel", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pagelabel_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('label', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='page_labels', to='db.label')), - ('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='page_labels', to='db.page')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_pagelabel', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pagelabel_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_pagelabel', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="pagelabel_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "label", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="page_labels", + to="db.label", + ), + ), + ( + "page", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="page_labels", + to="db.page", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_pagelabel", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="pagelabel_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_pagelabel", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Page Label', - 'verbose_name_plural': 'Page Labels', - 'db_table': 'page_labels', - 'ordering': ('-created_at',), + "verbose_name": "Page Label", + "verbose_name_plural": "Page Labels", + "db_table": "page_labels", + "ordering": ("-created_at",), }, ), migrations.AddField( - model_name='page', - name='labels', - field=models.ManyToManyField(blank=True, related_name='pages', through='db.PageLabel', to='db.Label'), + model_name="page", + name="labels", + field=models.ManyToManyField( + blank=True, + related_name="pages", + through="db.PageLabel", + to="db.Label", + ), ), ] diff --git a/apiserver/plane/db/migrations/0026_alter_projectmember_view_props.py b/apiserver/plane/db/migrations/0026_alter_projectmember_view_props.py index 6f74fa499..310087f97 100644 --- a/apiserver/plane/db/migrations/0026_alter_projectmember_view_props.py +++ b/apiserver/plane/db/migrations/0026_alter_projectmember_view_props.py @@ -5,15 +5,16 @@ import plane.db.models.project class Migration(migrations.Migration): - dependencies = [ - ('db', '0025_auto_20230331_0203'), + ("db", "0025_auto_20230331_0203"), ] operations = [ migrations.AlterField( - model_name='projectmember', - name='view_props', - field=models.JSONField(default=plane.db.models.project.get_default_props), + model_name="projectmember", + name="view_props", + field=models.JSONField( + default=plane.db.models.project.get_default_props + ), ), - ] \ No newline at end of file + ] diff --git a/apiserver/plane/db/migrations/0027_auto_20230409_0312.py b/apiserver/plane/db/migrations/0027_auto_20230409_0312.py index 8d344cf34..0377c84e8 100644 --- a/apiserver/plane/db/migrations/0027_auto_20230409_0312.py +++ b/apiserver/plane/db/migrations/0027_auto_20230409_0312.py @@ -9,89 +9,289 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0026_alter_projectmember_view_props'), + ("db", "0026_alter_projectmember_view_props"), ] operations = [ migrations.CreateModel( - name='Estimate', + name="Estimate", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255)), - ('description', models.TextField(blank=True, verbose_name='Estimate Description')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='estimate_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_estimate', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='estimate_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_estimate', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("name", models.CharField(max_length=255)), + ( + "description", + models.TextField( + blank=True, verbose_name="Estimate Description" + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="estimate_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_estimate", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="estimate_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_estimate", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Estimate', - 'verbose_name_plural': 'Estimates', - 'db_table': 'estimates', - 'ordering': ('name',), - 'unique_together': {('name', 'project')}, + "verbose_name": "Estimate", + "verbose_name_plural": "Estimates", + "db_table": "estimates", + "ordering": ("name",), + "unique_together": {("name", "project")}, }, ), migrations.RemoveField( - model_name='issue', - name='attachments', + model_name="issue", + name="attachments", ), migrations.AddField( - model_name='issue', - name='estimate_point', - field=models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(7)]), + model_name="issue", + name="estimate_point", + field=models.IntegerField( + default=0, + validators=[ + django.core.validators.MinValueValidator(0), + django.core.validators.MaxValueValidator(7), + ], + ), ), migrations.CreateModel( - name='IssueAttachment', + name="IssueAttachment", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('attributes', models.JSONField(default=dict)), - ('asset', models.FileField(upload_to=plane.db.models.issue.get_upload_path, validators=[plane.db.models.issue.file_size])), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueattachment_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_attachment', to='db.issue')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_issueattachment', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issueattachment_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_issueattachment', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("attributes", models.JSONField(default=dict)), + ( + "asset", + models.FileField( + upload_to=plane.db.models.issue.get_upload_path, + validators=[plane.db.models.issue.file_size], + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueattachment_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_attachment", + to="db.issue", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_issueattachment", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="issueattachment_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_issueattachment", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Attachment', - 'verbose_name_plural': 'Issue Attachments', - 'db_table': 'issue_attachments', - 'ordering': ('-created_at',), + "verbose_name": "Issue Attachment", + "verbose_name_plural": "Issue Attachments", + "db_table": "issue_attachments", + "ordering": ("-created_at",), }, ), migrations.AddField( - model_name='project', - name='estimate', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projects', to='db.estimate'), + model_name="project", + name="estimate", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="projects", + to="db.estimate", + ), ), migrations.CreateModel( - name='EstimatePoint', + name="EstimatePoint", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('key', models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(7)])), - ('description', models.TextField(blank=True)), - ('value', models.CharField(max_length=20)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='estimatepoint_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('estimate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='points', to='db.estimate')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_estimatepoint', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='estimatepoint_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_estimatepoint', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "key", + models.IntegerField( + default=0, + validators=[ + django.core.validators.MinValueValidator(0), + django.core.validators.MaxValueValidator(7), + ], + ), + ), + ("description", models.TextField(blank=True)), + ("value", models.CharField(max_length=20)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="estimatepoint_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "estimate", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="points", + to="db.estimate", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_estimatepoint", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="estimatepoint_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_estimatepoint", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Estimate Point', - 'verbose_name_plural': 'Estimate Points', - 'db_table': 'estimate_points', - 'ordering': ('value',), - 'unique_together': {('value', 'estimate')}, + "verbose_name": "Estimate Point", + "verbose_name_plural": "Estimate Points", + "db_table": "estimate_points", + "ordering": ("value",), + "unique_together": {("value", "estimate")}, }, ), ] diff --git a/apiserver/plane/db/migrations/0028_auto_20230414_1703.py b/apiserver/plane/db/migrations/0028_auto_20230414_1703.py index bb0b67b92..ffccccff5 100644 --- a/apiserver/plane/db/migrations/0028_auto_20230414_1703.py +++ b/apiserver/plane/db/migrations/0028_auto_20230414_1703.py @@ -8,41 +8,99 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0027_auto_20230409_0312'), + ("db", "0027_auto_20230409_0312"), ] operations = [ migrations.AddField( - model_name='user', - name='theme', + model_name="user", + name="theme", field=models.JSONField(default=dict), ), migrations.AlterField( - model_name='issue', - name='estimate_point', - field=models.IntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(7)]), + model_name="issue", + name="estimate_point", + field=models.IntegerField( + blank=True, + null=True, + validators=[ + django.core.validators.MinValueValidator(0), + django.core.validators.MaxValueValidator(7), + ], + ), ), migrations.CreateModel( - name='WorkspaceTheme', + name="WorkspaceTheme", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=300)), - ('colors', models.JSONField(default=dict)), - ('actor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='themes', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workspacetheme_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workspacetheme_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='themes', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("name", models.CharField(max_length=300)), + ("colors", models.JSONField(default=dict)), + ( + "actor", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="themes", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="workspacetheme_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="workspacetheme_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="themes", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Workspace Theme', - 'verbose_name_plural': 'Workspace Themes', - 'db_table': 'workspace_themes', - 'ordering': ('-created_at',), - 'unique_together': {('workspace', 'name')}, + "verbose_name": "Workspace Theme", + "verbose_name_plural": "Workspace Themes", + "db_table": "workspace_themes", + "ordering": ("-created_at",), + "unique_together": {("workspace", "name")}, }, ), ] diff --git a/apiserver/plane/db/migrations/0029_auto_20230502_0126.py b/apiserver/plane/db/migrations/0029_auto_20230502_0126.py index 373cc39bd..cd2b1b865 100644 --- a/apiserver/plane/db/migrations/0029_auto_20230502_0126.py +++ b/apiserver/plane/db/migrations/0029_auto_20230502_0126.py @@ -7,52 +7,110 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0028_auto_20230414_1703'), + ("db", "0028_auto_20230414_1703"), ] operations = [ migrations.AddField( - model_name='cycle', - name='view_props', + model_name="cycle", + name="view_props", field=models.JSONField(default=dict), ), migrations.AddField( - model_name='importer', - name='imported_data', + model_name="importer", + name="imported_data", field=models.JSONField(null=True), ), migrations.AddField( - model_name='module', - name='view_props', + model_name="module", + name="view_props", field=models.JSONField(default=dict), ), migrations.CreateModel( - name='SlackProjectSync', + name="SlackProjectSync", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('access_token', models.CharField(max_length=300)), - ('scopes', models.TextField()), - ('bot_user_id', models.CharField(max_length=50)), - ('webhook_url', models.URLField(max_length=1000)), - ('data', models.JSONField(default=dict)), - ('team_id', models.CharField(max_length=30)), - ('team_name', models.CharField(max_length=300)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='slackprojectsync_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_slackprojectsync', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='slackprojectsync_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_slackprojectsync', to='db.workspace')), - ('workspace_integration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='slack_syncs', to='db.workspaceintegration')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("access_token", models.CharField(max_length=300)), + ("scopes", models.TextField()), + ("bot_user_id", models.CharField(max_length=50)), + ("webhook_url", models.URLField(max_length=1000)), + ("data", models.JSONField(default=dict)), + ("team_id", models.CharField(max_length=30)), + ("team_name", models.CharField(max_length=300)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="slackprojectsync_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_slackprojectsync", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="slackprojectsync_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_slackprojectsync", + to="db.workspace", + ), + ), + ( + "workspace_integration", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="slack_syncs", + to="db.workspaceintegration", + ), + ), ], options={ - 'verbose_name': 'Slack Project Sync', - 'verbose_name_plural': 'Slack Project Syncs', - 'db_table': 'slack_project_syncs', - 'ordering': ('-created_at',), - 'unique_together': {('team_id', 'project')}, + "verbose_name": "Slack Project Sync", + "verbose_name_plural": "Slack Project Syncs", + "db_table": "slack_project_syncs", + "ordering": ("-created_at",), + "unique_together": {("team_id", "project")}, }, ), ] diff --git a/apiserver/plane/db/migrations/0030_alter_estimatepoint_unique_together.py b/apiserver/plane/db/migrations/0030_alter_estimatepoint_unique_together.py index bfc1da530..63db205dc 100644 --- a/apiserver/plane/db/migrations/0030_alter_estimatepoint_unique_together.py +++ b/apiserver/plane/db/migrations/0030_alter_estimatepoint_unique_together.py @@ -4,14 +4,13 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('db', '0029_auto_20230502_0126'), + ("db", "0029_auto_20230502_0126"), ] operations = [ migrations.AlterUniqueTogether( - name='estimatepoint', + name="estimatepoint", unique_together=set(), ), ] diff --git a/apiserver/plane/db/migrations/0031_analyticview.py b/apiserver/plane/db/migrations/0031_analyticview.py index 7e02b78b2..f4520a8f5 100644 --- a/apiserver/plane/db/migrations/0031_analyticview.py +++ b/apiserver/plane/db/migrations/0031_analyticview.py @@ -7,31 +7,75 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0030_alter_estimatepoint_unique_together'), + ("db", "0030_alter_estimatepoint_unique_together"), ] operations = [ migrations.CreateModel( - name='AnalyticView', + name="AnalyticView", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255)), - ('description', models.TextField(blank=True)), - ('query', models.JSONField()), - ('query_dict', models.JSONField(default=dict)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='analyticview_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='analyticview_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='analytics', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("name", models.CharField(max_length=255)), + ("description", models.TextField(blank=True)), + ("query", models.JSONField()), + ("query_dict", models.JSONField(default=dict)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="analyticview_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="analyticview_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="analytics", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Analytic', - 'verbose_name_plural': 'Analytics', - 'db_table': 'analytic_views', - 'ordering': ('-created_at',), + "verbose_name": "Analytic", + "verbose_name_plural": "Analytics", + "db_table": "analytic_views", + "ordering": ("-created_at",), }, ), ] diff --git a/apiserver/plane/db/migrations/0032_auto_20230520_2015.py b/apiserver/plane/db/migrations/0032_auto_20230520_2015.py index 27c13537e..c781d298c 100644 --- a/apiserver/plane/db/migrations/0032_auto_20230520_2015.py +++ b/apiserver/plane/db/migrations/0032_auto_20230520_2015.py @@ -4,20 +4,19 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('db', '0031_analyticview'), + ("db", "0031_analyticview"), ] operations = [ migrations.RenameField( - model_name='project', - old_name='icon', - new_name='emoji', + model_name="project", + old_name="icon", + new_name="emoji", ), migrations.AddField( - model_name='project', - name='icon_prop', + model_name="project", + name="icon_prop", field=models.JSONField(null=True), ), ] diff --git a/apiserver/plane/db/migrations/0033_auto_20230618_2125.py b/apiserver/plane/db/migrations/0033_auto_20230618_2125.py index 8eb2eda62..1705aead6 100644 --- a/apiserver/plane/db/migrations/0033_auto_20230618_2125.py +++ b/apiserver/plane/db/migrations/0033_auto_20230618_2125.py @@ -7,77 +7,210 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0032_auto_20230520_2015'), + ("db", "0032_auto_20230520_2015"), ] operations = [ migrations.CreateModel( - name='Inbox', + name="Inbox", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255)), - ('description', models.TextField(blank=True, verbose_name='Inbox Description')), - ('is_default', models.BooleanField(default=False)), - ('view_props', models.JSONField(default=dict)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inbox_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("name", models.CharField(max_length=255)), + ( + "description", + models.TextField( + blank=True, verbose_name="Inbox Description" + ), + ), + ("is_default", models.BooleanField(default=False)), + ("view_props", models.JSONField(default=dict)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="inbox_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), ], options={ - 'verbose_name': 'Inbox', - 'verbose_name_plural': 'Inboxes', - 'db_table': 'inboxes', - 'ordering': ('name',), + "verbose_name": "Inbox", + "verbose_name_plural": "Inboxes", + "db_table": "inboxes", + "ordering": ("name",), }, ), migrations.AddField( - model_name='project', - name='inbox_view', + model_name="project", + name="inbox_view", field=models.BooleanField(default=False), ), migrations.CreateModel( - name='InboxIssue', + name="InboxIssue", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('status', models.IntegerField(choices=[(-2, 'Pending'), (-1, 'Rejected'), (0, 'Snoozed'), (1, 'Accepted'), (2, 'Duplicate')], default=-2)), - ('snoozed_till', models.DateTimeField(null=True)), - ('source', models.TextField(blank=True, null=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inboxissue_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('duplicate_to', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inbox_duplicate', to='db.issue')), - ('inbox', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_inbox', to='db.inbox')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_inbox', to='db.issue')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_inboxissue', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inboxissue_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_inboxissue', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "status", + models.IntegerField( + choices=[ + (-2, "Pending"), + (-1, "Rejected"), + (0, "Snoozed"), + (1, "Accepted"), + (2, "Duplicate"), + ], + default=-2, + ), + ), + ("snoozed_till", models.DateTimeField(null=True)), + ("source", models.TextField(blank=True, null=True)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="inboxissue_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "duplicate_to", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="inbox_duplicate", + to="db.issue", + ), + ), + ( + "inbox", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_inbox", + to="db.inbox", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_inbox", + to="db.issue", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_inboxissue", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="inboxissue_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_inboxissue", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'InboxIssue', - 'verbose_name_plural': 'InboxIssues', - 'db_table': 'inbox_issues', - 'ordering': ('-created_at',), + "verbose_name": "InboxIssue", + "verbose_name_plural": "InboxIssues", + "db_table": "inbox_issues", + "ordering": ("-created_at",), }, ), migrations.AddField( - model_name='inbox', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_inbox', to='db.project'), + model_name="inbox", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_inbox", + to="db.project", + ), ), migrations.AddField( - model_name='inbox', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inbox_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), + model_name="inbox", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="inbox_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), ), migrations.AddField( - model_name='inbox', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_inbox', to='db.workspace'), + model_name="inbox", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_inbox", + to="db.workspace", + ), ), migrations.AlterUniqueTogether( - name='inbox', - unique_together={('name', 'project')}, + name="inbox", + unique_together={("name", "project")}, ), ] diff --git a/apiserver/plane/db/migrations/0034_auto_20230628_1046.py b/apiserver/plane/db/migrations/0034_auto_20230628_1046.py index cdd722f59..dd6d21f6d 100644 --- a/apiserver/plane/db/migrations/0034_auto_20230628_1046.py +++ b/apiserver/plane/db/migrations/0034_auto_20230628_1046.py @@ -4,36 +4,35 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('db', '0033_auto_20230618_2125'), + ("db", "0033_auto_20230618_2125"), ] operations = [ migrations.RemoveField( - model_name='timelineissue', - name='created_by', + model_name="timelineissue", + name="created_by", ), migrations.RemoveField( - model_name='timelineissue', - name='issue', + model_name="timelineissue", + name="issue", ), migrations.RemoveField( - model_name='timelineissue', - name='project', + model_name="timelineissue", + name="project", ), migrations.RemoveField( - model_name='timelineissue', - name='updated_by', + model_name="timelineissue", + name="updated_by", ), migrations.RemoveField( - model_name='timelineissue', - name='workspace', + model_name="timelineissue", + name="workspace", ), migrations.DeleteModel( - name='Shortcut', + name="Shortcut", ), migrations.DeleteModel( - name='TimelineIssue', + name="TimelineIssue", ), ] diff --git a/apiserver/plane/db/migrations/0035_auto_20230704_2225.py b/apiserver/plane/db/migrations/0035_auto_20230704_2225.py index dec6265e6..806bfef51 100644 --- a/apiserver/plane/db/migrations/0035_auto_20230704_2225.py +++ b/apiserver/plane/db/migrations/0035_auto_20230704_2225.py @@ -10,7 +10,9 @@ def update_company_organization_size(apps, schema_editor): obj.organization_size = str(obj.company_size) updated_size.append(obj) - Model.objects.bulk_update(updated_size, ["organization_size"], batch_size=100) + Model.objects.bulk_update( + updated_size, ["organization_size"], batch_size=100 + ) class Migration(migrations.Migration): @@ -28,7 +30,9 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="workspace", name="name", - field=models.CharField(max_length=80, verbose_name="Workspace Name"), + field=models.CharField( + max_length=80, verbose_name="Workspace Name" + ), ), migrations.AlterField( model_name="workspace", diff --git a/apiserver/plane/db/migrations/0036_alter_workspace_organization_size.py b/apiserver/plane/db/migrations/0036_alter_workspace_organization_size.py index 0b182f50b..86748c778 100644 --- a/apiserver/plane/db/migrations/0036_alter_workspace_organization_size.py +++ b/apiserver/plane/db/migrations/0036_alter_workspace_organization_size.py @@ -4,15 +4,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('db', '0035_auto_20230704_2225'), + ("db", "0035_auto_20230704_2225"), ] operations = [ migrations.AlterField( - model_name='workspace', - name='organization_size', + model_name="workspace", + name="organization_size", field=models.CharField(max_length=20), ), ] diff --git a/apiserver/plane/db/migrations/0037_issue_archived_at_project_archive_in_and_more.py b/apiserver/plane/db/migrations/0037_issue_archived_at_project_archive_in_and_more.py index d11e1afd8..e659133d1 100644 --- a/apiserver/plane/db/migrations/0037_issue_archived_at_project_archive_in_and_more.py +++ b/apiserver/plane/db/migrations/0037_issue_archived_at_project_archive_in_and_more.py @@ -8,7 +8,6 @@ import plane.db.models.user import uuid - def onboarding_default_steps(apps, schema_editor): default_onboarding_schema = { "workspace_join": True, @@ -24,7 +23,9 @@ def onboarding_default_steps(apps, schema_editor): obj.is_tour_completed = True updated_user.append(obj) - Model.objects.bulk_update(updated_user, ["onboarding_step", "is_tour_completed"], batch_size=100) + Model.objects.bulk_update( + updated_user, ["onboarding_step", "is_tour_completed"], batch_size=100 + ) class Migration(migrations.Migration): @@ -78,7 +79,9 @@ class Migration(migrations.Migration): migrations.AddField( model_name="user", name="onboarding_step", - field=models.JSONField(default=plane.db.models.user.get_default_onboarding), + field=models.JSONField( + default=plane.db.models.user.get_default_onboarding + ), ), migrations.RunPython(onboarding_default_steps), migrations.CreateModel( @@ -86,7 +89,9 @@ class Migration(migrations.Migration): fields=[ ( "created_at", - models.DateTimeField(auto_now_add=True, verbose_name="Created At"), + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), ), ( "updated_at", @@ -110,7 +115,10 @@ class Migration(migrations.Migration): ("entity_name", models.CharField(max_length=255)), ("title", models.TextField()), ("message", models.JSONField(null=True)), - ("message_html", models.TextField(blank=True, default="

")), + ( + "message_html", + models.TextField(blank=True, default="

"), + ), ("message_stripped", models.TextField(blank=True, null=True)), ("sender", models.CharField(max_length=255)), ("read_at", models.DateTimeField(null=True)), @@ -183,7 +191,9 @@ class Migration(migrations.Migration): fields=[ ( "created_at", - models.DateTimeField(auto_now_add=True, verbose_name="Created At"), + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), ), ( "updated_at", diff --git a/apiserver/plane/db/migrations/0038_auto_20230720_1505.py b/apiserver/plane/db/migrations/0038_auto_20230720_1505.py index 1f5c63a89..53e50ed41 100644 --- a/apiserver/plane/db/migrations/0038_auto_20230720_1505.py +++ b/apiserver/plane/db/migrations/0038_auto_20230720_1505.py @@ -15,14 +15,12 @@ def restructure_theming(apps, schema_editor): "text": current_theme.get("textBase", ""), "sidebarText": current_theme.get("textBase", ""), "palette": f"""{current_theme.get("bgBase","")},{current_theme.get("textBase", "")},{current_theme.get("accent", "")},{current_theme.get("sidebar","")},{current_theme.get("textBase", "")}""", - "darkPalette": current_theme.get("darkPalette", "") + "darkPalette": current_theme.get("darkPalette", ""), } obj.theme = updated_theme updated_user.append(obj) - Model.objects.bulk_update( - updated_user, ["theme"], batch_size=100 - ) + Model.objects.bulk_update(updated_user, ["theme"], batch_size=100) class Migration(migrations.Migration): @@ -30,6 +28,4 @@ class Migration(migrations.Migration): ("db", "0037_issue_archived_at_project_archive_in_and_more"), ] - operations = [ - migrations.RunPython(restructure_theming) - ] + operations = [migrations.RunPython(restructure_theming)] diff --git a/apiserver/plane/db/migrations/0039_auto_20230723_2203.py b/apiserver/plane/db/migrations/0039_auto_20230723_2203.py index 5d5747543..26849d7f7 100644 --- a/apiserver/plane/db/migrations/0039_auto_20230723_2203.py +++ b/apiserver/plane/db/migrations/0039_auto_20230723_2203.py @@ -55,7 +55,9 @@ def update_workspace_member_props(apps, schema_editor): updated_workspace_member.append(obj) - Model.objects.bulk_update(updated_workspace_member, ["view_props"], batch_size=100) + Model.objects.bulk_update( + updated_workspace_member, ["view_props"], batch_size=100 + ) def update_project_member_sort_order(apps, schema_editor): @@ -67,7 +69,9 @@ def update_project_member_sort_order(apps, schema_editor): obj.sort_order = random.randint(1, 65536) updated_project_members.append(obj) - Model.objects.bulk_update(updated_project_members, ["sort_order"], batch_size=100) + Model.objects.bulk_update( + updated_project_members, ["sort_order"], batch_size=100 + ) class Migration(migrations.Migration): @@ -79,18 +83,22 @@ class Migration(migrations.Migration): migrations.RunPython(rename_field), migrations.RunPython(update_workspace_member_props), migrations.AlterField( - model_name='workspacemember', - name='view_props', - field=models.JSONField(default=plane.db.models.workspace.get_default_props), + model_name="workspacemember", + name="view_props", + field=models.JSONField( + default=plane.db.models.workspace.get_default_props + ), ), migrations.AddField( - model_name='workspacemember', - name='default_props', - field=models.JSONField(default=plane.db.models.workspace.get_default_props), + model_name="workspacemember", + name="default_props", + field=models.JSONField( + default=plane.db.models.workspace.get_default_props + ), ), migrations.AddField( - model_name='projectmember', - name='sort_order', + model_name="projectmember", + name="sort_order", field=models.FloatField(default=65535), ), migrations.RunPython(update_project_member_sort_order), diff --git a/apiserver/plane/db/migrations/0040_projectmember_preferences_user_cover_image_and_more.py b/apiserver/plane/db/migrations/0040_projectmember_preferences_user_cover_image_and_more.py index 5662ef666..76f8e6272 100644 --- a/apiserver/plane/db/migrations/0040_projectmember_preferences_user_cover_image_and_more.py +++ b/apiserver/plane/db/migrations/0040_projectmember_preferences_user_cover_image_and_more.py @@ -8,74 +8,209 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0039_auto_20230723_2203'), + ("db", "0039_auto_20230723_2203"), ] operations = [ migrations.AddField( - model_name='projectmember', - name='preferences', - field=models.JSONField(default=plane.db.models.project.get_default_preferences), + model_name="projectmember", + name="preferences", + field=models.JSONField( + default=plane.db.models.project.get_default_preferences + ), ), migrations.AddField( - model_name='user', - name='cover_image', + model_name="user", + name="cover_image", field=models.URLField(blank=True, max_length=800, null=True), ), migrations.CreateModel( - name='IssueReaction', + name="IssueReaction", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('reaction', models.CharField(max_length=20)), - ('actor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_reactions', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_reactions', to='db.issue')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("reaction", models.CharField(max_length=20)), + ( + "actor", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_reactions", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_reactions", + to="db.issue", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Reaction', - 'verbose_name_plural': 'Issue Reactions', - 'db_table': 'issue_reactions', - 'ordering': ('-created_at',), - 'unique_together': {('issue', 'actor', 'reaction')}, + "verbose_name": "Issue Reaction", + "verbose_name_plural": "Issue Reactions", + "db_table": "issue_reactions", + "ordering": ("-created_at",), + "unique_together": {("issue", "actor", "reaction")}, }, ), migrations.CreateModel( - name='CommentReaction', + name="CommentReaction", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('reaction', models.CharField(max_length=20)), - ('actor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comment_reactions', to=settings.AUTH_USER_MODEL)), - ('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comment_reactions', to='db.issuecomment')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("reaction", models.CharField(max_length=20)), + ( + "actor", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="comment_reactions", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "comment", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="comment_reactions", + to="db.issuecomment", + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Comment Reaction', - 'verbose_name_plural': 'Comment Reactions', - 'db_table': 'comment_reactions', - 'ordering': ('-created_at',), - 'unique_together': {('comment', 'actor', 'reaction')}, + "verbose_name": "Comment Reaction", + "verbose_name_plural": "Comment Reactions", + "db_table": "comment_reactions", + "ordering": ("-created_at",), + "unique_together": {("comment", "actor", "reaction")}, }, - ), - migrations.AlterField( - model_name='project', - name='identifier', - field=models.CharField(max_length=12, verbose_name='Project Identifier'), ), migrations.AlterField( - model_name='projectidentifier', - name='name', + model_name="project", + name="identifier", + field=models.CharField( + max_length=12, verbose_name="Project Identifier" + ), + ), + migrations.AlterField( + model_name="projectidentifier", + name="name", field=models.CharField(max_length=12), ), ] diff --git a/apiserver/plane/db/migrations/0041_cycle_sort_order_issuecomment_access_and_more.py b/apiserver/plane/db/migrations/0041_cycle_sort_order_issuecomment_access_and_more.py index 07c302c76..91119dbbd 100644 --- a/apiserver/plane/db/migrations/0041_cycle_sort_order_issuecomment_access_and_more.py +++ b/apiserver/plane/db/migrations/0041_cycle_sort_order_issuecomment_access_and_more.py @@ -10,6 +10,7 @@ import uuid import random import string + def generate_display_name(apps, schema_editor): UserModel = apps.get_model("db", "User") updated_users = [] @@ -20,7 +21,9 @@ def generate_display_name(apps, schema_editor): else "".join(random.choice(string.ascii_letters) for _ in range(6)) ) updated_users.append(obj) - UserModel.objects.bulk_update(updated_users, ["display_name"], batch_size=100) + UserModel.objects.bulk_update( + updated_users, ["display_name"], batch_size=100 + ) def rectify_field_issue_activity(apps, schema_editor): @@ -72,7 +75,13 @@ def update_assignee_issue_activity(apps, schema_editor): Model.objects.bulk_update( updated_activity, - ["old_value", "new_value", "old_identifier", "new_identifier", "comment"], + [ + "old_value", + "new_value", + "old_identifier", + "new_identifier", + "comment", + ], batch_size=200, ) @@ -93,7 +102,9 @@ def random_cycle_order(apps, schema_editor): for obj in CycleModel.objects.all(): obj.sort_order = random.randint(1, 65536) updated_cycles.append(obj) - CycleModel.objects.bulk_update(updated_cycles, ["sort_order"], batch_size=100) + CycleModel.objects.bulk_update( + updated_cycles, ["sort_order"], batch_size=100 + ) def random_module_order(apps, schema_editor): @@ -102,7 +113,9 @@ def random_module_order(apps, schema_editor): for obj in ModuleModel.objects.all(): obj.sort_order = random.randint(1, 65536) updated_modules.append(obj) - ModuleModel.objects.bulk_update(updated_modules, ["sort_order"], batch_size=100) + ModuleModel.objects.bulk_update( + updated_modules, ["sort_order"], batch_size=100 + ) def update_user_issue_properties(apps, schema_editor): @@ -125,111 +138,353 @@ def workspace_member_properties(apps, schema_editor): updated_workspace_members.append(obj) WorkspaceMemberModel.objects.bulk_update( - updated_workspace_members, ["view_props", "default_props"], batch_size=100 + updated_workspace_members, + ["view_props", "default_props"], + batch_size=100, ) -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('db', '0040_projectmember_preferences_user_cover_image_and_more'), + ("db", "0040_projectmember_preferences_user_cover_image_and_more"), ] operations = [ migrations.AddField( - model_name='cycle', - name='sort_order', + model_name="cycle", + name="sort_order", field=models.FloatField(default=65535), ), migrations.AddField( - model_name='issuecomment', - name='access', - field=models.CharField(choices=[('INTERNAL', 'INTERNAL'), ('EXTERNAL', 'EXTERNAL')], default='INTERNAL', max_length=100), + model_name="issuecomment", + name="access", + field=models.CharField( + choices=[("INTERNAL", "INTERNAL"), ("EXTERNAL", "EXTERNAL")], + default="INTERNAL", + max_length=100, + ), ), migrations.AddField( - model_name='module', - name='sort_order', + model_name="module", + name="sort_order", field=models.FloatField(default=65535), ), migrations.AddField( - model_name='user', - name='display_name', - field=models.CharField(default='', max_length=255), + model_name="user", + name="display_name", + field=models.CharField(default="", max_length=255), ), migrations.CreateModel( - name='ExporterHistory', + name="ExporterHistory", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('project', django.contrib.postgres.fields.ArrayField(base_field=models.UUIDField(default=uuid.uuid4), blank=True, null=True, size=None)), - ('provider', models.CharField(choices=[('json', 'json'), ('csv', 'csv'), ('xlsx', 'xlsx')], max_length=50)), - ('status', models.CharField(choices=[('queued', 'Queued'), ('processing', 'Processing'), ('completed', 'Completed'), ('failed', 'Failed')], default='queued', max_length=50)), - ('reason', models.TextField(blank=True)), - ('key', models.TextField(blank=True)), - ('url', models.URLField(blank=True, max_length=800, null=True)), - ('token', models.CharField(default=plane.db.models.exporter.generate_token, max_length=255, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('initiated_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_exporters', to=settings.AUTH_USER_MODEL)), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_exporters', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "project", + django.contrib.postgres.fields.ArrayField( + base_field=models.UUIDField(default=uuid.uuid4), + blank=True, + null=True, + size=None, + ), + ), + ( + "provider", + models.CharField( + choices=[ + ("json", "json"), + ("csv", "csv"), + ("xlsx", "xlsx"), + ], + max_length=50, + ), + ), + ( + "status", + models.CharField( + choices=[ + ("queued", "Queued"), + ("processing", "Processing"), + ("completed", "Completed"), + ("failed", "Failed"), + ], + default="queued", + max_length=50, + ), + ), + ("reason", models.TextField(blank=True)), + ("key", models.TextField(blank=True)), + ( + "url", + models.URLField(blank=True, max_length=800, null=True), + ), + ( + "token", + models.CharField( + default=plane.db.models.exporter.generate_token, + max_length=255, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "initiated_by", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_exporters", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_exporters", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Exporter', - 'verbose_name_plural': 'Exporters', - 'db_table': 'exporters', - 'ordering': ('-created_at',), + "verbose_name": "Exporter", + "verbose_name_plural": "Exporters", + "db_table": "exporters", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='ProjectDeployBoard', + name="ProjectDeployBoard", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('anchor', models.CharField(db_index=True, default=plane.db.models.project.get_anchor, max_length=255, unique=True)), - ('comments', models.BooleanField(default=False)), - ('reactions', models.BooleanField(default=False)), - ('votes', models.BooleanField(default=False)), - ('views', models.JSONField(default=plane.db.models.project.get_default_views)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('inbox', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bord_inbox', to='db.inbox')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "anchor", + models.CharField( + db_index=True, + default=plane.db.models.project.get_anchor, + max_length=255, + unique=True, + ), + ), + ("comments", models.BooleanField(default=False)), + ("reactions", models.BooleanField(default=False)), + ("votes", models.BooleanField(default=False)), + ( + "views", + models.JSONField( + default=plane.db.models.project.get_default_views + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "inbox", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="bord_inbox", + to="db.inbox", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Project Deploy Board', - 'verbose_name_plural': 'Project Deploy Boards', - 'db_table': 'project_deploy_boards', - 'ordering': ('-created_at',), - 'unique_together': {('project', 'anchor')}, + "verbose_name": "Project Deploy Board", + "verbose_name_plural": "Project Deploy Boards", + "db_table": "project_deploy_boards", + "ordering": ("-created_at",), + "unique_together": {("project", "anchor")}, }, ), migrations.CreateModel( - name='IssueVote', + name="IssueVote", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('vote', models.IntegerField(choices=[(-1, 'DOWNVOTE'), (1, 'UPVOTE')])), - ('actor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='votes', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='votes', to='db.issue')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "vote", + models.IntegerField( + choices=[(-1, "DOWNVOTE"), (1, "UPVOTE")] + ), + ), + ( + "actor", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="votes", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="votes", + to="db.issue", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Vote', - 'verbose_name_plural': 'Issue Votes', - 'db_table': 'issue_votes', - 'ordering': ('-created_at',), - 'unique_together': {('issue', 'actor')}, + "verbose_name": "Issue Vote", + "verbose_name_plural": "Issue Votes", + "db_table": "issue_votes", + "ordering": ("-created_at",), + "unique_together": {("issue", "actor")}, }, ), migrations.AlterField( - model_name='modulelink', - name='title', + model_name="modulelink", + name="title", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.RunPython(generate_display_name), diff --git a/apiserver/plane/db/migrations/0042_alter_analyticview_created_by_and_more.py b/apiserver/plane/db/migrations/0042_alter_analyticview_created_by_and_more.py index 01af46d20..f1fa99a36 100644 --- a/apiserver/plane/db/migrations/0042_alter_analyticview_created_by_and_more.py +++ b/apiserver/plane/db/migrations/0042_alter_analyticview_created_by_and_more.py @@ -5,56 +5,762 @@ from django.db import migrations, models import django.db.models.deletion import uuid + def update_user_timezones(apps, schema_editor): UserModel = apps.get_model("db", "User") updated_users = [] for obj in UserModel.objects.all(): obj.user_timezone = "UTC" updated_users.append(obj) - UserModel.objects.bulk_update(updated_users, ["user_timezone"], batch_size=100) + UserModel.objects.bulk_update( + updated_users, ["user_timezone"], batch_size=100 + ) class Migration(migrations.Migration): - dependencies = [ - ('db', '0041_cycle_sort_order_issuecomment_access_and_more'), + ("db", "0041_cycle_sort_order_issuecomment_access_and_more"), ] operations = [ migrations.AlterField( - model_name='user', - name='user_timezone', - field=models.CharField(choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Timbuktu', 'Africa/Timbuktu'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Atka', 'America/Atka'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Ciudad_Juarez', 'America/Ciudad_Juarez'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Ensenada', 'America/Ensenada'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fort_Wayne', 'America/Fort_Wayne'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Knox_IN', 'America/Knox_IN'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montreal', 'America/Montreal'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Nuuk', 'America/Nuuk'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Acre', 'America/Porto_Acre'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Rosario', 'America/Rosario'), ('America/Santa_Isabel', 'America/Santa_Isabel'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Shiprock', 'America/Shiprock'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Virgin', 'America/Virgin'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/South_Pole', 'Antarctica/South_Pole'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Ashkhabad', 'Asia/Ashkhabad'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Chongqing', 'Asia/Chongqing'), ('Asia/Chungking', 'Asia/Chungking'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Dacca', 'Asia/Dacca'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Harbin', 'Asia/Harbin'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Istanbul', 'Asia/Istanbul'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kashgar', 'Asia/Kashgar'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macao', 'Asia/Macao'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tel_Aviv', 'Asia/Tel_Aviv'), ('Asia/Thimbu', 'Asia/Thimbu'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ujung_Pandang', 'Asia/Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Ulan_Bator', 'Asia/Ulan_Bator'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Jan_Mayen', 'Atlantic/Jan_Mayen'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/ACT', 'Australia/ACT'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Canberra', 'Australia/Canberra'), ('Australia/Currie', 'Australia/Currie'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/LHI', 'Australia/LHI'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/NSW', 'Australia/NSW'), ('Australia/North', 'Australia/North'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Queensland', 'Australia/Queensland'), ('Australia/South', 'Australia/South'), ('Australia/Sydney', 'Australia/Sydney'), ('Australia/Tasmania', 'Australia/Tasmania'), ('Australia/Victoria', 'Australia/Victoria'), ('Australia/West', 'Australia/West'), ('Australia/Yancowinna', 'Australia/Yancowinna'), ('Brazil/Acre', 'Brazil/Acre'), ('Brazil/DeNoronha', 'Brazil/DeNoronha'), ('Brazil/East', 'Brazil/East'), ('Brazil/West', 'Brazil/West'), ('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Canada/Atlantic', 'Canada/Atlantic'), ('Canada/Central', 'Canada/Central'), ('Canada/Eastern', 'Canada/Eastern'), ('Canada/Mountain', 'Canada/Mountain'), ('Canada/Newfoundland', 'Canada/Newfoundland'), ('Canada/Pacific', 'Canada/Pacific'), ('Canada/Saskatchewan', 'Canada/Saskatchewan'), ('Canada/Yukon', 'Canada/Yukon'), ('Chile/Continental', 'Chile/Continental'), ('Chile/EasterIsland', 'Chile/EasterIsland'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('Etc/GMT', 'Etc/GMT'), ('Etc/GMT+0', 'Etc/GMT+0'), ('Etc/GMT+1', 'Etc/GMT+1'), ('Etc/GMT+10', 'Etc/GMT+10'), ('Etc/GMT+11', 'Etc/GMT+11'), ('Etc/GMT+12', 'Etc/GMT+12'), ('Etc/GMT+2', 'Etc/GMT+2'), ('Etc/GMT+3', 'Etc/GMT+3'), ('Etc/GMT+4', 'Etc/GMT+4'), ('Etc/GMT+5', 'Etc/GMT+5'), ('Etc/GMT+6', 'Etc/GMT+6'), ('Etc/GMT+7', 'Etc/GMT+7'), ('Etc/GMT+8', 'Etc/GMT+8'), ('Etc/GMT+9', 'Etc/GMT+9'), ('Etc/GMT-0', 'Etc/GMT-0'), ('Etc/GMT-1', 'Etc/GMT-1'), ('Etc/GMT-10', 'Etc/GMT-10'), ('Etc/GMT-11', 'Etc/GMT-11'), ('Etc/GMT-12', 'Etc/GMT-12'), ('Etc/GMT-13', 'Etc/GMT-13'), ('Etc/GMT-14', 'Etc/GMT-14'), ('Etc/GMT-2', 'Etc/GMT-2'), ('Etc/GMT-3', 'Etc/GMT-3'), ('Etc/GMT-4', 'Etc/GMT-4'), ('Etc/GMT-5', 'Etc/GMT-5'), ('Etc/GMT-6', 'Etc/GMT-6'), ('Etc/GMT-7', 'Etc/GMT-7'), ('Etc/GMT-8', 'Etc/GMT-8'), ('Etc/GMT-9', 'Etc/GMT-9'), ('Etc/GMT0', 'Etc/GMT0'), ('Etc/Greenwich', 'Etc/Greenwich'), ('Etc/UCT', 'Etc/UCT'), ('Etc/UTC', 'Etc/UTC'), ('Etc/Universal', 'Etc/Universal'), ('Etc/Zulu', 'Etc/Zulu'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belfast', 'Europe/Belfast'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Kyiv', 'Europe/Kyiv'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Nicosia', 'Europe/Nicosia'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Tiraspol', 'Europe/Tiraspol'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('GMT', 'GMT'), ('GMT+0', 'GMT+0'), ('GMT-0', 'GMT-0'), ('GMT0', 'GMT0'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('Mexico/BajaNorte', 'Mexico/BajaNorte'), ('Mexico/BajaSur', 'Mexico/BajaSur'), ('Mexico/General', 'Mexico/General'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Johnston', 'Pacific/Johnston'), ('Pacific/Kanton', 'Pacific/Kanton'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Samoa', 'Pacific/Samoa'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('Pacific/Yap', 'Pacific/Yap'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('US/Alaska', 'US/Alaska'), ('US/Aleutian', 'US/Aleutian'), ('US/Arizona', 'US/Arizona'), ('US/Central', 'US/Central'), ('US/East-Indiana', 'US/East-Indiana'), ('US/Eastern', 'US/Eastern'), ('US/Hawaii', 'US/Hawaii'), ('US/Indiana-Starke', 'US/Indiana-Starke'), ('US/Michigan', 'US/Michigan'), ('US/Mountain', 'US/Mountain'), ('US/Pacific', 'US/Pacific'), ('US/Samoa', 'US/Samoa'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')], default='UTC', max_length=255), + model_name="user", + name="user_timezone", + field=models.CharField( + choices=[ + ("Africa/Abidjan", "Africa/Abidjan"), + ("Africa/Accra", "Africa/Accra"), + ("Africa/Addis_Ababa", "Africa/Addis_Ababa"), + ("Africa/Algiers", "Africa/Algiers"), + ("Africa/Asmara", "Africa/Asmara"), + ("Africa/Asmera", "Africa/Asmera"), + ("Africa/Bamako", "Africa/Bamako"), + ("Africa/Bangui", "Africa/Bangui"), + ("Africa/Banjul", "Africa/Banjul"), + ("Africa/Bissau", "Africa/Bissau"), + ("Africa/Blantyre", "Africa/Blantyre"), + ("Africa/Brazzaville", "Africa/Brazzaville"), + ("Africa/Bujumbura", "Africa/Bujumbura"), + ("Africa/Cairo", "Africa/Cairo"), + ("Africa/Casablanca", "Africa/Casablanca"), + ("Africa/Ceuta", "Africa/Ceuta"), + ("Africa/Conakry", "Africa/Conakry"), + ("Africa/Dakar", "Africa/Dakar"), + ("Africa/Dar_es_Salaam", "Africa/Dar_es_Salaam"), + ("Africa/Djibouti", "Africa/Djibouti"), + ("Africa/Douala", "Africa/Douala"), + ("Africa/El_Aaiun", "Africa/El_Aaiun"), + ("Africa/Freetown", "Africa/Freetown"), + ("Africa/Gaborone", "Africa/Gaborone"), + ("Africa/Harare", "Africa/Harare"), + ("Africa/Johannesburg", "Africa/Johannesburg"), + ("Africa/Juba", "Africa/Juba"), + ("Africa/Kampala", "Africa/Kampala"), + ("Africa/Khartoum", "Africa/Khartoum"), + ("Africa/Kigali", "Africa/Kigali"), + ("Africa/Kinshasa", "Africa/Kinshasa"), + ("Africa/Lagos", "Africa/Lagos"), + ("Africa/Libreville", "Africa/Libreville"), + ("Africa/Lome", "Africa/Lome"), + ("Africa/Luanda", "Africa/Luanda"), + ("Africa/Lubumbashi", "Africa/Lubumbashi"), + ("Africa/Lusaka", "Africa/Lusaka"), + ("Africa/Malabo", "Africa/Malabo"), + ("Africa/Maputo", "Africa/Maputo"), + ("Africa/Maseru", "Africa/Maseru"), + ("Africa/Mbabane", "Africa/Mbabane"), + ("Africa/Mogadishu", "Africa/Mogadishu"), + ("Africa/Monrovia", "Africa/Monrovia"), + ("Africa/Nairobi", "Africa/Nairobi"), + ("Africa/Ndjamena", "Africa/Ndjamena"), + ("Africa/Niamey", "Africa/Niamey"), + ("Africa/Nouakchott", "Africa/Nouakchott"), + ("Africa/Ouagadougou", "Africa/Ouagadougou"), + ("Africa/Porto-Novo", "Africa/Porto-Novo"), + ("Africa/Sao_Tome", "Africa/Sao_Tome"), + ("Africa/Timbuktu", "Africa/Timbuktu"), + ("Africa/Tripoli", "Africa/Tripoli"), + ("Africa/Tunis", "Africa/Tunis"), + ("Africa/Windhoek", "Africa/Windhoek"), + ("America/Adak", "America/Adak"), + ("America/Anchorage", "America/Anchorage"), + ("America/Anguilla", "America/Anguilla"), + ("America/Antigua", "America/Antigua"), + ("America/Araguaina", "America/Araguaina"), + ( + "America/Argentina/Buenos_Aires", + "America/Argentina/Buenos_Aires", + ), + ( + "America/Argentina/Catamarca", + "America/Argentina/Catamarca", + ), + ( + "America/Argentina/ComodRivadavia", + "America/Argentina/ComodRivadavia", + ), + ("America/Argentina/Cordoba", "America/Argentina/Cordoba"), + ("America/Argentina/Jujuy", "America/Argentina/Jujuy"), + ( + "America/Argentina/La_Rioja", + "America/Argentina/La_Rioja", + ), + ("America/Argentina/Mendoza", "America/Argentina/Mendoza"), + ( + "America/Argentina/Rio_Gallegos", + "America/Argentina/Rio_Gallegos", + ), + ("America/Argentina/Salta", "America/Argentina/Salta"), + ( + "America/Argentina/San_Juan", + "America/Argentina/San_Juan", + ), + ( + "America/Argentina/San_Luis", + "America/Argentina/San_Luis", + ), + ("America/Argentina/Tucuman", "America/Argentina/Tucuman"), + ("America/Argentina/Ushuaia", "America/Argentina/Ushuaia"), + ("America/Aruba", "America/Aruba"), + ("America/Asuncion", "America/Asuncion"), + ("America/Atikokan", "America/Atikokan"), + ("America/Atka", "America/Atka"), + ("America/Bahia", "America/Bahia"), + ("America/Bahia_Banderas", "America/Bahia_Banderas"), + ("America/Barbados", "America/Barbados"), + ("America/Belem", "America/Belem"), + ("America/Belize", "America/Belize"), + ("America/Blanc-Sablon", "America/Blanc-Sablon"), + ("America/Boa_Vista", "America/Boa_Vista"), + ("America/Bogota", "America/Bogota"), + ("America/Boise", "America/Boise"), + ("America/Buenos_Aires", "America/Buenos_Aires"), + ("America/Cambridge_Bay", "America/Cambridge_Bay"), + ("America/Campo_Grande", "America/Campo_Grande"), + ("America/Cancun", "America/Cancun"), + ("America/Caracas", "America/Caracas"), + ("America/Catamarca", "America/Catamarca"), + ("America/Cayenne", "America/Cayenne"), + ("America/Cayman", "America/Cayman"), + ("America/Chicago", "America/Chicago"), + ("America/Chihuahua", "America/Chihuahua"), + ("America/Ciudad_Juarez", "America/Ciudad_Juarez"), + ("America/Coral_Harbour", "America/Coral_Harbour"), + ("America/Cordoba", "America/Cordoba"), + ("America/Costa_Rica", "America/Costa_Rica"), + ("America/Creston", "America/Creston"), + ("America/Cuiaba", "America/Cuiaba"), + ("America/Curacao", "America/Curacao"), + ("America/Danmarkshavn", "America/Danmarkshavn"), + ("America/Dawson", "America/Dawson"), + ("America/Dawson_Creek", "America/Dawson_Creek"), + ("America/Denver", "America/Denver"), + ("America/Detroit", "America/Detroit"), + ("America/Dominica", "America/Dominica"), + ("America/Edmonton", "America/Edmonton"), + ("America/Eirunepe", "America/Eirunepe"), + ("America/El_Salvador", "America/El_Salvador"), + ("America/Ensenada", "America/Ensenada"), + ("America/Fort_Nelson", "America/Fort_Nelson"), + ("America/Fort_Wayne", "America/Fort_Wayne"), + ("America/Fortaleza", "America/Fortaleza"), + ("America/Glace_Bay", "America/Glace_Bay"), + ("America/Godthab", "America/Godthab"), + ("America/Goose_Bay", "America/Goose_Bay"), + ("America/Grand_Turk", "America/Grand_Turk"), + ("America/Grenada", "America/Grenada"), + ("America/Guadeloupe", "America/Guadeloupe"), + ("America/Guatemala", "America/Guatemala"), + ("America/Guayaquil", "America/Guayaquil"), + ("America/Guyana", "America/Guyana"), + ("America/Halifax", "America/Halifax"), + ("America/Havana", "America/Havana"), + ("America/Hermosillo", "America/Hermosillo"), + ( + "America/Indiana/Indianapolis", + "America/Indiana/Indianapolis", + ), + ("America/Indiana/Knox", "America/Indiana/Knox"), + ("America/Indiana/Marengo", "America/Indiana/Marengo"), + ( + "America/Indiana/Petersburg", + "America/Indiana/Petersburg", + ), + ("America/Indiana/Tell_City", "America/Indiana/Tell_City"), + ("America/Indiana/Vevay", "America/Indiana/Vevay"), + ("America/Indiana/Vincennes", "America/Indiana/Vincennes"), + ("America/Indiana/Winamac", "America/Indiana/Winamac"), + ("America/Indianapolis", "America/Indianapolis"), + ("America/Inuvik", "America/Inuvik"), + ("America/Iqaluit", "America/Iqaluit"), + ("America/Jamaica", "America/Jamaica"), + ("America/Jujuy", "America/Jujuy"), + ("America/Juneau", "America/Juneau"), + ( + "America/Kentucky/Louisville", + "America/Kentucky/Louisville", + ), + ( + "America/Kentucky/Monticello", + "America/Kentucky/Monticello", + ), + ("America/Knox_IN", "America/Knox_IN"), + ("America/Kralendijk", "America/Kralendijk"), + ("America/La_Paz", "America/La_Paz"), + ("America/Lima", "America/Lima"), + ("America/Los_Angeles", "America/Los_Angeles"), + ("America/Louisville", "America/Louisville"), + ("America/Lower_Princes", "America/Lower_Princes"), + ("America/Maceio", "America/Maceio"), + ("America/Managua", "America/Managua"), + ("America/Manaus", "America/Manaus"), + ("America/Marigot", "America/Marigot"), + ("America/Martinique", "America/Martinique"), + ("America/Matamoros", "America/Matamoros"), + ("America/Mazatlan", "America/Mazatlan"), + ("America/Mendoza", "America/Mendoza"), + ("America/Menominee", "America/Menominee"), + ("America/Merida", "America/Merida"), + ("America/Metlakatla", "America/Metlakatla"), + ("America/Mexico_City", "America/Mexico_City"), + ("America/Miquelon", "America/Miquelon"), + ("America/Moncton", "America/Moncton"), + ("America/Monterrey", "America/Monterrey"), + ("America/Montevideo", "America/Montevideo"), + ("America/Montreal", "America/Montreal"), + ("America/Montserrat", "America/Montserrat"), + ("America/Nassau", "America/Nassau"), + ("America/New_York", "America/New_York"), + ("America/Nipigon", "America/Nipigon"), + ("America/Nome", "America/Nome"), + ("America/Noronha", "America/Noronha"), + ( + "America/North_Dakota/Beulah", + "America/North_Dakota/Beulah", + ), + ( + "America/North_Dakota/Center", + "America/North_Dakota/Center", + ), + ( + "America/North_Dakota/New_Salem", + "America/North_Dakota/New_Salem", + ), + ("America/Nuuk", "America/Nuuk"), + ("America/Ojinaga", "America/Ojinaga"), + ("America/Panama", "America/Panama"), + ("America/Pangnirtung", "America/Pangnirtung"), + ("America/Paramaribo", "America/Paramaribo"), + ("America/Phoenix", "America/Phoenix"), + ("America/Port-au-Prince", "America/Port-au-Prince"), + ("America/Port_of_Spain", "America/Port_of_Spain"), + ("America/Porto_Acre", "America/Porto_Acre"), + ("America/Porto_Velho", "America/Porto_Velho"), + ("America/Puerto_Rico", "America/Puerto_Rico"), + ("America/Punta_Arenas", "America/Punta_Arenas"), + ("America/Rainy_River", "America/Rainy_River"), + ("America/Rankin_Inlet", "America/Rankin_Inlet"), + ("America/Recife", "America/Recife"), + ("America/Regina", "America/Regina"), + ("America/Resolute", "America/Resolute"), + ("America/Rio_Branco", "America/Rio_Branco"), + ("America/Rosario", "America/Rosario"), + ("America/Santa_Isabel", "America/Santa_Isabel"), + ("America/Santarem", "America/Santarem"), + ("America/Santiago", "America/Santiago"), + ("America/Santo_Domingo", "America/Santo_Domingo"), + ("America/Sao_Paulo", "America/Sao_Paulo"), + ("America/Scoresbysund", "America/Scoresbysund"), + ("America/Shiprock", "America/Shiprock"), + ("America/Sitka", "America/Sitka"), + ("America/St_Barthelemy", "America/St_Barthelemy"), + ("America/St_Johns", "America/St_Johns"), + ("America/St_Kitts", "America/St_Kitts"), + ("America/St_Lucia", "America/St_Lucia"), + ("America/St_Thomas", "America/St_Thomas"), + ("America/St_Vincent", "America/St_Vincent"), + ("America/Swift_Current", "America/Swift_Current"), + ("America/Tegucigalpa", "America/Tegucigalpa"), + ("America/Thule", "America/Thule"), + ("America/Thunder_Bay", "America/Thunder_Bay"), + ("America/Tijuana", "America/Tijuana"), + ("America/Toronto", "America/Toronto"), + ("America/Tortola", "America/Tortola"), + ("America/Vancouver", "America/Vancouver"), + ("America/Virgin", "America/Virgin"), + ("America/Whitehorse", "America/Whitehorse"), + ("America/Winnipeg", "America/Winnipeg"), + ("America/Yakutat", "America/Yakutat"), + ("America/Yellowknife", "America/Yellowknife"), + ("Antarctica/Casey", "Antarctica/Casey"), + ("Antarctica/Davis", "Antarctica/Davis"), + ("Antarctica/DumontDUrville", "Antarctica/DumontDUrville"), + ("Antarctica/Macquarie", "Antarctica/Macquarie"), + ("Antarctica/Mawson", "Antarctica/Mawson"), + ("Antarctica/McMurdo", "Antarctica/McMurdo"), + ("Antarctica/Palmer", "Antarctica/Palmer"), + ("Antarctica/Rothera", "Antarctica/Rothera"), + ("Antarctica/South_Pole", "Antarctica/South_Pole"), + ("Antarctica/Syowa", "Antarctica/Syowa"), + ("Antarctica/Troll", "Antarctica/Troll"), + ("Antarctica/Vostok", "Antarctica/Vostok"), + ("Arctic/Longyearbyen", "Arctic/Longyearbyen"), + ("Asia/Aden", "Asia/Aden"), + ("Asia/Almaty", "Asia/Almaty"), + ("Asia/Amman", "Asia/Amman"), + ("Asia/Anadyr", "Asia/Anadyr"), + ("Asia/Aqtau", "Asia/Aqtau"), + ("Asia/Aqtobe", "Asia/Aqtobe"), + ("Asia/Ashgabat", "Asia/Ashgabat"), + ("Asia/Ashkhabad", "Asia/Ashkhabad"), + ("Asia/Atyrau", "Asia/Atyrau"), + ("Asia/Baghdad", "Asia/Baghdad"), + ("Asia/Bahrain", "Asia/Bahrain"), + ("Asia/Baku", "Asia/Baku"), + ("Asia/Bangkok", "Asia/Bangkok"), + ("Asia/Barnaul", "Asia/Barnaul"), + ("Asia/Beirut", "Asia/Beirut"), + ("Asia/Bishkek", "Asia/Bishkek"), + ("Asia/Brunei", "Asia/Brunei"), + ("Asia/Calcutta", "Asia/Calcutta"), + ("Asia/Chita", "Asia/Chita"), + ("Asia/Choibalsan", "Asia/Choibalsan"), + ("Asia/Chongqing", "Asia/Chongqing"), + ("Asia/Chungking", "Asia/Chungking"), + ("Asia/Colombo", "Asia/Colombo"), + ("Asia/Dacca", "Asia/Dacca"), + ("Asia/Damascus", "Asia/Damascus"), + ("Asia/Dhaka", "Asia/Dhaka"), + ("Asia/Dili", "Asia/Dili"), + ("Asia/Dubai", "Asia/Dubai"), + ("Asia/Dushanbe", "Asia/Dushanbe"), + ("Asia/Famagusta", "Asia/Famagusta"), + ("Asia/Gaza", "Asia/Gaza"), + ("Asia/Harbin", "Asia/Harbin"), + ("Asia/Hebron", "Asia/Hebron"), + ("Asia/Ho_Chi_Minh", "Asia/Ho_Chi_Minh"), + ("Asia/Hong_Kong", "Asia/Hong_Kong"), + ("Asia/Hovd", "Asia/Hovd"), + ("Asia/Irkutsk", "Asia/Irkutsk"), + ("Asia/Istanbul", "Asia/Istanbul"), + ("Asia/Jakarta", "Asia/Jakarta"), + ("Asia/Jayapura", "Asia/Jayapura"), + ("Asia/Jerusalem", "Asia/Jerusalem"), + ("Asia/Kabul", "Asia/Kabul"), + ("Asia/Kamchatka", "Asia/Kamchatka"), + ("Asia/Karachi", "Asia/Karachi"), + ("Asia/Kashgar", "Asia/Kashgar"), + ("Asia/Kathmandu", "Asia/Kathmandu"), + ("Asia/Katmandu", "Asia/Katmandu"), + ("Asia/Khandyga", "Asia/Khandyga"), + ("Asia/Kolkata", "Asia/Kolkata"), + ("Asia/Krasnoyarsk", "Asia/Krasnoyarsk"), + ("Asia/Kuala_Lumpur", "Asia/Kuala_Lumpur"), + ("Asia/Kuching", "Asia/Kuching"), + ("Asia/Kuwait", "Asia/Kuwait"), + ("Asia/Macao", "Asia/Macao"), + ("Asia/Macau", "Asia/Macau"), + ("Asia/Magadan", "Asia/Magadan"), + ("Asia/Makassar", "Asia/Makassar"), + ("Asia/Manila", "Asia/Manila"), + ("Asia/Muscat", "Asia/Muscat"), + ("Asia/Nicosia", "Asia/Nicosia"), + ("Asia/Novokuznetsk", "Asia/Novokuznetsk"), + ("Asia/Novosibirsk", "Asia/Novosibirsk"), + ("Asia/Omsk", "Asia/Omsk"), + ("Asia/Oral", "Asia/Oral"), + ("Asia/Phnom_Penh", "Asia/Phnom_Penh"), + ("Asia/Pontianak", "Asia/Pontianak"), + ("Asia/Pyongyang", "Asia/Pyongyang"), + ("Asia/Qatar", "Asia/Qatar"), + ("Asia/Qostanay", "Asia/Qostanay"), + ("Asia/Qyzylorda", "Asia/Qyzylorda"), + ("Asia/Rangoon", "Asia/Rangoon"), + ("Asia/Riyadh", "Asia/Riyadh"), + ("Asia/Saigon", "Asia/Saigon"), + ("Asia/Sakhalin", "Asia/Sakhalin"), + ("Asia/Samarkand", "Asia/Samarkand"), + ("Asia/Seoul", "Asia/Seoul"), + ("Asia/Shanghai", "Asia/Shanghai"), + ("Asia/Singapore", "Asia/Singapore"), + ("Asia/Srednekolymsk", "Asia/Srednekolymsk"), + ("Asia/Taipei", "Asia/Taipei"), + ("Asia/Tashkent", "Asia/Tashkent"), + ("Asia/Tbilisi", "Asia/Tbilisi"), + ("Asia/Tehran", "Asia/Tehran"), + ("Asia/Tel_Aviv", "Asia/Tel_Aviv"), + ("Asia/Thimbu", "Asia/Thimbu"), + ("Asia/Thimphu", "Asia/Thimphu"), + ("Asia/Tokyo", "Asia/Tokyo"), + ("Asia/Tomsk", "Asia/Tomsk"), + ("Asia/Ujung_Pandang", "Asia/Ujung_Pandang"), + ("Asia/Ulaanbaatar", "Asia/Ulaanbaatar"), + ("Asia/Ulan_Bator", "Asia/Ulan_Bator"), + ("Asia/Urumqi", "Asia/Urumqi"), + ("Asia/Ust-Nera", "Asia/Ust-Nera"), + ("Asia/Vientiane", "Asia/Vientiane"), + ("Asia/Vladivostok", "Asia/Vladivostok"), + ("Asia/Yakutsk", "Asia/Yakutsk"), + ("Asia/Yangon", "Asia/Yangon"), + ("Asia/Yekaterinburg", "Asia/Yekaterinburg"), + ("Asia/Yerevan", "Asia/Yerevan"), + ("Atlantic/Azores", "Atlantic/Azores"), + ("Atlantic/Bermuda", "Atlantic/Bermuda"), + ("Atlantic/Canary", "Atlantic/Canary"), + ("Atlantic/Cape_Verde", "Atlantic/Cape_Verde"), + ("Atlantic/Faeroe", "Atlantic/Faeroe"), + ("Atlantic/Faroe", "Atlantic/Faroe"), + ("Atlantic/Jan_Mayen", "Atlantic/Jan_Mayen"), + ("Atlantic/Madeira", "Atlantic/Madeira"), + ("Atlantic/Reykjavik", "Atlantic/Reykjavik"), + ("Atlantic/South_Georgia", "Atlantic/South_Georgia"), + ("Atlantic/St_Helena", "Atlantic/St_Helena"), + ("Atlantic/Stanley", "Atlantic/Stanley"), + ("Australia/ACT", "Australia/ACT"), + ("Australia/Adelaide", "Australia/Adelaide"), + ("Australia/Brisbane", "Australia/Brisbane"), + ("Australia/Broken_Hill", "Australia/Broken_Hill"), + ("Australia/Canberra", "Australia/Canberra"), + ("Australia/Currie", "Australia/Currie"), + ("Australia/Darwin", "Australia/Darwin"), + ("Australia/Eucla", "Australia/Eucla"), + ("Australia/Hobart", "Australia/Hobart"), + ("Australia/LHI", "Australia/LHI"), + ("Australia/Lindeman", "Australia/Lindeman"), + ("Australia/Lord_Howe", "Australia/Lord_Howe"), + ("Australia/Melbourne", "Australia/Melbourne"), + ("Australia/NSW", "Australia/NSW"), + ("Australia/North", "Australia/North"), + ("Australia/Perth", "Australia/Perth"), + ("Australia/Queensland", "Australia/Queensland"), + ("Australia/South", "Australia/South"), + ("Australia/Sydney", "Australia/Sydney"), + ("Australia/Tasmania", "Australia/Tasmania"), + ("Australia/Victoria", "Australia/Victoria"), + ("Australia/West", "Australia/West"), + ("Australia/Yancowinna", "Australia/Yancowinna"), + ("Brazil/Acre", "Brazil/Acre"), + ("Brazil/DeNoronha", "Brazil/DeNoronha"), + ("Brazil/East", "Brazil/East"), + ("Brazil/West", "Brazil/West"), + ("CET", "CET"), + ("CST6CDT", "CST6CDT"), + ("Canada/Atlantic", "Canada/Atlantic"), + ("Canada/Central", "Canada/Central"), + ("Canada/Eastern", "Canada/Eastern"), + ("Canada/Mountain", "Canada/Mountain"), + ("Canada/Newfoundland", "Canada/Newfoundland"), + ("Canada/Pacific", "Canada/Pacific"), + ("Canada/Saskatchewan", "Canada/Saskatchewan"), + ("Canada/Yukon", "Canada/Yukon"), + ("Chile/Continental", "Chile/Continental"), + ("Chile/EasterIsland", "Chile/EasterIsland"), + ("Cuba", "Cuba"), + ("EET", "EET"), + ("EST", "EST"), + ("EST5EDT", "EST5EDT"), + ("Egypt", "Egypt"), + ("Eire", "Eire"), + ("Etc/GMT", "Etc/GMT"), + ("Etc/GMT+0", "Etc/GMT+0"), + ("Etc/GMT+1", "Etc/GMT+1"), + ("Etc/GMT+10", "Etc/GMT+10"), + ("Etc/GMT+11", "Etc/GMT+11"), + ("Etc/GMT+12", "Etc/GMT+12"), + ("Etc/GMT+2", "Etc/GMT+2"), + ("Etc/GMT+3", "Etc/GMT+3"), + ("Etc/GMT+4", "Etc/GMT+4"), + ("Etc/GMT+5", "Etc/GMT+5"), + ("Etc/GMT+6", "Etc/GMT+6"), + ("Etc/GMT+7", "Etc/GMT+7"), + ("Etc/GMT+8", "Etc/GMT+8"), + ("Etc/GMT+9", "Etc/GMT+9"), + ("Etc/GMT-0", "Etc/GMT-0"), + ("Etc/GMT-1", "Etc/GMT-1"), + ("Etc/GMT-10", "Etc/GMT-10"), + ("Etc/GMT-11", "Etc/GMT-11"), + ("Etc/GMT-12", "Etc/GMT-12"), + ("Etc/GMT-13", "Etc/GMT-13"), + ("Etc/GMT-14", "Etc/GMT-14"), + ("Etc/GMT-2", "Etc/GMT-2"), + ("Etc/GMT-3", "Etc/GMT-3"), + ("Etc/GMT-4", "Etc/GMT-4"), + ("Etc/GMT-5", "Etc/GMT-5"), + ("Etc/GMT-6", "Etc/GMT-6"), + ("Etc/GMT-7", "Etc/GMT-7"), + ("Etc/GMT-8", "Etc/GMT-8"), + ("Etc/GMT-9", "Etc/GMT-9"), + ("Etc/GMT0", "Etc/GMT0"), + ("Etc/Greenwich", "Etc/Greenwich"), + ("Etc/UCT", "Etc/UCT"), + ("Etc/UTC", "Etc/UTC"), + ("Etc/Universal", "Etc/Universal"), + ("Etc/Zulu", "Etc/Zulu"), + ("Europe/Amsterdam", "Europe/Amsterdam"), + ("Europe/Andorra", "Europe/Andorra"), + ("Europe/Astrakhan", "Europe/Astrakhan"), + ("Europe/Athens", "Europe/Athens"), + ("Europe/Belfast", "Europe/Belfast"), + ("Europe/Belgrade", "Europe/Belgrade"), + ("Europe/Berlin", "Europe/Berlin"), + ("Europe/Bratislava", "Europe/Bratislava"), + ("Europe/Brussels", "Europe/Brussels"), + ("Europe/Bucharest", "Europe/Bucharest"), + ("Europe/Budapest", "Europe/Budapest"), + ("Europe/Busingen", "Europe/Busingen"), + ("Europe/Chisinau", "Europe/Chisinau"), + ("Europe/Copenhagen", "Europe/Copenhagen"), + ("Europe/Dublin", "Europe/Dublin"), + ("Europe/Gibraltar", "Europe/Gibraltar"), + ("Europe/Guernsey", "Europe/Guernsey"), + ("Europe/Helsinki", "Europe/Helsinki"), + ("Europe/Isle_of_Man", "Europe/Isle_of_Man"), + ("Europe/Istanbul", "Europe/Istanbul"), + ("Europe/Jersey", "Europe/Jersey"), + ("Europe/Kaliningrad", "Europe/Kaliningrad"), + ("Europe/Kiev", "Europe/Kiev"), + ("Europe/Kirov", "Europe/Kirov"), + ("Europe/Kyiv", "Europe/Kyiv"), + ("Europe/Lisbon", "Europe/Lisbon"), + ("Europe/Ljubljana", "Europe/Ljubljana"), + ("Europe/London", "Europe/London"), + ("Europe/Luxembourg", "Europe/Luxembourg"), + ("Europe/Madrid", "Europe/Madrid"), + ("Europe/Malta", "Europe/Malta"), + ("Europe/Mariehamn", "Europe/Mariehamn"), + ("Europe/Minsk", "Europe/Minsk"), + ("Europe/Monaco", "Europe/Monaco"), + ("Europe/Moscow", "Europe/Moscow"), + ("Europe/Nicosia", "Europe/Nicosia"), + ("Europe/Oslo", "Europe/Oslo"), + ("Europe/Paris", "Europe/Paris"), + ("Europe/Podgorica", "Europe/Podgorica"), + ("Europe/Prague", "Europe/Prague"), + ("Europe/Riga", "Europe/Riga"), + ("Europe/Rome", "Europe/Rome"), + ("Europe/Samara", "Europe/Samara"), + ("Europe/San_Marino", "Europe/San_Marino"), + ("Europe/Sarajevo", "Europe/Sarajevo"), + ("Europe/Saratov", "Europe/Saratov"), + ("Europe/Simferopol", "Europe/Simferopol"), + ("Europe/Skopje", "Europe/Skopje"), + ("Europe/Sofia", "Europe/Sofia"), + ("Europe/Stockholm", "Europe/Stockholm"), + ("Europe/Tallinn", "Europe/Tallinn"), + ("Europe/Tirane", "Europe/Tirane"), + ("Europe/Tiraspol", "Europe/Tiraspol"), + ("Europe/Ulyanovsk", "Europe/Ulyanovsk"), + ("Europe/Uzhgorod", "Europe/Uzhgorod"), + ("Europe/Vaduz", "Europe/Vaduz"), + ("Europe/Vatican", "Europe/Vatican"), + ("Europe/Vienna", "Europe/Vienna"), + ("Europe/Vilnius", "Europe/Vilnius"), + ("Europe/Volgograd", "Europe/Volgograd"), + ("Europe/Warsaw", "Europe/Warsaw"), + ("Europe/Zagreb", "Europe/Zagreb"), + ("Europe/Zaporozhye", "Europe/Zaporozhye"), + ("Europe/Zurich", "Europe/Zurich"), + ("GB", "GB"), + ("GB-Eire", "GB-Eire"), + ("GMT", "GMT"), + ("GMT+0", "GMT+0"), + ("GMT-0", "GMT-0"), + ("GMT0", "GMT0"), + ("Greenwich", "Greenwich"), + ("HST", "HST"), + ("Hongkong", "Hongkong"), + ("Iceland", "Iceland"), + ("Indian/Antananarivo", "Indian/Antananarivo"), + ("Indian/Chagos", "Indian/Chagos"), + ("Indian/Christmas", "Indian/Christmas"), + ("Indian/Cocos", "Indian/Cocos"), + ("Indian/Comoro", "Indian/Comoro"), + ("Indian/Kerguelen", "Indian/Kerguelen"), + ("Indian/Mahe", "Indian/Mahe"), + ("Indian/Maldives", "Indian/Maldives"), + ("Indian/Mauritius", "Indian/Mauritius"), + ("Indian/Mayotte", "Indian/Mayotte"), + ("Indian/Reunion", "Indian/Reunion"), + ("Iran", "Iran"), + ("Israel", "Israel"), + ("Jamaica", "Jamaica"), + ("Japan", "Japan"), + ("Kwajalein", "Kwajalein"), + ("Libya", "Libya"), + ("MET", "MET"), + ("MST", "MST"), + ("MST7MDT", "MST7MDT"), + ("Mexico/BajaNorte", "Mexico/BajaNorte"), + ("Mexico/BajaSur", "Mexico/BajaSur"), + ("Mexico/General", "Mexico/General"), + ("NZ", "NZ"), + ("NZ-CHAT", "NZ-CHAT"), + ("Navajo", "Navajo"), + ("PRC", "PRC"), + ("PST8PDT", "PST8PDT"), + ("Pacific/Apia", "Pacific/Apia"), + ("Pacific/Auckland", "Pacific/Auckland"), + ("Pacific/Bougainville", "Pacific/Bougainville"), + ("Pacific/Chatham", "Pacific/Chatham"), + ("Pacific/Chuuk", "Pacific/Chuuk"), + ("Pacific/Easter", "Pacific/Easter"), + ("Pacific/Efate", "Pacific/Efate"), + ("Pacific/Enderbury", "Pacific/Enderbury"), + ("Pacific/Fakaofo", "Pacific/Fakaofo"), + ("Pacific/Fiji", "Pacific/Fiji"), + ("Pacific/Funafuti", "Pacific/Funafuti"), + ("Pacific/Galapagos", "Pacific/Galapagos"), + ("Pacific/Gambier", "Pacific/Gambier"), + ("Pacific/Guadalcanal", "Pacific/Guadalcanal"), + ("Pacific/Guam", "Pacific/Guam"), + ("Pacific/Honolulu", "Pacific/Honolulu"), + ("Pacific/Johnston", "Pacific/Johnston"), + ("Pacific/Kanton", "Pacific/Kanton"), + ("Pacific/Kiritimati", "Pacific/Kiritimati"), + ("Pacific/Kosrae", "Pacific/Kosrae"), + ("Pacific/Kwajalein", "Pacific/Kwajalein"), + ("Pacific/Majuro", "Pacific/Majuro"), + ("Pacific/Marquesas", "Pacific/Marquesas"), + ("Pacific/Midway", "Pacific/Midway"), + ("Pacific/Nauru", "Pacific/Nauru"), + ("Pacific/Niue", "Pacific/Niue"), + ("Pacific/Norfolk", "Pacific/Norfolk"), + ("Pacific/Noumea", "Pacific/Noumea"), + ("Pacific/Pago_Pago", "Pacific/Pago_Pago"), + ("Pacific/Palau", "Pacific/Palau"), + ("Pacific/Pitcairn", "Pacific/Pitcairn"), + ("Pacific/Pohnpei", "Pacific/Pohnpei"), + ("Pacific/Ponape", "Pacific/Ponape"), + ("Pacific/Port_Moresby", "Pacific/Port_Moresby"), + ("Pacific/Rarotonga", "Pacific/Rarotonga"), + ("Pacific/Saipan", "Pacific/Saipan"), + ("Pacific/Samoa", "Pacific/Samoa"), + ("Pacific/Tahiti", "Pacific/Tahiti"), + ("Pacific/Tarawa", "Pacific/Tarawa"), + ("Pacific/Tongatapu", "Pacific/Tongatapu"), + ("Pacific/Truk", "Pacific/Truk"), + ("Pacific/Wake", "Pacific/Wake"), + ("Pacific/Wallis", "Pacific/Wallis"), + ("Pacific/Yap", "Pacific/Yap"), + ("Poland", "Poland"), + ("Portugal", "Portugal"), + ("ROC", "ROC"), + ("ROK", "ROK"), + ("Singapore", "Singapore"), + ("Turkey", "Turkey"), + ("UCT", "UCT"), + ("US/Alaska", "US/Alaska"), + ("US/Aleutian", "US/Aleutian"), + ("US/Arizona", "US/Arizona"), + ("US/Central", "US/Central"), + ("US/East-Indiana", "US/East-Indiana"), + ("US/Eastern", "US/Eastern"), + ("US/Hawaii", "US/Hawaii"), + ("US/Indiana-Starke", "US/Indiana-Starke"), + ("US/Michigan", "US/Michigan"), + ("US/Mountain", "US/Mountain"), + ("US/Pacific", "US/Pacific"), + ("US/Samoa", "US/Samoa"), + ("UTC", "UTC"), + ("Universal", "Universal"), + ("W-SU", "W-SU"), + ("WET", "WET"), + ("Zulu", "Zulu"), + ], + default="UTC", + max_length=255, + ), ), migrations.AlterField( - model_name='issuelink', - name='title', + model_name="issuelink", + name="title", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.RunPython(update_user_timezones), migrations.AlterField( - model_name='issuevote', - name='vote', - field=models.IntegerField(choices=[(-1, 'DOWNVOTE'), (1, 'UPVOTE')], default=1), + model_name="issuevote", + name="vote", + field=models.IntegerField( + choices=[(-1, "DOWNVOTE"), (1, "UPVOTE")], default=1 + ), ), migrations.CreateModel( - name='ProjectPublicMember', + name="ProjectPublicMember", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='public_project_members', to=settings.AUTH_USER_MODEL)), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "member", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="public_project_members", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Project Public Member', - 'verbose_name_plural': 'Project Public Members', - 'db_table': 'project_public_members', - 'ordering': ('-created_at',), - 'unique_together': {('project', 'member')}, + "verbose_name": "Project Public Member", + "verbose_name_plural": "Project Public Members", + "db_table": "project_public_members", + "ordering": ("-created_at",), + "unique_together": {("project", "member")}, }, ), ] diff --git a/apiserver/plane/db/migrations/0043_alter_analyticview_created_by_and_more.py b/apiserver/plane/db/migrations/0043_alter_analyticview_created_by_and_more.py index 5a806c704..81d91bb78 100644 --- a/apiserver/plane/db/migrations/0043_alter_analyticview_created_by_and_more.py +++ b/apiserver/plane/db/migrations/0043_alter_analyticview_created_by_and_more.py @@ -24,7 +24,9 @@ def create_issue_relation(apps, schema_editor): updated_by_id=blocked_issue.updated_by_id, ) ) - IssueRelation.objects.bulk_create(updated_issue_relation, batch_size=100) + IssueRelation.objects.bulk_create( + updated_issue_relation, batch_size=100 + ) except Exception as e: print(e) capture_exception(e) @@ -36,47 +38,137 @@ def update_issue_priority_choice(apps, schema_editor): for obj in IssueModel.objects.filter(priority=None): obj.priority = "none" updated_issues.append(obj) - IssueModel.objects.bulk_update(updated_issues, ["priority"], batch_size=100) + IssueModel.objects.bulk_update( + updated_issues, ["priority"], batch_size=100 + ) class Migration(migrations.Migration): - dependencies = [ - ('db', '0042_alter_analyticview_created_by_and_more'), + ("db", "0042_alter_analyticview_created_by_and_more"), ] operations = [ migrations.CreateModel( - name='IssueRelation', + name="IssueRelation", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('relation_type', models.CharField(choices=[('duplicate', 'Duplicate'), ('relates_to', 'Relates To'), ('blocked_by', 'Blocked By')], default='blocked_by', max_length=20, verbose_name='Issue Relation Type')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_relation', to='db.issue')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project')), - ('related_issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_related', to='db.issue')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "relation_type", + models.CharField( + choices=[ + ("duplicate", "Duplicate"), + ("relates_to", "Relates To"), + ("blocked_by", "Blocked By"), + ], + default="blocked_by", + max_length=20, + verbose_name="Issue Relation Type", + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_relation", + to="db.issue", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + ( + "related_issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_related", + to="db.issue", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Relation', - 'verbose_name_plural': 'Issue Relations', - 'db_table': 'issue_relations', - 'ordering': ('-created_at',), - 'unique_together': {('issue', 'related_issue')}, + "verbose_name": "Issue Relation", + "verbose_name_plural": "Issue Relations", + "db_table": "issue_relations", + "ordering": ("-created_at",), + "unique_together": {("issue", "related_issue")}, }, ), migrations.AddField( - model_name='issue', - name='is_draft', + model_name="issue", + name="is_draft", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='issue', - name='priority', - field=models.CharField(choices=[('urgent', 'Urgent'), ('high', 'High'), ('medium', 'Medium'), ('low', 'Low'), ('none', 'None')], default='none', max_length=30, verbose_name='Issue Priority'), + model_name="issue", + name="priority", + field=models.CharField( + choices=[ + ("urgent", "Urgent"), + ("high", "High"), + ("medium", "Medium"), + ("low", "Low"), + ("none", "None"), + ], + default="none", + max_length=30, + verbose_name="Issue Priority", + ), ), migrations.RunPython(create_issue_relation), migrations.RunPython(update_issue_priority_choice), diff --git a/apiserver/plane/db/migrations/0044_auto_20230913_0709.py b/apiserver/plane/db/migrations/0044_auto_20230913_0709.py index 19a1449af..d42b3431e 100644 --- a/apiserver/plane/db/migrations/0044_auto_20230913_0709.py +++ b/apiserver/plane/db/migrations/0044_auto_20230913_0709.py @@ -8,12 +8,16 @@ def workspace_member_props(old_props): "filters": { "priority": old_props.get("filters", {}).get("priority", None), "state": old_props.get("filters", {}).get("state", None), - "state_group": old_props.get("filters", {}).get("state_group", None), + "state_group": old_props.get("filters", {}).get( + "state_group", None + ), "assignees": old_props.get("filters", {}).get("assignees", None), "created_by": old_props.get("filters", {}).get("created_by", None), "labels": old_props.get("filters", {}).get("labels", None), "start_date": old_props.get("filters", {}).get("start_date", None), - "target_date": old_props.get("filters", {}).get("target_date", None), + "target_date": old_props.get("filters", {}).get( + "target_date", None + ), "subscriber": old_props.get("filters", {}).get("subscriber", None), }, "display_filters": { @@ -27,18 +31,28 @@ def workspace_member_props(old_props): }, "display_properties": { "assignee": old_props.get("properties", {}).get("assignee", True), - "attachment_count": old_props.get("properties", {}).get("attachment_count", True), - "created_on": old_props.get("properties", {}).get("created_on", True), + "attachment_count": old_props.get("properties", {}).get( + "attachment_count", True + ), + "created_on": old_props.get("properties", {}).get( + "created_on", True + ), "due_date": old_props.get("properties", {}).get("due_date", True), "estimate": old_props.get("properties", {}).get("estimate", True), "key": old_props.get("properties", {}).get("key", True), "labels": old_props.get("properties", {}).get("labels", True), "link": old_props.get("properties", {}).get("link", True), "priority": old_props.get("properties", {}).get("priority", True), - "start_date": old_props.get("properties", {}).get("start_date", True), + "start_date": old_props.get("properties", {}).get( + "start_date", True + ), "state": old_props.get("properties", {}).get("state", True), - "sub_issue_count": old_props.get("properties", {}).get("sub_issue_count", True), - "updated_on": old_props.get("properties", {}).get("updated_on", True), + "sub_issue_count": old_props.get("properties", {}).get( + "sub_issue_count", True + ), + "updated_on": old_props.get("properties", {}).get( + "updated_on", True + ), }, } return new_props @@ -49,12 +63,16 @@ def project_member_props(old_props): "filters": { "priority": old_props.get("filters", {}).get("priority", None), "state": old_props.get("filters", {}).get("state", None), - "state_group": old_props.get("filters", {}).get("state_group", None), + "state_group": old_props.get("filters", {}).get( + "state_group", None + ), "assignees": old_props.get("filters", {}).get("assignees", None), "created_by": old_props.get("filters", {}).get("created_by", None), "labels": old_props.get("filters", {}).get("labels", None), "start_date": old_props.get("filters", {}).get("start_date", None), - "target_date": old_props.get("filters", {}).get("target_date", None), + "target_date": old_props.get("filters", {}).get( + "target_date", None + ), "subscriber": old_props.get("filters", {}).get("subscriber", None), }, "display_filters": { @@ -75,59 +93,75 @@ def cycle_module_props(old_props): "filters": { "priority": old_props.get("filters", {}).get("priority", None), "state": old_props.get("filters", {}).get("state", None), - "state_group": old_props.get("filters", {}).get("state_group", None), + "state_group": old_props.get("filters", {}).get( + "state_group", None + ), "assignees": old_props.get("filters", {}).get("assignees", None), "created_by": old_props.get("filters", {}).get("created_by", None), "labels": old_props.get("filters", {}).get("labels", None), "start_date": old_props.get("filters", {}).get("start_date", None), - "target_date": old_props.get("filters", {}).get("target_date", None), + "target_date": old_props.get("filters", {}).get( + "target_date", None + ), "subscriber": old_props.get("filters", {}).get("subscriber", None), }, } return new_props - + def update_workspace_member_view_props(apps, schema_editor): WorkspaceMemberModel = apps.get_model("db", "WorkspaceMember") updated_workspace_member = [] for obj in WorkspaceMemberModel.objects.all(): - obj.view_props = workspace_member_props(obj.view_props) - obj.default_props = workspace_member_props(obj.default_props) - updated_workspace_member.append(obj) - WorkspaceMemberModel.objects.bulk_update(updated_workspace_member, ["view_props", "default_props"], batch_size=100) + obj.view_props = workspace_member_props(obj.view_props) + obj.default_props = workspace_member_props(obj.default_props) + updated_workspace_member.append(obj) + WorkspaceMemberModel.objects.bulk_update( + updated_workspace_member, + ["view_props", "default_props"], + batch_size=100, + ) + def update_project_member_view_props(apps, schema_editor): ProjectMemberModel = apps.get_model("db", "ProjectMember") updated_project_member = [] for obj in ProjectMemberModel.objects.all(): - obj.view_props = project_member_props(obj.view_props) - obj.default_props = project_member_props(obj.default_props) - updated_project_member.append(obj) - ProjectMemberModel.objects.bulk_update(updated_project_member, ["view_props", "default_props"], batch_size=100) + obj.view_props = project_member_props(obj.view_props) + obj.default_props = project_member_props(obj.default_props) + updated_project_member.append(obj) + ProjectMemberModel.objects.bulk_update( + updated_project_member, ["view_props", "default_props"], batch_size=100 + ) + def update_cycle_props(apps, schema_editor): CycleModel = apps.get_model("db", "Cycle") updated_cycle = [] for obj in CycleModel.objects.all(): - if "filter" in obj.view_props: - obj.view_props = cycle_module_props(obj.view_props) - updated_cycle.append(obj) - CycleModel.objects.bulk_update(updated_cycle, ["view_props"], batch_size=100) + if "filter" in obj.view_props: + obj.view_props = cycle_module_props(obj.view_props) + updated_cycle.append(obj) + CycleModel.objects.bulk_update( + updated_cycle, ["view_props"], batch_size=100 + ) + def update_module_props(apps, schema_editor): ModuleModel = apps.get_model("db", "Module") updated_module = [] for obj in ModuleModel.objects.all(): - if "filter" in obj.view_props: - obj.view_props = cycle_module_props(obj.view_props) - updated_module.append(obj) - ModuleModel.objects.bulk_update(updated_module, ["view_props"], batch_size=100) + if "filter" in obj.view_props: + obj.view_props = cycle_module_props(obj.view_props) + updated_module.append(obj) + ModuleModel.objects.bulk_update( + updated_module, ["view_props"], batch_size=100 + ) class Migration(migrations.Migration): - dependencies = [ - ('db', '0043_alter_analyticview_created_by_and_more'), + ("db", "0043_alter_analyticview_created_by_and_more"), ] operations = [ diff --git a/apiserver/plane/db/migrations/0045_issueactivity_epoch_workspacemember_issue_props_and_more.py b/apiserver/plane/db/migrations/0045_issueactivity_epoch_workspacemember_issue_props_and_more.py index 4b9c1b1eb..9ac528829 100644 --- a/apiserver/plane/db/migrations/0045_issueactivity_epoch_workspacemember_issue_props_and_more.py +++ b/apiserver/plane/db/migrations/0045_issueactivity_epoch_workspacemember_issue_props_and_more.py @@ -21,6 +21,7 @@ def update_issue_activity_priority(apps, schema_editor): batch_size=2000, ) + def update_issue_activity_blocked(apps, schema_editor): IssueActivity = apps.get_model("db", "IssueActivity") updated_issue_activity = [] @@ -34,44 +35,104 @@ def update_issue_activity_blocked(apps, schema_editor): batch_size=1000, ) -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('db', '0044_auto_20230913_0709'), + ("db", "0044_auto_20230913_0709"), ] operations = [ migrations.CreateModel( - name='GlobalView', + name="GlobalView", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('name', models.CharField(max_length=255, verbose_name='View Name')), - ('description', models.TextField(blank=True, verbose_name='View Description')), - ('query', models.JSONField(verbose_name='View Query')), - ('access', models.PositiveSmallIntegerField(choices=[(0, 'Private'), (1, 'Public')], default=1)), - ('query_data', models.JSONField(default=dict)), - ('sort_order', models.FloatField(default=65535)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='global_views', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "name", + models.CharField(max_length=255, verbose_name="View Name"), + ), + ( + "description", + models.TextField( + blank=True, verbose_name="View Description" + ), + ), + ("query", models.JSONField(verbose_name="View Query")), + ( + "access", + models.PositiveSmallIntegerField( + choices=[(0, "Private"), (1, "Public")], default=1 + ), + ), + ("query_data", models.JSONField(default=dict)), + ("sort_order", models.FloatField(default=65535)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="global_views", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Global View', - 'verbose_name_plural': 'Global Views', - 'db_table': 'global_views', - 'ordering': ('-created_at',), + "verbose_name": "Global View", + "verbose_name_plural": "Global Views", + "db_table": "global_views", + "ordering": ("-created_at",), }, ), migrations.AddField( - model_name='workspacemember', - name='issue_props', - field=models.JSONField(default=plane.db.models.workspace.get_issue_props), + model_name="workspacemember", + name="issue_props", + field=models.JSONField( + default=plane.db.models.workspace.get_issue_props + ), ), migrations.AddField( - model_name='issueactivity', - name='epoch', + model_name="issueactivity", + name="epoch", field=models.FloatField(null=True), ), migrations.RunPython(update_issue_activity_priority), diff --git a/apiserver/plane/db/migrations/0046_label_sort_order_alter_analyticview_created_by_and_more.py b/apiserver/plane/db/migrations/0046_label_sort_order_alter_analyticview_created_by_and_more.py index f02660e1d..be58c8f5f 100644 --- a/apiserver/plane/db/migrations/0046_label_sort_order_alter_analyticview_created_by_and_more.py +++ b/apiserver/plane/db/migrations/0046_label_sort_order_alter_analyticview_created_by_and_more.py @@ -7,977 +7,2001 @@ import plane.db.models.issue import uuid import random + def random_sort_ordering(apps, schema_editor): Label = apps.get_model("db", "Label") bulk_labels = [] for label in Label.objects.all(): - label.sort_order = random.randint(0,65535) + label.sort_order = random.randint(0, 65535) bulk_labels.append(label) Label.objects.bulk_update(bulk_labels, ["sort_order"], batch_size=1000) -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('db', '0045_issueactivity_epoch_workspacemember_issue_props_and_more'), + ( + "db", + "0045_issueactivity_epoch_workspacemember_issue_props_and_more", + ), ] operations = [ migrations.AddField( - model_name='label', - name='sort_order', + model_name="label", + name="sort_order", field=models.FloatField(default=65535), ), migrations.AlterField( - model_name='analyticview', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='analyticview', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='apitoken', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='apitoken', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='cycle', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='cycle', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='cycle', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='cycle', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='cyclefavorite', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='cyclefavorite', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='cyclefavorite', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='cyclefavorite', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='cycleissue', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='cycleissue', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='cycleissue', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='cycleissue', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='estimate', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='estimate', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='estimate', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='estimate', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='estimatepoint', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='estimatepoint', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='estimatepoint', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='estimatepoint', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='fileasset', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='fileasset', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='githubcommentsync', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='githubcommentsync', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='githubcommentsync', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='githubcommentsync', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='githubissuesync', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='githubissuesync', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='githubissuesync', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='githubissuesync', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='githubrepository', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='githubrepository', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='githubrepository', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='githubrepository', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='githubrepositorysync', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='githubrepositorysync', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='githubrepositorysync', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='githubrepositorysync', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='importer', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='importer', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='importer', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='importer', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='inbox', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='inbox', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='inbox', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='inbox', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='inboxissue', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='inboxissue', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='inboxissue', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='inboxissue', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='integration', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='integration', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issue', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='issue', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='issue', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issue', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='issueactivity', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='issueactivity', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='issueactivity', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issueactivity', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='issueassignee', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='issueassignee', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='issueassignee', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issueassignee', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='issueattachment', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='issueattachment', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='issueattachment', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issueattachment', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='issueblocker', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='issueblocker', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='issueblocker', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issueblocker', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='issuecomment', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='issuecomment', - name='issue', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_comments', to='db.issue'), - ), - migrations.AlterField( - model_name='issuecomment', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='issuecomment', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issuecomment', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='issuelabel', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='issuelabel', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='issuelabel', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issuelabel', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='issuelink', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='issuelink', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='issuelink', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issuelink', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='issueproperty', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='issueproperty', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='issueproperty', - name='properties', - field=models.JSONField(default=plane.db.models.issue.get_default_properties), - ), - migrations.AlterField( - model_name='issueproperty', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issueproperty', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='issuesequence', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='issuesequence', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='issuesequence', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issuesequence', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='issueview', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='issueview', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='issueview', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issueview', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='issueviewfavorite', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='issueviewfavorite', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='issueviewfavorite', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='issueviewfavorite', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='label', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='label', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='label', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='label', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='module', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='module', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='module', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='module', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='modulefavorite', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='modulefavorite', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='modulefavorite', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='modulefavorite', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='moduleissue', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='moduleissue', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='moduleissue', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='moduleissue', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='modulelink', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='modulelink', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='modulelink', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='modulelink', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='modulemember', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='modulemember', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='modulemember', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='modulemember', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='page', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='page', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='page', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='page', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='pageblock', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='pageblock', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='pageblock', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='pageblock', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='pagefavorite', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='pagefavorite', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='pagefavorite', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='pagefavorite', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='pagelabel', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='pagelabel', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='pagelabel', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='pagelabel', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='project', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='project', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='projectfavorite', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='projectfavorite', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='projectfavorite', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='projectfavorite', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='projectidentifier', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='projectidentifier', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='projectmember', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='projectmember', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='projectmember', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='projectmember', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='projectmemberinvite', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='projectmemberinvite', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='projectmemberinvite', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='projectmemberinvite', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='slackprojectsync', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='slackprojectsync', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='slackprojectsync', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='slackprojectsync', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='socialloginconnection', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='socialloginconnection', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='state', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='state', - name='project', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), - ), - migrations.AlterField( - model_name='state', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='state', - name='workspace', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace'), - ), - migrations.AlterField( - model_name='team', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='team', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='teammember', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='teammember', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='workspace', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='workspace', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='workspaceintegration', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='workspaceintegration', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='workspacemember', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='workspacemember', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='workspacememberinvite', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='workspacememberinvite', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), - ), - migrations.AlterField( - model_name='workspacetheme', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='workspacetheme', - name='updated_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By'), + model_name="analyticview", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="analyticview", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="apitoken", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="apitoken", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="cycle", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="cycle", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="cycle", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="cycle", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="cyclefavorite", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="cyclefavorite", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="cyclefavorite", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="cyclefavorite", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="cycleissue", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="cycleissue", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="cycleissue", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="cycleissue", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="estimate", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="estimate", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="estimate", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="estimate", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="estimatepoint", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="estimatepoint", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="estimatepoint", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="estimatepoint", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="fileasset", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="fileasset", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="githubcommentsync", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="githubcommentsync", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="githubcommentsync", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="githubcommentsync", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="githubissuesync", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="githubissuesync", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="githubissuesync", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="githubissuesync", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="githubrepository", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="githubrepository", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="githubrepository", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="githubrepository", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="githubrepositorysync", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="githubrepositorysync", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="githubrepositorysync", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="githubrepositorysync", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="importer", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="importer", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="importer", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="importer", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="inbox", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="inbox", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="inbox", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="inbox", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="inboxissue", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="inboxissue", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="inboxissue", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="inboxissue", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="integration", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="integration", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issue", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="issue", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="issue", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issue", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="issueactivity", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="issueactivity", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="issueactivity", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issueactivity", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="issueassignee", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="issueassignee", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="issueassignee", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issueassignee", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="issueattachment", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="issueattachment", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="issueattachment", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issueattachment", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="issueblocker", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="issueblocker", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="issueblocker", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issueblocker", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="issuecomment", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="issuecomment", + name="issue", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_comments", + to="db.issue", + ), + ), + migrations.AlterField( + model_name="issuecomment", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="issuecomment", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issuecomment", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="issuelabel", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="issuelabel", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="issuelabel", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issuelabel", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="issuelink", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="issuelink", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="issuelink", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issuelink", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="issueproperty", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="issueproperty", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="issueproperty", + name="properties", + field=models.JSONField( + default=plane.db.models.issue.get_default_properties + ), + ), + migrations.AlterField( + model_name="issueproperty", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issueproperty", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="issuesequence", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="issuesequence", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="issuesequence", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issuesequence", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="issueview", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="issueview", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="issueview", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issueview", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="issueviewfavorite", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="issueviewfavorite", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="issueviewfavorite", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="issueviewfavorite", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="label", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="label", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="label", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="label", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="module", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="module", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="module", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="module", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="modulefavorite", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="modulefavorite", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="modulefavorite", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="modulefavorite", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="moduleissue", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="moduleissue", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="moduleissue", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="moduleissue", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="modulelink", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="modulelink", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="modulelink", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="modulelink", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="modulemember", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="modulemember", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="modulemember", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="modulemember", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="page", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="page", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="page", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="page", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="pageblock", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="pageblock", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="pageblock", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="pageblock", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="pagefavorite", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="pagefavorite", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="pagefavorite", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="pagefavorite", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="pagelabel", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="pagelabel", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="pagelabel", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="pagelabel", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="project", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="project", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="projectfavorite", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="projectfavorite", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="projectfavorite", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="projectfavorite", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="projectidentifier", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="projectidentifier", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="projectmember", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="projectmember", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="projectmember", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="projectmember", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="projectmemberinvite", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="projectmemberinvite", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="projectmemberinvite", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="projectmemberinvite", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="slackprojectsync", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="slackprojectsync", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="slackprojectsync", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="slackprojectsync", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="socialloginconnection", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="socialloginconnection", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="state", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="state", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + migrations.AlterField( + model_name="state", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="state", + name="workspace", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), + migrations.AlterField( + model_name="team", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="team", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="teammember", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="teammember", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="workspace", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="workspace", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="workspaceintegration", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="workspaceintegration", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="workspacemember", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="workspacemember", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="workspacememberinvite", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="workspacememberinvite", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + migrations.AlterField( + model_name="workspacetheme", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + migrations.AlterField( + model_name="workspacetheme", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), ), migrations.CreateModel( - name='IssueMention', + name="IssueMention", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_mention', to='db.issue')), - ('mention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_mention', to=settings.AUTH_USER_MODEL)), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "issue", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_mention", + to="db.issue", + ), + ), + ( + "mention", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_mention", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Issue Mention', - 'verbose_name_plural': 'Issue Mentions', - 'db_table': 'issue_mentions', - 'ordering': ('-created_at',), - 'unique_together': {('issue', 'mention')}, + "verbose_name": "Issue Mention", + "verbose_name_plural": "Issue Mentions", + "db_table": "issue_mentions", + "ordering": ("-created_at",), + "unique_together": {("issue", "mention")}, }, ), migrations.RunPython(random_sort_ordering), diff --git a/apiserver/plane/db/migrations/0047_webhook_apitoken_description_apitoken_expired_at_and_more.py b/apiserver/plane/db/migrations/0047_webhook_apitoken_description_apitoken_expired_at_and_more.py index d44f760d0..f0a52a355 100644 --- a/apiserver/plane/db/migrations/0047_webhook_apitoken_description_apitoken_expired_at_and_more.py +++ b/apiserver/plane/db/migrations/0047_webhook_apitoken_description_apitoken_expired_at_and_more.py @@ -9,123 +9,288 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0046_label_sort_order_alter_analyticview_created_by_and_more'), + ("db", "0046_label_sort_order_alter_analyticview_created_by_and_more"), ] operations = [ migrations.CreateModel( - name='Webhook', + name="Webhook", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('url', models.URLField(validators=[plane.db.models.webhook.validate_schema, plane.db.models.webhook.validate_domain])), - ('is_active', models.BooleanField(default=True)), - ('secret_key', models.CharField(default=plane.db.models.webhook.generate_token, max_length=255)), - ('project', models.BooleanField(default=False)), - ('issue', models.BooleanField(default=False)), - ('module', models.BooleanField(default=False)), - ('cycle', models.BooleanField(default=False)), - ('issue_comment', models.BooleanField(default=False)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_webhooks', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "url", + models.URLField( + validators=[ + plane.db.models.webhook.validate_schema, + plane.db.models.webhook.validate_domain, + ] + ), + ), + ("is_active", models.BooleanField(default=True)), + ( + "secret_key", + models.CharField( + default=plane.db.models.webhook.generate_token, + max_length=255, + ), + ), + ("project", models.BooleanField(default=False)), + ("issue", models.BooleanField(default=False)), + ("module", models.BooleanField(default=False)), + ("cycle", models.BooleanField(default=False)), + ("issue_comment", models.BooleanField(default=False)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_webhooks", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Webhook', - 'verbose_name_plural': 'Webhooks', - 'db_table': 'webhooks', - 'ordering': ('-created_at',), - 'unique_together': {('workspace', 'url')}, + "verbose_name": "Webhook", + "verbose_name_plural": "Webhooks", + "db_table": "webhooks", + "ordering": ("-created_at",), + "unique_together": {("workspace", "url")}, }, ), migrations.AddField( - model_name='apitoken', - name='description', + model_name="apitoken", + name="description", field=models.TextField(blank=True), ), migrations.AddField( - model_name='apitoken', - name='expired_at', + model_name="apitoken", + name="expired_at", field=models.DateTimeField(blank=True, null=True), ), migrations.AddField( - model_name='apitoken', - name='is_active', + model_name="apitoken", + name="is_active", field=models.BooleanField(default=True), ), migrations.AddField( - model_name='apitoken', - name='last_used', + model_name="apitoken", + name="last_used", field=models.DateTimeField(null=True), ), migrations.AddField( - model_name='projectmember', - name='is_active', + model_name="projectmember", + name="is_active", field=models.BooleanField(default=True), ), migrations.AddField( - model_name='workspacemember', - name='is_active', + model_name="workspacemember", + name="is_active", field=models.BooleanField(default=True), ), migrations.AlterField( - model_name='apitoken', - name='token', - field=models.CharField(db_index=True, default=plane.db.models.api.generate_token, max_length=255, unique=True), + model_name="apitoken", + name="token", + field=models.CharField( + db_index=True, + default=plane.db.models.api.generate_token, + max_length=255, + unique=True, + ), ), migrations.CreateModel( - name='WebhookLog', + name="WebhookLog", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('event_type', models.CharField(blank=True, max_length=255, null=True)), - ('request_method', models.CharField(blank=True, max_length=10, null=True)), - ('request_headers', models.TextField(blank=True, null=True)), - ('request_body', models.TextField(blank=True, null=True)), - ('response_status', models.TextField(blank=True, null=True)), - ('response_headers', models.TextField(blank=True, null=True)), - ('response_body', models.TextField(blank=True, null=True)), - ('retry_count', models.PositiveSmallIntegerField(default=0)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('webhook', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='db.webhook')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='webhook_logs', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "event_type", + models.CharField(blank=True, max_length=255, null=True), + ), + ( + "request_method", + models.CharField(blank=True, max_length=10, null=True), + ), + ("request_headers", models.TextField(blank=True, null=True)), + ("request_body", models.TextField(blank=True, null=True)), + ("response_status", models.TextField(blank=True, null=True)), + ("response_headers", models.TextField(blank=True, null=True)), + ("response_body", models.TextField(blank=True, null=True)), + ("retry_count", models.PositiveSmallIntegerField(default=0)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "webhook", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="logs", + to="db.webhook", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="webhook_logs", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Webhook Log', - 'verbose_name_plural': 'Webhook Logs', - 'db_table': 'webhook_logs', - 'ordering': ('-created_at',), + "verbose_name": "Webhook Log", + "verbose_name_plural": "Webhook Logs", + "db_table": "webhook_logs", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='APIActivityLog', + name="APIActivityLog", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('token_identifier', models.CharField(max_length=255)), - ('path', models.CharField(max_length=255)), - ('method', models.CharField(max_length=10)), - ('query_params', models.TextField(blank=True, null=True)), - ('headers', models.TextField(blank=True, null=True)), - ('body', models.TextField(blank=True, null=True)), - ('response_code', models.PositiveIntegerField()), - ('response_body', models.TextField(blank=True, null=True)), - ('ip_address', models.GenericIPAddressField(blank=True, null=True)), - ('user_agent', models.CharField(blank=True, max_length=512, null=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("token_identifier", models.CharField(max_length=255)), + ("path", models.CharField(max_length=255)), + ("method", models.CharField(max_length=10)), + ("query_params", models.TextField(blank=True, null=True)), + ("headers", models.TextField(blank=True, null=True)), + ("body", models.TextField(blank=True, null=True)), + ("response_code", models.PositiveIntegerField()), + ("response_body", models.TextField(blank=True, null=True)), + ( + "ip_address", + models.GenericIPAddressField(blank=True, null=True), + ), + ( + "user_agent", + models.CharField(blank=True, max_length=512, null=True), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), ], options={ - 'verbose_name': 'API Activity Log', - 'verbose_name_plural': 'API Activity Logs', - 'db_table': 'api_activity_logs', - 'ordering': ('-created_at',), + "verbose_name": "API Activity Log", + "verbose_name_plural": "API Activity Logs", + "db_table": "api_activity_logs", + "ordering": ("-created_at",), }, ), ] diff --git a/apiserver/plane/db/migrations/0048_auto_20231116_0713.py b/apiserver/plane/db/migrations/0048_auto_20231116_0713.py index 8d896b01d..791affed6 100644 --- a/apiserver/plane/db/migrations/0048_auto_20231116_0713.py +++ b/apiserver/plane/db/migrations/0048_auto_20231116_0713.py @@ -7,48 +7,135 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0047_webhook_apitoken_description_apitoken_expired_at_and_more'), + ( + "db", + "0047_webhook_apitoken_description_apitoken_expired_at_and_more", + ), ] operations = [ migrations.CreateModel( - name='PageLog', + name="PageLog", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('transaction', models.UUIDField(default=uuid.uuid4)), - ('entity_identifier', models.UUIDField(null=True)), - ('entity_name', models.CharField(choices=[('to_do', 'To Do'), ('issue', 'issue'), ('image', 'Image'), ('video', 'Video'), ('file', 'File'), ('link', 'Link'), ('cycle', 'Cycle'), ('module', 'Module'), ('back_link', 'Back Link'), ('forward_link', 'Forward Link'), ('page_mention', 'Page Mention'), ('user_mention', 'User Mention')], max_length=30, verbose_name='Transaction Type')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='page_log', to='db.page')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("transaction", models.UUIDField(default=uuid.uuid4)), + ("entity_identifier", models.UUIDField(null=True)), + ( + "entity_name", + models.CharField( + choices=[ + ("to_do", "To Do"), + ("issue", "issue"), + ("image", "Image"), + ("video", "Video"), + ("file", "File"), + ("link", "Link"), + ("cycle", "Cycle"), + ("module", "Module"), + ("back_link", "Back Link"), + ("forward_link", "Forward Link"), + ("page_mention", "Page Mention"), + ("user_mention", "User Mention"), + ], + max_length=30, + verbose_name="Transaction Type", + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "page", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="page_log", + to="db.page", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Page Log', - 'verbose_name_plural': 'Page Logs', - 'db_table': 'page_logs', - 'ordering': ('-created_at',), - 'unique_together': {('page', 'transaction')} + "verbose_name": "Page Log", + "verbose_name_plural": "Page Logs", + "db_table": "page_logs", + "ordering": ("-created_at",), + "unique_together": {("page", "transaction")}, }, ), migrations.AddField( - model_name='page', - name='archived_at', + model_name="page", + name="archived_at", field=models.DateField(null=True), ), migrations.AddField( - model_name='page', - name='is_locked', + model_name="page", + name="is_locked", field=models.BooleanField(default=False), ), migrations.AddField( - model_name='page', - name='parent', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='child_page', to='db.page'), + model_name="page", + name="parent", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="child_page", + to="db.page", + ), ), - ] \ No newline at end of file + ] diff --git a/apiserver/plane/db/migrations/0049_auto_20231116_0713.py b/apiserver/plane/db/migrations/0049_auto_20231116_0713.py index 75d5e5982..d59fc5a84 100644 --- a/apiserver/plane/db/migrations/0049_auto_20231116_0713.py +++ b/apiserver/plane/db/migrations/0049_auto_20231116_0713.py @@ -18,7 +18,9 @@ def update_pages(apps, schema_editor): # looping through all the pages for page in Page.objects.all(): page_blocks = PageBlock.objects.filter( - page_id=page.id, project_id=page.project_id, workspace_id=page.workspace_id + page_id=page.id, + project_id=page.project_id, + workspace_id=page.workspace_id, ).order_by("sort_order") if page_blocks: @@ -69,4 +71,4 @@ class Migration(migrations.Migration): operations = [ migrations.RunPython(update_pages), - ] \ No newline at end of file + ] diff --git a/apiserver/plane/db/migrations/0050_user_use_case_alter_workspace_organization_size.py b/apiserver/plane/db/migrations/0050_user_use_case_alter_workspace_organization_size.py index a8807d104..327a5ab72 100644 --- a/apiserver/plane/db/migrations/0050_user_use_case_alter_workspace_organization_size.py +++ b/apiserver/plane/db/migrations/0050_user_use_case_alter_workspace_organization_size.py @@ -3,37 +3,41 @@ from django.db import migrations, models import plane.db.models.workspace + def user_password_autoset(apps, schema_editor): User = apps.get_model("db", "User") User.objects.update(is_password_autoset=True) class Migration(migrations.Migration): - dependencies = [ - ('db', '0049_auto_20231116_0713'), + ("db", "0049_auto_20231116_0713"), ] operations = [ migrations.AddField( - model_name='user', - name='use_case', + model_name="user", + name="use_case", field=models.TextField(blank=True, null=True), ), migrations.AlterField( - model_name='workspace', - name='organization_size', + model_name="workspace", + name="organization_size", field=models.CharField(blank=True, max_length=20, null=True), ), migrations.AddField( - model_name='fileasset', - name='is_deleted', + model_name="fileasset", + name="is_deleted", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='workspace', - name='slug', - field=models.SlugField(max_length=48, unique=True, validators=[plane.db.models.workspace.slug_validator]), + model_name="workspace", + name="slug", + field=models.SlugField( + max_length=48, + unique=True, + validators=[plane.db.models.workspace.slug_validator], + ), ), - migrations.RunPython(user_password_autoset), + migrations.RunPython(user_password_autoset), ] diff --git a/apiserver/plane/db/migrations/0051_cycle_external_id_cycle_external_source_and_more.py b/apiserver/plane/db/migrations/0051_cycle_external_id_cycle_external_source_and_more.py index 19267dfc2..886cee52d 100644 --- a/apiserver/plane/db/migrations/0051_cycle_external_id_cycle_external_source_and_more.py +++ b/apiserver/plane/db/migrations/0051_cycle_external_id_cycle_external_source_and_more.py @@ -4,80 +4,79 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('db', '0050_user_use_case_alter_workspace_organization_size'), + ("db", "0050_user_use_case_alter_workspace_organization_size"), ] operations = [ migrations.AddField( - model_name='cycle', - name='external_id', + model_name="cycle", + name="external_id", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='cycle', - name='external_source', + model_name="cycle", + name="external_source", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='inboxissue', - name='external_id', + model_name="inboxissue", + name="external_id", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='inboxissue', - name='external_source', + model_name="inboxissue", + name="external_source", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='issue', - name='external_id', + model_name="issue", + name="external_id", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='issue', - name='external_source', + model_name="issue", + name="external_source", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='issuecomment', - name='external_id', + model_name="issuecomment", + name="external_id", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='issuecomment', - name='external_source', + model_name="issuecomment", + name="external_source", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='label', - name='external_id', + model_name="label", + name="external_id", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='label', - name='external_source', + model_name="label", + name="external_source", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='module', - name='external_id', + model_name="module", + name="external_id", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='module', - name='external_source', + model_name="module", + name="external_source", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='state', - name='external_id', + model_name="state", + name="external_id", field=models.CharField(blank=True, max_length=255, null=True), ), migrations.AddField( - model_name='state', - name='external_source', + model_name="state", + name="external_source", field=models.CharField(blank=True, max_length=255, null=True), ), ] diff --git a/apiserver/plane/db/migrations/0052_auto_20231220_1141.py b/apiserver/plane/db/migrations/0052_auto_20231220_1141.py index 5ec614ab4..da16fb9f6 100644 --- a/apiserver/plane/db/migrations/0052_auto_20231220_1141.py +++ b/apiserver/plane/db/migrations/0052_auto_20231220_1141.py @@ -12,125 +12,368 @@ import uuid class Migration(migrations.Migration): - dependencies = [ - ('db', '0051_cycle_external_id_cycle_external_source_and_more'), + ("db", "0051_cycle_external_id_cycle_external_source_and_more"), ] operations = [ migrations.RenameField( - model_name='issueview', - old_name='query_data', - new_name='filters', + model_name="issueview", + old_name="query_data", + new_name="filters", ), migrations.RenameField( - model_name='issueproperty', - old_name='properties', - new_name='display_properties', + model_name="issueproperty", + old_name="properties", + new_name="display_properties", ), migrations.AlterField( - model_name='issueproperty', - name='display_properties', - field=models.JSONField(default=plane.db.models.issue.get_default_display_properties), + model_name="issueproperty", + name="display_properties", + field=models.JSONField( + default=plane.db.models.issue.get_default_display_properties + ), ), migrations.AddField( - model_name='issueproperty', - name='display_filters', - field=models.JSONField(default=plane.db.models.issue.get_default_display_filters), + model_name="issueproperty", + name="display_filters", + field=models.JSONField( + default=plane.db.models.issue.get_default_display_filters + ), ), migrations.AddField( - model_name='issueproperty', - name='filters', - field=models.JSONField(default=plane.db.models.issue.get_default_filters), + model_name="issueproperty", + name="filters", + field=models.JSONField( + default=plane.db.models.issue.get_default_filters + ), ), migrations.AddField( - model_name='issueview', - name='display_filters', - field=models.JSONField(default=plane.db.models.view.get_default_display_filters), + model_name="issueview", + name="display_filters", + field=models.JSONField( + default=plane.db.models.view.get_default_display_filters + ), ), migrations.AddField( - model_name='issueview', - name='display_properties', - field=models.JSONField(default=plane.db.models.view.get_default_display_properties), + model_name="issueview", + name="display_properties", + field=models.JSONField( + default=plane.db.models.view.get_default_display_properties + ), ), migrations.AddField( - model_name='issueview', - name='sort_order', + model_name="issueview", + name="sort_order", field=models.FloatField(default=65535), ), migrations.AlterField( - model_name='issueview', - name='project', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project'), + model_name="issueview", + name="project", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), ), migrations.CreateModel( - name='WorkspaceUserProperties', + name="WorkspaceUserProperties", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('filters', models.JSONField(default=plane.db.models.workspace.get_default_filters)), - ('display_filters', models.JSONField(default=plane.db.models.workspace.get_default_display_filters)), - ('display_properties', models.JSONField(default=plane.db.models.workspace.get_default_display_properties)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_user_properties', to=settings.AUTH_USER_MODEL)), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_user_properties', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "filters", + models.JSONField( + default=plane.db.models.workspace.get_default_filters + ), + ), + ( + "display_filters", + models.JSONField( + default=plane.db.models.workspace.get_default_display_filters + ), + ), + ( + "display_properties", + models.JSONField( + default=plane.db.models.workspace.get_default_display_properties + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_user_properties", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_user_properties", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Workspace User Property', - 'verbose_name_plural': 'Workspace User Property', - 'db_table': 'Workspace_user_properties', - 'ordering': ('-created_at',), - 'unique_together': {('workspace', 'user')}, + "verbose_name": "Workspace User Property", + "verbose_name_plural": "Workspace User Property", + "db_table": "Workspace_user_properties", + "ordering": ("-created_at",), + "unique_together": {("workspace", "user")}, }, ), migrations.CreateModel( - name='ModuleUserProperties', + name="ModuleUserProperties", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('filters', models.JSONField(default=plane.db.models.module.get_default_filters)), - ('display_filters', models.JSONField(default=plane.db.models.module.get_default_display_filters)), - ('display_properties', models.JSONField(default=plane.db.models.module.get_default_display_properties)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('module', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='module_user_properties', to='db.module')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='module_user_properties', to=settings.AUTH_USER_MODEL)), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "filters", + models.JSONField( + default=plane.db.models.module.get_default_filters + ), + ), + ( + "display_filters", + models.JSONField( + default=plane.db.models.module.get_default_display_filters + ), + ), + ( + "display_properties", + models.JSONField( + default=plane.db.models.module.get_default_display_properties + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "module", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="module_user_properties", + to="db.module", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="module_user_properties", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Module User Property', - 'verbose_name_plural': 'Module User Property', - 'db_table': 'module_user_properties', - 'ordering': ('-created_at',), - 'unique_together': {('module', 'user')}, + "verbose_name": "Module User Property", + "verbose_name_plural": "Module User Property", + "db_table": "module_user_properties", + "ordering": ("-created_at",), + "unique_together": {("module", "user")}, }, ), migrations.CreateModel( - name='CycleUserProperties', + name="CycleUserProperties", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('filters', models.JSONField(default=plane.db.models.cycle.get_default_filters)), - ('display_filters', models.JSONField(default=plane.db.models.cycle.get_default_display_filters)), - ('display_properties', models.JSONField(default=plane.db.models.cycle.get_default_display_properties)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('cycle', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cycle_user_properties', to='db.cycle')), - ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cycle_user_properties', to=settings.AUTH_USER_MODEL)), - ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "filters", + models.JSONField( + default=plane.db.models.cycle.get_default_filters + ), + ), + ( + "display_filters", + models.JSONField( + default=plane.db.models.cycle.get_default_display_filters + ), + ), + ( + "display_properties", + models.JSONField( + default=plane.db.models.cycle.get_default_display_properties + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "cycle", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="cycle_user_properties", + to="db.cycle", + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="project_%(class)s", + to="db.project", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="cycle_user_properties", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "workspace", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="workspace_%(class)s", + to="db.workspace", + ), + ), ], options={ - 'verbose_name': 'Cycle User Property', - 'verbose_name_plural': 'Cycle User Properties', - 'db_table': 'cycle_user_properties', - 'ordering': ('-created_at',), - 'unique_together': {('cycle', 'user')}, + "verbose_name": "Cycle User Property", + "verbose_name_plural": "Cycle User Properties", + "db_table": "cycle_user_properties", + "ordering": ("-created_at",), + "unique_together": {("cycle", "user")}, }, ), - ] \ No newline at end of file + ] diff --git a/apiserver/plane/db/migrations/0053_auto_20240102_1315.py b/apiserver/plane/db/migrations/0053_auto_20240102_1315.py index 798d0d7bb..32b5ad2d5 100644 --- a/apiserver/plane/db/migrations/0053_auto_20240102_1315.py +++ b/apiserver/plane/db/migrations/0053_auto_20240102_1315.py @@ -11,31 +11,46 @@ def workspace_user_properties(apps, schema_editor): updated_workspace_user_properties.append( WorkspaceUserProperties( user_id=workspace_members.member_id, - display_filters=workspace_members.view_props.get("display_filters"), - display_properties=workspace_members.view_props.get("display_properties"), + display_filters=workspace_members.view_props.get( + "display_filters" + ), + display_properties=workspace_members.view_props.get( + "display_properties" + ), workspace_id=workspace_members.workspace_id, ) ) - WorkspaceUserProperties.objects.bulk_create(updated_workspace_user_properties, batch_size=2000) + WorkspaceUserProperties.objects.bulk_create( + updated_workspace_user_properties, batch_size=2000 + ) def project_user_properties(apps, schema_editor): IssueProperty = apps.get_model("db", "IssueProperty") updated_issue_user_properties = [] for issue_property in IssueProperty.objects.all(): - project_member = ProjectMember.objects.filter(project_id=issue_property.project_id, member_id=issue_property.user_id).first() + project_member = ProjectMember.objects.filter( + project_id=issue_property.project_id, + member_id=issue_property.user_id, + ).first() if project_member: issue_property.filters = project_member.view_props.get("filters") - issue_property.display_filters = project_member.view_props.get("display_filters") + issue_property.display_filters = project_member.view_props.get( + "display_filters" + ) updated_issue_user_properties.append(issue_property) - IssueProperty.objects.bulk_update(updated_issue_user_properties, ["filters", "display_filters"], batch_size=2000) + IssueProperty.objects.bulk_update( + updated_issue_user_properties, + ["filters", "display_filters"], + batch_size=2000, + ) def issue_view(apps, schema_editor): GlobalView = apps.get_model("db", "GlobalView") updated_issue_views = [] - + for global_view in GlobalView.objects.all(): updated_issue_views.append( IssueView( @@ -52,10 +67,10 @@ def issue_view(apps, schema_editor): ) IssueView.objects.bulk_create(updated_issue_views, batch_size=100) -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('db', '0052_auto_20231220_1141'), + ("db", "0052_auto_20231220_1141"), ] operations = [ diff --git a/apiserver/plane/db/mixins.py b/apiserver/plane/db/mixins.py index 728cb9933..263f9ab9a 100644 --- a/apiserver/plane/db/mixins.py +++ b/apiserver/plane/db/mixins.py @@ -13,7 +13,9 @@ class TimeAuditModel(models.Model): auto_now_add=True, verbose_name="Created At", ) - updated_at = models.DateTimeField(auto_now=True, verbose_name="Last Modified At") + updated_at = models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ) class Meta: abstract = True diff --git a/apiserver/plane/db/models/__init__.py b/apiserver/plane/db/models/__init__.py index b88ee8e46..9ae0d154d 100644 --- a/apiserver/plane/db/models/__init__.py +++ b/apiserver/plane/db/models/__init__.py @@ -54,7 +54,14 @@ from .cycle import Cycle, CycleIssue, CycleFavorite, CycleUserProperties from .view import GlobalView, IssueView, IssueViewFavorite -from .module import Module, ModuleMember, ModuleIssue, ModuleLink, ModuleFavorite, ModuleUserProperties +from .module import ( + Module, + ModuleMember, + ModuleIssue, + ModuleLink, + ModuleFavorite, + ModuleUserProperties, +) from .api import APIToken, APIActivityLog diff --git a/apiserver/plane/db/models/api.py b/apiserver/plane/db/models/api.py index 0fa1d4aba..78da81814 100644 --- a/apiserver/plane/db/models/api.py +++ b/apiserver/plane/db/models/api.py @@ -38,7 +38,10 @@ class APIToken(BaseModel): choices=((0, "Human"), (1, "Bot")), default=0 ) workspace = models.ForeignKey( - "db.Workspace", related_name="api_tokens", on_delete=models.CASCADE, null=True + "db.Workspace", + related_name="api_tokens", + on_delete=models.CASCADE, + null=True, ) expired_at = models.DateTimeField(blank=True, null=True) diff --git a/apiserver/plane/db/models/asset.py b/apiserver/plane/db/models/asset.py index ab3c38d9c..713508613 100644 --- a/apiserver/plane/db/models/asset.py +++ b/apiserver/plane/db/models/asset.py @@ -34,7 +34,10 @@ class FileAsset(BaseModel): ], ) workspace = models.ForeignKey( - "db.Workspace", on_delete=models.CASCADE, null=True, related_name="assets" + "db.Workspace", + on_delete=models.CASCADE, + null=True, + related_name="assets", ) is_deleted = models.BooleanField(default=False) diff --git a/apiserver/plane/db/models/base.py b/apiserver/plane/db/models/base.py index d0531e881..63c08afa4 100644 --- a/apiserver/plane/db/models/base.py +++ b/apiserver/plane/db/models/base.py @@ -12,7 +12,11 @@ from ..mixins import AuditModel class BaseModel(AuditModel): id = models.UUIDField( - default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True + default=uuid.uuid4, + unique=True, + editable=False, + db_index=True, + primary_key=True, ) class Meta: diff --git a/apiserver/plane/db/models/cycle.py b/apiserver/plane/db/models/cycle.py index a441057e1..5251c68ec 100644 --- a/apiserver/plane/db/models/cycle.py +++ b/apiserver/plane/db/models/cycle.py @@ -19,6 +19,7 @@ def get_default_filters(): "subscriber": None, } + def get_default_display_filters(): return { "group_by": None, @@ -30,6 +31,7 @@ def get_default_display_filters(): "calendar_date_range": "", } + def get_default_display_properties(): return { "assignee": True, @@ -47,10 +49,15 @@ def get_default_display_properties(): "updated_on": True, } + class Cycle(ProjectBaseModel): name = models.CharField(max_length=255, verbose_name="Cycle Name") - description = models.TextField(verbose_name="Cycle Description", blank=True) - start_date = models.DateField(verbose_name="Start Date", blank=True, null=True) + description = models.TextField( + verbose_name="Cycle Description", blank=True + ) + start_date = models.DateField( + verbose_name="Start Date", blank=True, null=True + ) end_date = models.DateField(verbose_name="End Date", blank=True, null=True) owned_by = models.ForeignKey( settings.AUTH_USER_MODEL, @@ -134,7 +141,9 @@ class CycleFavorite(ProjectBaseModel): class CycleUserProperties(ProjectBaseModel): cycle = models.ForeignKey( - "db.Cycle", on_delete=models.CASCADE, related_name="cycle_user_properties" + "db.Cycle", + on_delete=models.CASCADE, + related_name="cycle_user_properties", ) user = models.ForeignKey( settings.AUTH_USER_MODEL, @@ -143,8 +152,9 @@ class CycleUserProperties(ProjectBaseModel): ) filters = models.JSONField(default=get_default_filters) display_filters = models.JSONField(default=get_default_display_filters) - display_properties = models.JSONField(default=get_default_display_properties) - + display_properties = models.JSONField( + default=get_default_display_properties + ) class Meta: unique_together = ["cycle", "user"] @@ -154,4 +164,4 @@ class CycleUserProperties(ProjectBaseModel): ordering = ("-created_at",) def __str__(self): - return f"{self.cycle.name} {self.user.email}" \ No newline at end of file + return f"{self.cycle.name} {self.user.email}" diff --git a/apiserver/plane/db/models/estimate.py b/apiserver/plane/db/models/estimate.py index d95a86316..bb57e788c 100644 --- a/apiserver/plane/db/models/estimate.py +++ b/apiserver/plane/db/models/estimate.py @@ -8,7 +8,9 @@ from . import ProjectBaseModel class Estimate(ProjectBaseModel): name = models.CharField(max_length=255) - description = models.TextField(verbose_name="Estimate Description", blank=True) + description = models.TextField( + verbose_name="Estimate Description", blank=True + ) def __str__(self): """Return name of the estimate""" diff --git a/apiserver/plane/db/models/exporter.py b/apiserver/plane/db/models/exporter.py index 0383807b7..d427eb0f6 100644 --- a/apiserver/plane/db/models/exporter.py +++ b/apiserver/plane/db/models/exporter.py @@ -11,14 +11,20 @@ from django.contrib.postgres.fields import ArrayField # Module imports from . import BaseModel + def generate_token(): return uuid4().hex + class ExporterHistory(BaseModel): workspace = models.ForeignKey( - "db.WorkSpace", on_delete=models.CASCADE, related_name="workspace_exporters" + "db.WorkSpace", + on_delete=models.CASCADE, + related_name="workspace_exporters", + ) + project = ArrayField( + models.UUIDField(default=uuid.uuid4), blank=True, null=True ) - project = ArrayField(models.UUIDField(default=uuid.uuid4), blank=True, null=True) provider = models.CharField( max_length=50, choices=( @@ -40,9 +46,13 @@ class ExporterHistory(BaseModel): reason = models.TextField(blank=True) key = models.TextField(blank=True) url = models.URLField(max_length=800, blank=True, null=True) - token = models.CharField(max_length=255, default=generate_token, unique=True) + token = models.CharField( + max_length=255, default=generate_token, unique=True + ) initiated_by = models.ForeignKey( - settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="workspace_exporters" + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="workspace_exporters", ) class Meta: diff --git a/apiserver/plane/db/models/importer.py b/apiserver/plane/db/models/importer.py index a2d1d3166..651927458 100644 --- a/apiserver/plane/db/models/importer.py +++ b/apiserver/plane/db/models/importer.py @@ -25,7 +25,9 @@ class Importer(ProjectBaseModel): default="queued", ) initiated_by = models.ForeignKey( - settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="imports" + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="imports", ) metadata = models.JSONField(default=dict) config = models.JSONField(default=dict) diff --git a/apiserver/plane/db/models/inbox.py b/apiserver/plane/db/models/inbox.py index 6ad88e681..809a11821 100644 --- a/apiserver/plane/db/models/inbox.py +++ b/apiserver/plane/db/models/inbox.py @@ -7,7 +7,9 @@ from plane.db.models import ProjectBaseModel class Inbox(ProjectBaseModel): name = models.CharField(max_length=255) - description = models.TextField(verbose_name="Inbox Description", blank=True) + description = models.TextField( + verbose_name="Inbox Description", blank=True + ) is_default = models.BooleanField(default=False) view_props = models.JSONField(default=dict) @@ -31,12 +33,21 @@ class InboxIssue(ProjectBaseModel): "db.Issue", related_name="issue_inbox", on_delete=models.CASCADE ) status = models.IntegerField( - choices=((-2, "Pending"), (-1, "Rejected"), (0, "Snoozed"), (1, "Accepted"), (2, "Duplicate")), + choices=( + (-2, "Pending"), + (-1, "Rejected"), + (0, "Snoozed"), + (1, "Accepted"), + (2, "Duplicate"), + ), default=-2, ) snoozed_till = models.DateTimeField(null=True) duplicate_to = models.ForeignKey( - "db.Issue", related_name="inbox_duplicate", on_delete=models.SET_NULL, null=True + "db.Issue", + related_name="inbox_duplicate", + on_delete=models.SET_NULL, + null=True, ) source = models.TextField(blank=True, null=True) external_source = models.CharField(max_length=255, null=True, blank=True) diff --git a/apiserver/plane/db/models/integration/__init__.py b/apiserver/plane/db/models/integration/__init__.py index 3bef68708..34b40e57d 100644 --- a/apiserver/plane/db/models/integration/__init__.py +++ b/apiserver/plane/db/models/integration/__init__.py @@ -1,3 +1,8 @@ from .base import Integration, WorkspaceIntegration -from .github import GithubRepository, GithubRepositorySync, GithubIssueSync, GithubCommentSync +from .github import ( + GithubRepository, + GithubRepositorySync, + GithubIssueSync, + GithubCommentSync, +) from .slack import SlackProjectSync diff --git a/apiserver/plane/db/models/integration/base.py b/apiserver/plane/db/models/integration/base.py index 47db0483c..0c68adfd2 100644 --- a/apiserver/plane/db/models/integration/base.py +++ b/apiserver/plane/db/models/integration/base.py @@ -11,7 +11,11 @@ from plane.db.mixins import AuditModel class Integration(AuditModel): id = models.UUIDField( - default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True + default=uuid.uuid4, + unique=True, + editable=False, + db_index=True, + primary_key=True, ) title = models.CharField(max_length=400) provider = models.CharField(max_length=400, unique=True) @@ -40,14 +44,18 @@ class Integration(AuditModel): class WorkspaceIntegration(BaseModel): workspace = models.ForeignKey( - "db.Workspace", related_name="workspace_integrations", on_delete=models.CASCADE + "db.Workspace", + related_name="workspace_integrations", + on_delete=models.CASCADE, ) # Bot user actor = models.ForeignKey( "db.User", related_name="integrations", on_delete=models.CASCADE ) integration = models.ForeignKey( - "db.Integration", related_name="integrated_workspaces", on_delete=models.CASCADE + "db.Integration", + related_name="integrated_workspaces", + on_delete=models.CASCADE, ) api_token = models.ForeignKey( "db.APIToken", related_name="integrations", on_delete=models.CASCADE diff --git a/apiserver/plane/db/models/integration/github.py b/apiserver/plane/db/models/integration/github.py index f4d152bb1..f3331c874 100644 --- a/apiserver/plane/db/models/integration/github.py +++ b/apiserver/plane/db/models/integration/github.py @@ -36,10 +36,15 @@ class GithubRepositorySync(ProjectBaseModel): "db.User", related_name="user_syncs", on_delete=models.CASCADE ) workspace_integration = models.ForeignKey( - "db.WorkspaceIntegration", related_name="github_syncs", on_delete=models.CASCADE + "db.WorkspaceIntegration", + related_name="github_syncs", + on_delete=models.CASCADE, ) label = models.ForeignKey( - "db.Label", on_delete=models.SET_NULL, null=True, related_name="repo_syncs" + "db.Label", + on_delete=models.SET_NULL, + null=True, + related_name="repo_syncs", ) def __str__(self): @@ -62,7 +67,9 @@ class GithubIssueSync(ProjectBaseModel): "db.Issue", related_name="github_syncs", on_delete=models.CASCADE ) repository_sync = models.ForeignKey( - "db.GithubRepositorySync", related_name="issue_syncs", on_delete=models.CASCADE + "db.GithubRepositorySync", + related_name="issue_syncs", + on_delete=models.CASCADE, ) def __str__(self): @@ -80,10 +87,14 @@ class GithubIssueSync(ProjectBaseModel): class GithubCommentSync(ProjectBaseModel): repo_comment_id = models.BigIntegerField() comment = models.ForeignKey( - "db.IssueComment", related_name="comment_syncs", on_delete=models.CASCADE + "db.IssueComment", + related_name="comment_syncs", + on_delete=models.CASCADE, ) issue_sync = models.ForeignKey( - "db.GithubIssueSync", related_name="comment_syncs", on_delete=models.CASCADE + "db.GithubIssueSync", + related_name="comment_syncs", + on_delete=models.CASCADE, ) def __str__(self): diff --git a/apiserver/plane/db/models/integration/slack.py b/apiserver/plane/db/models/integration/slack.py index 6b29968f6..72df4dfd7 100644 --- a/apiserver/plane/db/models/integration/slack.py +++ b/apiserver/plane/db/models/integration/slack.py @@ -17,7 +17,9 @@ class SlackProjectSync(ProjectBaseModel): team_id = models.CharField(max_length=30) team_name = models.CharField(max_length=300) workspace_integration = models.ForeignKey( - "db.WorkspaceIntegration", related_name="slack_syncs", on_delete=models.CASCADE + "db.WorkspaceIntegration", + related_name="slack_syncs", + on_delete=models.CASCADE, ) def __str__(self): diff --git a/apiserver/plane/db/models/issue.py b/apiserver/plane/db/models/issue.py index b14376bc5..43274ea13 100644 --- a/apiserver/plane/db/models/issue.py +++ b/apiserver/plane/db/models/issue.py @@ -46,6 +46,7 @@ def get_default_filters(): "subscriber": None, } + def get_default_display_filters(): return { "group_by": None, @@ -57,6 +58,7 @@ def get_default_display_filters(): "calendar_date_range": "", } + def get_default_display_properties(): return { "assignee": True, @@ -115,7 +117,9 @@ class Issue(ProjectBaseModel): related_name="state_issue", ) estimate_point = models.IntegerField( - validators=[MinValueValidator(0), MaxValueValidator(7)], null=True, blank=True + validators=[MinValueValidator(0), MaxValueValidator(7)], + null=True, + blank=True, ) name = models.CharField(max_length=255, verbose_name="Issue Name") description = models.JSONField(blank=True, default=dict) @@ -136,7 +140,9 @@ class Issue(ProjectBaseModel): through="IssueAssignee", through_fields=("issue", "assignee"), ) - sequence_id = models.IntegerField(default=1, verbose_name="Issue Sequence ID") + sequence_id = models.IntegerField( + default=1, verbose_name="Issue Sequence ID" + ) labels = models.ManyToManyField( "db.Label", blank=True, related_name="labels", through="IssueLabel" ) @@ -163,7 +169,9 @@ class Issue(ProjectBaseModel): from plane.db.models import State default_state = State.objects.filter( - ~models.Q(name="Triage"), project=self.project, default=True + ~models.Q(name="Triage"), + project=self.project, + default=True, ).first() # if there is no default state assign any random state if default_state is None: @@ -176,12 +184,11 @@ class Issue(ProjectBaseModel): except ImportError: pass - if self._state.adding: # Get the maximum display_id value from the database - last_id = IssueSequence.objects.filter(project=self.project).aggregate( - largest=models.Max("sequence") - )["largest"] + last_id = IssueSequence.objects.filter( + project=self.project + ).aggregate(largest=models.Max("sequence"))["largest"] # aggregate can return None! Check it first. # If it isn't none, just use the last ID specified (which should be the greatest) and add one to it if last_id: @@ -254,8 +261,9 @@ class IssueRelation(ProjectBaseModel): ordering = ("-created_at",) def __str__(self): - return f"{self.issue.name} {self.related_issue.name}" - + return f"{self.issue.name} {self.related_issue.name}" + + class IssueMention(ProjectBaseModel): issue = models.ForeignKey( Issue, on_delete=models.CASCADE, related_name="issue_mention" @@ -265,6 +273,7 @@ class IssueMention(ProjectBaseModel): on_delete=models.CASCADE, related_name="issue_mention", ) + class Meta: unique_together = ["issue", "mention"] verbose_name = "Issue Mention" @@ -273,7 +282,7 @@ class IssueMention(ProjectBaseModel): ordering = ("-created_at",) def __str__(self): - return f"{self.issue.name} {self.mention.email}" + return f"{self.issue.name} {self.mention.email}" class IssueAssignee(ProjectBaseModel): @@ -349,17 +358,28 @@ class IssueAttachment(ProjectBaseModel): class IssueActivity(ProjectBaseModel): issue = models.ForeignKey( - Issue, on_delete=models.SET_NULL, null=True, related_name="issue_activity" + Issue, + on_delete=models.SET_NULL, + null=True, + related_name="issue_activity", + ) + verb = models.CharField( + max_length=255, verbose_name="Action", default="created" ) - verb = models.CharField(max_length=255, verbose_name="Action", default="created") field = models.CharField( max_length=255, verbose_name="Field Name", blank=True, null=True ) - old_value = models.TextField(verbose_name="Old Value", blank=True, null=True) - new_value = models.TextField(verbose_name="New Value", blank=True, null=True) + old_value = models.TextField( + verbose_name="Old Value", blank=True, null=True + ) + new_value = models.TextField( + verbose_name="New Value", blank=True, null=True + ) comment = models.TextField(verbose_name="Comment", blank=True) - attachments = ArrayField(models.URLField(), size=10, blank=True, default=list) + attachments = ArrayField( + models.URLField(), size=10, blank=True, default=list + ) issue_comment = models.ForeignKey( "db.IssueComment", on_delete=models.SET_NULL, @@ -391,7 +411,9 @@ class IssueComment(ProjectBaseModel): comment_stripped = models.TextField(verbose_name="Comment", blank=True) comment_json = models.JSONField(blank=True, default=dict) comment_html = models.TextField(blank=True, default="

") - attachments = ArrayField(models.URLField(), size=10, blank=True, default=list) + attachments = ArrayField( + models.URLField(), size=10, blank=True, default=list + ) issue = models.ForeignKey( Issue, on_delete=models.CASCADE, related_name="issue_comments" ) @@ -438,7 +460,9 @@ class IssueProperty(ProjectBaseModel): ) filters = models.JSONField(default=get_default_filters) display_filters = models.JSONField(default=get_default_display_filters) - display_properties = models.JSONField(default=get_default_display_properties) + display_properties = models.JSONField( + default=get_default_display_properties + ) class Meta: verbose_name = "Issue Property" @@ -510,7 +534,10 @@ class IssueLabel(ProjectBaseModel): class IssueSequence(ProjectBaseModel): issue = models.ForeignKey( - Issue, on_delete=models.SET_NULL, related_name="issue_sequence", null=True + Issue, + on_delete=models.SET_NULL, + related_name="issue_sequence", + null=True, ) sequence = models.PositiveBigIntegerField(default=1) deleted = models.BooleanField(default=False) @@ -572,7 +599,9 @@ class CommentReaction(ProjectBaseModel): related_name="comment_reactions", ) comment = models.ForeignKey( - IssueComment, on_delete=models.CASCADE, related_name="comment_reactions" + IssueComment, + on_delete=models.CASCADE, + related_name="comment_reactions", ) reaction = models.CharField(max_length=20) @@ -588,9 +617,13 @@ class CommentReaction(ProjectBaseModel): class IssueVote(ProjectBaseModel): - issue = models.ForeignKey(Issue, on_delete=models.CASCADE, related_name="votes") + issue = models.ForeignKey( + Issue, on_delete=models.CASCADE, related_name="votes" + ) actor = models.ForeignKey( - settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="votes" + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="votes", ) vote = models.IntegerField( choices=( @@ -619,5 +652,7 @@ class IssueVote(ProjectBaseModel): def create_issue_sequence(sender, instance, created, **kwargs): if created: IssueSequence.objects.create( - issue=instance, sequence=instance.sequence_id, project=instance.project + issue=instance, + sequence=instance.sequence_id, + project=instance.project, ) diff --git a/apiserver/plane/db/models/module.py b/apiserver/plane/db/models/module.py index cc8185946..1a47aac1b 100644 --- a/apiserver/plane/db/models/module.py +++ b/apiserver/plane/db/models/module.py @@ -7,17 +7,20 @@ from . import ProjectBaseModel def get_default_filters(): - return { - "priority": None, - "state": None, - "state_group": None, - "assignees": None, - "created_by": None, - "labels": None, - "start_date": None, - "target_date": None, - "subscriber": None, - }, + return ( + { + "priority": None, + "state": None, + "state_group": None, + "assignees": None, + "created_by": None, + "labels": None, + "start_date": None, + "target_date": None, + "subscriber": None, + }, + ) + def get_default_display_filters(): return { @@ -30,6 +33,7 @@ def get_default_display_filters(): "calendar_date_range": "", } + def get_default_display_properties(): return { "assignee": True, @@ -47,9 +51,12 @@ def get_default_display_properties(): "updated_on": True, } + class Module(ProjectBaseModel): name = models.CharField(max_length=255, verbose_name="Module Name") - description = models.TextField(verbose_name="Module Description", blank=True) + description = models.TextField( + verbose_name="Module Description", blank=True + ) description_text = models.JSONField( verbose_name="Module Description RT", blank=True, null=True ) @@ -71,7 +78,10 @@ class Module(ProjectBaseModel): max_length=20, ) lead = models.ForeignKey( - "db.User", on_delete=models.SET_NULL, related_name="module_leads", null=True + "db.User", + on_delete=models.SET_NULL, + related_name="module_leads", + null=True, ) members = models.ManyToManyField( settings.AUTH_USER_MODEL, @@ -94,9 +104,9 @@ class Module(ProjectBaseModel): def save(self, *args, **kwargs): if self._state.adding: - smallest_sort_order = Module.objects.filter(project=self.project).aggregate( - smallest=models.Min("sort_order") - )["smallest"] + smallest_sort_order = Module.objects.filter( + project=self.project + ).aggregate(smallest=models.Min("sort_order"))["smallest"] if smallest_sort_order is not None: self.sort_order = smallest_sort_order - 10000 @@ -186,7 +196,9 @@ class ModuleFavorite(ProjectBaseModel): class ModuleUserProperties(ProjectBaseModel): module = models.ForeignKey( - "db.Module", on_delete=models.CASCADE, related_name="module_user_properties" + "db.Module", + on_delete=models.CASCADE, + related_name="module_user_properties", ) user = models.ForeignKey( settings.AUTH_USER_MODEL, @@ -195,8 +207,9 @@ class ModuleUserProperties(ProjectBaseModel): ) filters = models.JSONField(default=get_default_filters) display_filters = models.JSONField(default=get_default_display_filters) - display_properties = models.JSONField(default=get_default_display_properties) - + display_properties = models.JSONField( + default=get_default_display_properties + ) class Meta: unique_together = ["module", "user"] @@ -206,4 +219,4 @@ class ModuleUserProperties(ProjectBaseModel): ordering = ("-created_at",) def __str__(self): - return f"{self.module.name} {self.user.email}" \ No newline at end of file + return f"{self.module.name} {self.user.email}" diff --git a/apiserver/plane/db/models/notification.py b/apiserver/plane/db/models/notification.py index 3df935718..8e6a48e14 100644 --- a/apiserver/plane/db/models/notification.py +++ b/apiserver/plane/db/models/notification.py @@ -10,7 +10,10 @@ class Notification(BaseModel): "db.Workspace", related_name="notifications", on_delete=models.CASCADE ) project = models.ForeignKey( - "db.Project", related_name="notifications", on_delete=models.CASCADE, null=True + "db.Project", + related_name="notifications", + on_delete=models.CASCADE, + null=True, ) data = models.JSONField(null=True) entity_identifier = models.UUIDField(null=True) @@ -20,8 +23,17 @@ class Notification(BaseModel): message_html = models.TextField(blank=True, default="

") message_stripped = models.TextField(blank=True, null=True) sender = models.CharField(max_length=255) - triggered_by = models.ForeignKey("db.User", related_name="triggered_notifications", on_delete=models.SET_NULL, null=True) - receiver = models.ForeignKey("db.User", related_name="received_notifications", on_delete=models.CASCADE) + triggered_by = models.ForeignKey( + "db.User", + related_name="triggered_notifications", + on_delete=models.SET_NULL, + null=True, + ) + receiver = models.ForeignKey( + "db.User", + related_name="received_notifications", + on_delete=models.CASCADE, + ) read_at = models.DateTimeField(null=True) snoozed_till = models.DateTimeField(null=True) archived_at = models.DateTimeField(null=True) diff --git a/apiserver/plane/db/models/page.py b/apiserver/plane/db/models/page.py index de65cb98f..6ed94798a 100644 --- a/apiserver/plane/db/models/page.py +++ b/apiserver/plane/db/models/page.py @@ -15,7 +15,9 @@ class Page(ProjectBaseModel): description_html = models.TextField(blank=True, default="

") description_stripped = models.TextField(blank=True, null=True) owned_by = models.ForeignKey( - settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="pages" + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="pages", ) access = models.PositiveSmallIntegerField( choices=((0, "Public"), (1, "Private")), default=0 @@ -53,7 +55,7 @@ class PageLog(ProjectBaseModel): ("video", "Video"), ("file", "File"), ("link", "Link"), - ("cycle","Cycle"), + ("cycle", "Cycle"), ("module", "Module"), ("back_link", "Back Link"), ("forward_link", "Forward Link"), @@ -77,13 +79,15 @@ class PageLog(ProjectBaseModel): verbose_name_plural = "Page Logs" db_table = "page_logs" ordering = ("-created_at",) - + def __str__(self): return f"{self.page.name} {self.type}" class PageBlock(ProjectBaseModel): - page = models.ForeignKey("db.Page", on_delete=models.CASCADE, related_name="blocks") + page = models.ForeignKey( + "db.Page", on_delete=models.CASCADE, related_name="blocks" + ) name = models.CharField(max_length=255) description = models.JSONField(default=dict, blank=True) description_html = models.TextField(blank=True, default="

") @@ -118,7 +122,9 @@ class PageBlock(ProjectBaseModel): group="completed", project=self.project ).first() if completed_state is not None: - Issue.objects.update(pk=self.issue_id, state=completed_state) + Issue.objects.update( + pk=self.issue_id, state=completed_state + ) except ImportError: pass super(PageBlock, self).save(*args, **kwargs) diff --git a/apiserver/plane/db/models/project.py b/apiserver/plane/db/models/project.py index fe72c260b..b93174724 100644 --- a/apiserver/plane/db/models/project.py +++ b/apiserver/plane/db/models/project.py @@ -35,7 +35,7 @@ def get_default_props(): }, "display_filters": { "group_by": None, - "order_by": '-created_at', + "order_by": "-created_at", "type": None, "sub_issue": True, "show_empty_groups": True, @@ -52,16 +52,22 @@ def get_default_preferences(): class Project(BaseModel): NETWORK_CHOICES = ((0, "Secret"), (2, "Public")) name = models.CharField(max_length=255, verbose_name="Project Name") - description = models.TextField(verbose_name="Project Description", blank=True) + description = models.TextField( + verbose_name="Project Description", blank=True + ) description_text = models.JSONField( verbose_name="Project Description RT", blank=True, null=True ) description_html = models.JSONField( verbose_name="Project Description HTML", blank=True, null=True ) - network = models.PositiveSmallIntegerField(default=2, choices=NETWORK_CHOICES) + network = models.PositiveSmallIntegerField( + default=2, choices=NETWORK_CHOICES + ) workspace = models.ForeignKey( - "db.WorkSpace", on_delete=models.CASCADE, related_name="workspace_project" + "db.WorkSpace", + on_delete=models.CASCADE, + related_name="workspace_project", ) identifier = models.CharField( max_length=12, @@ -90,7 +96,10 @@ class Project(BaseModel): inbox_view = models.BooleanField(default=False) cover_image = models.URLField(blank=True, null=True, max_length=800) estimate = models.ForeignKey( - "db.Estimate", on_delete=models.SET_NULL, related_name="projects", null=True + "db.Estimate", + on_delete=models.SET_NULL, + related_name="projects", + null=True, ) archive_in = models.IntegerField( default=0, validators=[MinValueValidator(0), MaxValueValidator(12)] @@ -99,7 +108,10 @@ class Project(BaseModel): default=0, validators=[MinValueValidator(0), MaxValueValidator(12)] ) default_state = models.ForeignKey( - "db.State", on_delete=models.SET_NULL, null=True, related_name="default_state" + "db.State", + on_delete=models.SET_NULL, + null=True, + related_name="default_state", ) def __str__(self): @@ -195,7 +207,10 @@ class ProjectMember(ProjectBaseModel): # TODO: Remove workspace relation later class ProjectIdentifier(AuditModel): workspace = models.ForeignKey( - "db.Workspace", models.CASCADE, related_name="project_identifiers", null=True + "db.Workspace", + models.CASCADE, + related_name="project_identifiers", + null=True, ) project = models.OneToOneField( Project, on_delete=models.CASCADE, related_name="project_identifier" @@ -250,7 +265,10 @@ class ProjectDeployBoard(ProjectBaseModel): comments = models.BooleanField(default=False) reactions = models.BooleanField(default=False) inbox = models.ForeignKey( - "db.Inbox", related_name="bord_inbox", on_delete=models.SET_NULL, null=True + "db.Inbox", + related_name="bord_inbox", + on_delete=models.SET_NULL, + null=True, ) votes = models.BooleanField(default=False) views = models.JSONField(default=get_default_views) diff --git a/apiserver/plane/db/models/state.py b/apiserver/plane/db/models/state.py index 3370f239d..ab9b780c8 100644 --- a/apiserver/plane/db/models/state.py +++ b/apiserver/plane/db/models/state.py @@ -8,7 +8,9 @@ from . import ProjectBaseModel class State(ProjectBaseModel): name = models.CharField(max_length=255, verbose_name="State Name") - description = models.TextField(verbose_name="State Description", blank=True) + description = models.TextField( + verbose_name="State Description", blank=True + ) color = models.CharField(max_length=255, verbose_name="State Color") slug = models.SlugField(max_length=100, blank=True) sequence = models.FloatField(default=65535) diff --git a/apiserver/plane/db/models/user.py b/apiserver/plane/db/models/user.py index fe75a6a26..82e49c15f 100644 --- a/apiserver/plane/db/models/user.py +++ b/apiserver/plane/db/models/user.py @@ -8,7 +8,11 @@ import pytz from django.db import models from django.db.models.signals import post_save from django.dispatch import receiver -from django.contrib.auth.models import AbstractBaseUser, UserManager, PermissionsMixin +from django.contrib.auth.models import ( + AbstractBaseUser, + UserManager, + PermissionsMixin, +) from django.utils import timezone from django.conf import settings @@ -29,22 +33,34 @@ def get_default_onboarding(): class User(AbstractBaseUser, PermissionsMixin): id = models.UUIDField( - default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True + default=uuid.uuid4, + unique=True, + editable=False, + db_index=True, + primary_key=True, ) username = models.CharField(max_length=128, unique=True) # user fields mobile_number = models.CharField(max_length=255, blank=True, null=True) - email = models.CharField(max_length=255, null=True, blank=True, unique=True) + email = models.CharField( + max_length=255, null=True, blank=True, unique=True + ) first_name = models.CharField(max_length=255, blank=True) last_name = models.CharField(max_length=255, blank=True) avatar = models.CharField(max_length=255, blank=True) cover_image = models.URLField(blank=True, null=True, max_length=800) # tracking metrics - date_joined = models.DateTimeField(auto_now_add=True, verbose_name="Created At") - created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created At") - updated_at = models.DateTimeField(auto_now=True, verbose_name="Last Modified At") + date_joined = models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ) + created_at = models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ) + updated_at = models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ) last_location = models.CharField(max_length=255, blank=True) created_location = models.CharField(max_length=255, blank=True) @@ -65,7 +81,9 @@ class User(AbstractBaseUser, PermissionsMixin): has_billing_address = models.BooleanField(default=False) USER_TIMEZONE_CHOICES = tuple(zip(pytz.all_timezones, pytz.all_timezones)) - user_timezone = models.CharField(max_length=255, default="UTC", choices=USER_TIMEZONE_CHOICES) + user_timezone = models.CharField( + max_length=255, default="UTC", choices=USER_TIMEZONE_CHOICES + ) last_active = models.DateTimeField(default=timezone.now, null=True) last_login_time = models.DateTimeField(null=True) @@ -115,7 +133,9 @@ class User(AbstractBaseUser, PermissionsMixin): self.display_name = ( self.email.split("@")[0] if len(self.email.split("@")) - else "".join(random.choice(string.ascii_letters) for _ in range(6)) + else "".join( + random.choice(string.ascii_letters) for _ in range(6) + ) ) if self.is_superuser: diff --git a/apiserver/plane/db/models/view.py b/apiserver/plane/db/models/view.py index 8a77f0586..13500b5a4 100644 --- a/apiserver/plane/db/models/view.py +++ b/apiserver/plane/db/models/view.py @@ -19,6 +19,7 @@ def get_default_filters(): "subscriber": None, } + def get_default_display_filters(): return { "group_by": None, @@ -30,6 +31,7 @@ def get_default_display_filters(): "calendar_date_range": "", } + def get_default_display_properties(): return { "assignee": True, @@ -47,6 +49,7 @@ def get_default_display_properties(): "updated_on": True, } + class GlobalView(BaseModel): workspace = models.ForeignKey( "db.Workspace", on_delete=models.CASCADE, related_name="global_views" @@ -65,7 +68,7 @@ class GlobalView(BaseModel): verbose_name_plural = "Global Views" db_table = "global_views" ordering = ("-created_at",) - + def save(self, *args, **kwargs): if self._state.adding: largest_sort_order = GlobalView.objects.filter( @@ -87,7 +90,9 @@ class IssueView(WorkspaceBaseModel): query = models.JSONField(verbose_name="View Query") filters = models.JSONField(default=dict) display_filters = models.JSONField(default=get_default_display_filters) - display_properties = models.JSONField(default=get_default_display_properties) + display_properties = models.JSONField( + default=get_default_display_properties + ) access = models.PositiveSmallIntegerField( default=1, choices=((0, "Private"), (1, "Public")) ) diff --git a/apiserver/plane/db/models/webhook.py b/apiserver/plane/db/models/webhook.py index ea2b508e5..fbe74d03a 100644 --- a/apiserver/plane/db/models/webhook.py +++ b/apiserver/plane/db/models/webhook.py @@ -17,7 +17,9 @@ def generate_token(): def validate_schema(value): parsed_url = urlparse(value) if parsed_url.scheme not in ["http", "https"]: - raise ValidationError("Invalid schema. Only HTTP and HTTPS are allowed.") + raise ValidationError( + "Invalid schema. Only HTTP and HTTPS are allowed." + ) def validate_domain(value): @@ -63,7 +65,9 @@ class WebhookLog(BaseModel): "db.Workspace", on_delete=models.CASCADE, related_name="webhook_logs" ) # Associated webhook - webhook = models.ForeignKey(Webhook, on_delete=models.CASCADE, related_name="logs") + webhook = models.ForeignKey( + Webhook, on_delete=models.CASCADE, related_name="logs" + ) # Basic request details event_type = models.CharField(max_length=255, blank=True, null=True) diff --git a/apiserver/plane/db/models/workspace.py b/apiserver/plane/db/models/workspace.py index f0d64ecae..7e5d6d90b 100644 --- a/apiserver/plane/db/models/workspace.py +++ b/apiserver/plane/db/models/workspace.py @@ -54,6 +54,7 @@ def get_default_props(): }, } + def get_default_filters(): return { "priority": None, @@ -67,6 +68,7 @@ def get_default_filters(): "subscriber": None, } + def get_default_display_filters(): return { "display_filters": { @@ -80,6 +82,7 @@ def get_default_display_filters(): }, } + def get_default_display_properties(): return { "display_properties": { @@ -134,7 +137,14 @@ class Workspace(BaseModel): on_delete=models.CASCADE, related_name="owner_workspace", ) - slug = models.SlugField(max_length=48, db_index=True, unique=True, validators=[slug_validator,]) + slug = models.SlugField( + max_length=48, + db_index=True, + unique=True, + validators=[ + slug_validator, + ], + ) organization_size = models.CharField(max_length=20, blank=True, null=True) def __str__(self): @@ -153,20 +163,26 @@ class WorkspaceBaseModel(BaseModel): "db.Workspace", models.CASCADE, related_name="workspace_%(class)s" ) project = models.ForeignKey( - "db.Project", models.CASCADE, related_name="project_%(class)s", null=True + "db.Project", + models.CASCADE, + related_name="project_%(class)s", + null=True, ) class Meta: abstract = True - + def save(self, *args, **kwargs): if self.project: self.workspace = self.project.workspace super(WorkspaceBaseModel, self).save(*args, **kwargs) + class WorkspaceMember(BaseModel): workspace = models.ForeignKey( - "db.Workspace", on_delete=models.CASCADE, related_name="workspace_member" + "db.Workspace", + on_delete=models.CASCADE, + related_name="workspace_member", ) member = models.ForeignKey( settings.AUTH_USER_MODEL, @@ -194,7 +210,9 @@ class WorkspaceMember(BaseModel): class WorkspaceMemberInvite(BaseModel): workspace = models.ForeignKey( - "db.Workspace", on_delete=models.CASCADE, related_name="workspace_member_invite" + "db.Workspace", + on_delete=models.CASCADE, + related_name="workspace_member_invite", ) email = models.CharField(max_length=255) accepted = models.BooleanField(default=False) @@ -244,9 +262,13 @@ class TeamMember(BaseModel): workspace = models.ForeignKey( Workspace, on_delete=models.CASCADE, related_name="team_member" ) - team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="team_member") + team = models.ForeignKey( + Team, on_delete=models.CASCADE, related_name="team_member" + ) member = models.ForeignKey( - settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="team_member" + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="team_member", ) def __str__(self): @@ -266,7 +288,9 @@ class WorkspaceTheme(BaseModel): ) name = models.CharField(max_length=300) actor = models.ForeignKey( - settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="themes" + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="themes", ) colors = models.JSONField(default=dict) @@ -283,7 +307,9 @@ class WorkspaceTheme(BaseModel): class WorkspaceUserProperties(BaseModel): workspace = models.ForeignKey( - "db.Workspace", on_delete=models.CASCADE, related_name="workspace_user_properties" + "db.Workspace", + on_delete=models.CASCADE, + related_name="workspace_user_properties", ) user = models.ForeignKey( settings.AUTH_USER_MODEL, @@ -292,8 +318,9 @@ class WorkspaceUserProperties(BaseModel): ) filters = models.JSONField(default=get_default_filters) display_filters = models.JSONField(default=get_default_display_filters) - display_properties = models.JSONField(default=get_default_display_properties) - + display_properties = models.JSONField( + default=get_default_display_properties + ) class Meta: unique_together = ["workspace", "user"] @@ -303,4 +330,4 @@ class WorkspaceUserProperties(BaseModel): ordering = ("-created_at",) def __str__(self): - return f"{self.workspace.name} {self.user.email}" \ No newline at end of file + return f"{self.workspace.name} {self.user.email}" diff --git a/apiserver/plane/license/api/permissions/instance.py b/apiserver/plane/license/api/permissions/instance.py index dff16605a..9ee85404b 100644 --- a/apiserver/plane/license/api/permissions/instance.py +++ b/apiserver/plane/license/api/permissions/instance.py @@ -7,7 +7,6 @@ from plane.license.models import Instance, InstanceAdmin class InstanceAdminPermission(BasePermission): def has_permission(self, request, view): - if request.user.is_anonymous: return False diff --git a/apiserver/plane/license/api/serializers/__init__.py b/apiserver/plane/license/api/serializers/__init__.py index b658ff148..e6beda0e9 100644 --- a/apiserver/plane/license/api/serializers/__init__.py +++ b/apiserver/plane/license/api/serializers/__init__.py @@ -1 +1,5 @@ -from .instance import InstanceSerializer, InstanceAdminSerializer, InstanceConfigurationSerializer \ No newline at end of file +from .instance import ( + InstanceSerializer, + InstanceAdminSerializer, + InstanceConfigurationSerializer, +) diff --git a/apiserver/plane/license/api/serializers/instance.py b/apiserver/plane/license/api/serializers/instance.py index 173d718d9..8a99acbae 100644 --- a/apiserver/plane/license/api/serializers/instance.py +++ b/apiserver/plane/license/api/serializers/instance.py @@ -4,8 +4,11 @@ from plane.app.serializers import BaseSerializer from plane.app.serializers import UserAdminLiteSerializer from plane.license.utils.encryption import decrypt_data + class InstanceSerializer(BaseSerializer): - primary_owner_details = UserAdminLiteSerializer(source="primary_owner", read_only=True) + primary_owner_details = UserAdminLiteSerializer( + source="primary_owner", read_only=True + ) class Meta: model = Instance @@ -34,8 +37,8 @@ class InstanceAdminSerializer(BaseSerializer): "user", ] -class InstanceConfigurationSerializer(BaseSerializer): +class InstanceConfigurationSerializer(BaseSerializer): class Meta: model = InstanceConfiguration fields = "__all__" diff --git a/apiserver/plane/license/api/views/instance.py b/apiserver/plane/license/api/views/instance.py index c88b3b75f..112c68bc8 100644 --- a/apiserver/plane/license/api/views/instance.py +++ b/apiserver/plane/license/api/views/instance.py @@ -61,7 +61,9 @@ class InstanceEndpoint(BaseAPIView): def patch(self, request): # Get the instance instance = Instance.objects.first() - serializer = InstanceSerializer(instance, data=request.data, partial=True) + serializer = InstanceSerializer( + instance, data=request.data, partial=True + ) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) @@ -80,7 +82,8 @@ class InstanceAdminEndpoint(BaseAPIView): if not email: return Response( - {"error": "Email is required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Email is required"}, + status=status.HTTP_400_BAD_REQUEST, ) instance = Instance.objects.first() @@ -114,7 +117,9 @@ class InstanceAdminEndpoint(BaseAPIView): def delete(self, request, pk): instance = Instance.objects.first() - instance_admin = InstanceAdmin.objects.filter(instance=instance, pk=pk).delete() + instance_admin = InstanceAdmin.objects.filter( + instance=instance, pk=pk + ).delete() return Response(status=status.HTTP_204_NO_CONTENT) @@ -125,7 +130,9 @@ class InstanceConfigurationEndpoint(BaseAPIView): def get(self, request): instance_configurations = InstanceConfiguration.objects.all() - serializer = InstanceConfigurationSerializer(instance_configurations, many=True) + serializer = InstanceConfigurationSerializer( + instance_configurations, many=True + ) return Response(serializer.data, status=status.HTTP_200_OK) def patch(self, request): diff --git a/apiserver/plane/license/management/commands/configure_instance.py b/apiserver/plane/license/management/commands/configure_instance.py index 67137d0d9..f81d98cba 100644 --- a/apiserver/plane/license/management/commands/configure_instance.py +++ b/apiserver/plane/license/management/commands/configure_instance.py @@ -21,7 +21,7 @@ class Command(BaseCommand): "key": "ENABLE_SIGNUP", "value": os.environ.get("ENABLE_SIGNUP", "1"), "category": "AUTHENTICATION", - "is_encrypted": False, + "is_encrypted": False, }, { "key": "ENABLE_EMAIL_PASSWORD", @@ -128,5 +128,7 @@ class Command(BaseCommand): ) else: self.stdout.write( - self.style.WARNING(f"{obj.key} configuration already exists") + self.style.WARNING( + f"{obj.key} configuration already exists" + ) ) diff --git a/apiserver/plane/license/management/commands/register_instance.py b/apiserver/plane/license/management/commands/register_instance.py index e6cfa7167..889cd46dc 100644 --- a/apiserver/plane/license/management/commands/register_instance.py +++ b/apiserver/plane/license/management/commands/register_instance.py @@ -12,13 +12,15 @@ from django.conf import settings from plane.license.models import Instance from plane.db.models import User + class Command(BaseCommand): help = "Check if instance in registered else register" def add_arguments(self, parser): # Positional argument - parser.add_argument('machine_signature', type=str, help='Machine signature') - + parser.add_argument( + "machine_signature", type=str, help="Machine signature" + ) def handle(self, *args, **options): # Check if the instance is registered @@ -30,7 +32,9 @@ class Command(BaseCommand): # Load JSON content from the file data = json.load(file) - machine_signature = options.get("machine_signature", "machine-signature") + machine_signature = options.get( + "machine_signature", "machine-signature" + ) if not machine_signature: raise CommandError("Machine signature is required") @@ -52,15 +56,9 @@ class Command(BaseCommand): user_count=payload.get("user_count", 0), ) - self.stdout.write( - self.style.SUCCESS( - f"Instance registered" - ) - ) + self.stdout.write(self.style.SUCCESS(f"Instance registered")) else: self.stdout.write( - self.style.SUCCESS( - f"Instance already registered" - ) + self.style.SUCCESS(f"Instance already registered") ) return diff --git a/apiserver/plane/license/migrations/0001_initial.py b/apiserver/plane/license/migrations/0001_initial.py index c8b5f1f02..4eed3adf7 100644 --- a/apiserver/plane/license/migrations/0001_initial.py +++ b/apiserver/plane/license/migrations/0001_initial.py @@ -7,7 +7,6 @@ import uuid class Migration(migrations.Migration): - initial = True dependencies = [ @@ -16,74 +15,220 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Instance', + name="Instance", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('instance_name', models.CharField(max_length=255)), - ('whitelist_emails', models.TextField(blank=True, null=True)), - ('instance_id', models.CharField(max_length=25, unique=True)), - ('license_key', models.CharField(blank=True, max_length=256, null=True)), - ('api_key', models.CharField(max_length=16)), - ('version', models.CharField(max_length=10)), - ('last_checked_at', models.DateTimeField()), - ('namespace', models.CharField(blank=True, max_length=50, null=True)), - ('is_telemetry_enabled', models.BooleanField(default=True)), - ('is_support_required', models.BooleanField(default=True)), - ('is_setup_done', models.BooleanField(default=False)), - ('is_signup_screen_visited', models.BooleanField(default=False)), - ('user_count', models.PositiveBigIntegerField(default=0)), - ('is_verified', models.BooleanField(default=False)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("instance_name", models.CharField(max_length=255)), + ("whitelist_emails", models.TextField(blank=True, null=True)), + ("instance_id", models.CharField(max_length=25, unique=True)), + ( + "license_key", + models.CharField(blank=True, max_length=256, null=True), + ), + ("api_key", models.CharField(max_length=16)), + ("version", models.CharField(max_length=10)), + ("last_checked_at", models.DateTimeField()), + ( + "namespace", + models.CharField(blank=True, max_length=50, null=True), + ), + ("is_telemetry_enabled", models.BooleanField(default=True)), + ("is_support_required", models.BooleanField(default=True)), + ("is_setup_done", models.BooleanField(default=False)), + ( + "is_signup_screen_visited", + models.BooleanField(default=False), + ), + ("user_count", models.PositiveBigIntegerField(default=0)), + ("is_verified", models.BooleanField(default=False)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), ], options={ - 'verbose_name': 'Instance', - 'verbose_name_plural': 'Instances', - 'db_table': 'instances', - 'ordering': ('-created_at',), + "verbose_name": "Instance", + "verbose_name_plural": "Instances", + "db_table": "instances", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='InstanceConfiguration', + name="InstanceConfiguration", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('key', models.CharField(max_length=100, unique=True)), - ('value', models.TextField(blank=True, default=None, null=True)), - ('category', models.TextField()), - ('is_encrypted', models.BooleanField(default=False)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("key", models.CharField(max_length=100, unique=True)), + ( + "value", + models.TextField(blank=True, default=None, null=True), + ), + ("category", models.TextField()), + ("is_encrypted", models.BooleanField(default=False)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), ], options={ - 'verbose_name': 'Instance Configuration', - 'verbose_name_plural': 'Instance Configurations', - 'db_table': 'instance_configurations', - 'ordering': ('-created_at',), + "verbose_name": "Instance Configuration", + "verbose_name_plural": "Instance Configurations", + "db_table": "instance_configurations", + "ordering": ("-created_at",), }, ), migrations.CreateModel( - name='InstanceAdmin', + name="InstanceAdmin", fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), - ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), - ('role', models.PositiveIntegerField(choices=[(20, 'Admin')], default=20)), - ('is_verified', models.BooleanField(default=False)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='admins', to='license.instance')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), - ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='instance_owner', to=settings.AUTH_USER_MODEL)), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Created At" + ), + ), + ( + "updated_at", + models.DateTimeField( + auto_now=True, verbose_name="Last Modified At" + ), + ), + ( + "id", + models.UUIDField( + db_index=True, + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "role", + models.PositiveIntegerField( + choices=[(20, "Admin")], default=20 + ), + ), + ("is_verified", models.BooleanField(default=False)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Created By", + ), + ), + ( + "instance", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="admins", + to="license.instance", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + verbose_name="Last Modified By", + ), + ), + ( + "user", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="instance_owner", + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'verbose_name': 'Instance Admin', - 'verbose_name_plural': 'Instance Admins', - 'db_table': 'instance_admins', - 'ordering': ('-created_at',), - 'unique_together': {('instance', 'user')}, + "verbose_name": "Instance Admin", + "verbose_name_plural": "Instance Admins", + "db_table": "instance_admins", + "ordering": ("-created_at",), + "unique_together": {("instance", "user")}, }, ), ] diff --git a/apiserver/plane/license/models/__init__.py b/apiserver/plane/license/models/__init__.py index 28f2c4352..0f35f718d 100644 --- a/apiserver/plane/license/models/__init__.py +++ b/apiserver/plane/license/models/__init__.py @@ -1 +1 @@ -from .instance import Instance, InstanceAdmin, InstanceConfiguration \ No newline at end of file +from .instance import Instance, InstanceAdmin, InstanceConfiguration diff --git a/apiserver/plane/license/models/instance.py b/apiserver/plane/license/models/instance.py index 86845c34b..b8957e44f 100644 --- a/apiserver/plane/license/models/instance.py +++ b/apiserver/plane/license/models/instance.py @@ -5,9 +5,7 @@ from django.conf import settings # Module imports from plane.db.models import BaseModel -ROLE_CHOICES = ( - (20, "Admin"), -) +ROLE_CHOICES = ((20, "Admin"),) class Instance(BaseModel): @@ -46,7 +44,9 @@ class InstanceAdmin(BaseModel): null=True, related_name="instance_owner", ) - instance = models.ForeignKey(Instance, on_delete=models.CASCADE, related_name="admins") + instance = models.ForeignKey( + Instance, on_delete=models.CASCADE, related_name="admins" + ) role = models.PositiveIntegerField(choices=ROLE_CHOICES, default=20) is_verified = models.BooleanField(default=False) @@ -70,4 +70,3 @@ class InstanceConfiguration(BaseModel): verbose_name_plural = "Instance Configurations" db_table = "instance_configurations" ordering = ("-created_at",) - diff --git a/apiserver/plane/license/utils/encryption.py b/apiserver/plane/license/utils/encryption.py index c2d369c2e..11bd9000e 100644 --- a/apiserver/plane/license/utils/encryption.py +++ b/apiserver/plane/license/utils/encryption.py @@ -6,9 +6,10 @@ from cryptography.fernet import Fernet def derive_key(secret_key): # Use a key derivation function to get a suitable encryption key - dk = hashlib.pbkdf2_hmac('sha256', secret_key.encode(), b'salt', 100000) + dk = hashlib.pbkdf2_hmac("sha256", secret_key.encode(), b"salt", 100000) return base64.urlsafe_b64encode(dk) + # Encrypt data def encrypt_data(data): if data: @@ -18,11 +19,14 @@ def encrypt_data(data): else: return "" -# Decrypt data + +# Decrypt data def decrypt_data(encrypted_data): if encrypted_data: cipher_suite = Fernet(derive_key(settings.SECRET_KEY)) - decrypted_data = cipher_suite.decrypt(encrypted_data.encode()) # Convert string back to bytes + decrypted_data = cipher_suite.decrypt( + encrypted_data.encode() + ) # Convert string back to bytes return decrypted_data.decode() else: - return "" \ No newline at end of file + return "" diff --git a/apiserver/plane/license/utils/instance_value.py b/apiserver/plane/license/utils/instance_value.py index e56525893..bc4fd5d21 100644 --- a/apiserver/plane/license/utils/instance_value.py +++ b/apiserver/plane/license/utils/instance_value.py @@ -22,7 +22,9 @@ def get_configuration_value(keys): for item in instance_configuration: if key.get("key") == item.get("key"): if item.get("is_encrypted", False): - environment_list.append(decrypt_data(item.get("value"))) + environment_list.append( + decrypt_data(item.get("value")) + ) else: environment_list.append(item.get("value")) @@ -32,40 +34,41 @@ def get_configuration_value(keys): else: # Get the configuration from os for key in keys: - environment_list.append(os.environ.get(key.get("key"), key.get("default"))) + environment_list.append( + os.environ.get(key.get("key"), key.get("default")) + ) return tuple(environment_list) def get_email_configuration(): - return ( - get_configuration_value( - [ - { - "key": "EMAIL_HOST", - "default": os.environ.get("EMAIL_HOST"), - }, - { - "key": "EMAIL_HOST_USER", - "default": os.environ.get("EMAIL_HOST_USER"), - }, - { - "key": "EMAIL_HOST_PASSWORD", - "default": os.environ.get("EMAIL_HOST_PASSWORD"), - }, - { - "key": "EMAIL_PORT", - "default": os.environ.get("EMAIL_PORT", 587), - }, - { - "key": "EMAIL_USE_TLS", - "default": os.environ.get("EMAIL_USE_TLS", "1"), - }, - { - "key": "EMAIL_FROM", - "default": os.environ.get("EMAIL_FROM", "Team Plane "), - }, - ] - ) + return get_configuration_value( + [ + { + "key": "EMAIL_HOST", + "default": os.environ.get("EMAIL_HOST"), + }, + { + "key": "EMAIL_HOST_USER", + "default": os.environ.get("EMAIL_HOST_USER"), + }, + { + "key": "EMAIL_HOST_PASSWORD", + "default": os.environ.get("EMAIL_HOST_PASSWORD"), + }, + { + "key": "EMAIL_PORT", + "default": os.environ.get("EMAIL_PORT", 587), + }, + { + "key": "EMAIL_USE_TLS", + "default": os.environ.get("EMAIL_USE_TLS", "1"), + }, + { + "key": "EMAIL_FROM", + "default": os.environ.get( + "EMAIL_FROM", "Team Plane " + ), + }, + ] ) - diff --git a/apiserver/plane/middleware/api_log_middleware.py b/apiserver/plane/middleware/api_log_middleware.py index a1894fad5..a49d43b55 100644 --- a/apiserver/plane/middleware/api_log_middleware.py +++ b/apiserver/plane/middleware/api_log_middleware.py @@ -23,9 +23,13 @@ class APITokenLogMiddleware: method=request.method, query_params=request.META.get("QUERY_STRING", ""), headers=str(request.headers), - body=(request_body.decode('utf-8') if request_body else None), + body=( + request_body.decode("utf-8") if request_body else None + ), response_body=( - response.content.decode("utf-8") if response.content else None + response.content.decode("utf-8") + if response.content + else None ), response_code=response.status_code, ip_address=request.META.get("REMOTE_ADDR", None), diff --git a/apiserver/plane/middleware/apps.py b/apiserver/plane/middleware/apps.py index 3da4958c1..9deac8091 100644 --- a/apiserver/plane/middleware/apps.py +++ b/apiserver/plane/middleware/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class Middleware(AppConfig): - name = 'plane.middleware' + name = "plane.middleware" diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index 971ed5543..c6e650ec7 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -71,13 +71,19 @@ REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ( "rest_framework_simplejwt.authentication.JWTAuthentication", ), - "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",), + "DEFAULT_PERMISSION_CLASSES": ( + "rest_framework.permissions.IsAuthenticated", + ), "DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",), - "DEFAULT_FILTER_BACKENDS": ("django_filters.rest_framework.DjangoFilterBackend",), + "DEFAULT_FILTER_BACKENDS": ( + "django_filters.rest_framework.DjangoFilterBackend", + ), } # Django Auth Backend -AUTHENTICATION_BACKENDS = ("django.contrib.auth.backends.ModelBackend",) # default +AUTHENTICATION_BACKENDS = ( + "django.contrib.auth.backends.ModelBackend", +) # default # Root Urls ROOT_URLCONF = "plane.urls" @@ -229,9 +235,9 @@ AWS_REGION = os.environ.get("AWS_REGION", "") AWS_DEFAULT_ACL = "public-read" AWS_QUERYSTRING_AUTH = False AWS_S3_FILE_OVERWRITE = False -AWS_S3_ENDPOINT_URL = os.environ.get("AWS_S3_ENDPOINT_URL", None) or os.environ.get( - "MINIO_ENDPOINT_URL", None -) +AWS_S3_ENDPOINT_URL = os.environ.get( + "AWS_S3_ENDPOINT_URL", None +) or os.environ.get("MINIO_ENDPOINT_URL", None) if AWS_S3_ENDPOINT_URL: parsed_url = urlparse(os.environ.get("WEB_URL", "http://localhost")) AWS_S3_CUSTOM_DOMAIN = f"{parsed_url.netloc}/{AWS_STORAGE_BUCKET_NAME}" @@ -274,9 +280,7 @@ CELERY_ACCEPT_CONTENT = ["application/json"] if REDIS_SSL: redis_url = os.environ.get("REDIS_URL") - broker_url = ( - f"{redis_url}?ssl_cert_reqs={ssl.CERT_NONE.name}&ssl_ca_certs={certifi.where()}" - ) + broker_url = f"{redis_url}?ssl_cert_reqs={ssl.CERT_NONE.name}&ssl_ca_certs={certifi.where()}" CELERY_BROKER_URL = broker_url CELERY_RESULT_BACKEND = broker_url else: @@ -331,7 +335,8 @@ POSTHOG_HOST = os.environ.get("POSTHOG_HOST", False) # instance key INSTANCE_KEY = os.environ.get( - "INSTANCE_KEY", "ae6517d563dfc13d8270bd45cf17b08f70b37d989128a9dab46ff687603333c3" + "INSTANCE_KEY", + "ae6517d563dfc13d8270bd45cf17b08f70b37d989128a9dab46ff687603333c3", ) # Skip environment variable configuration diff --git a/apiserver/plane/settings/test.py b/apiserver/plane/settings/test.py index 34ae16555..1e2a55144 100644 --- a/apiserver/plane/settings/test.py +++ b/apiserver/plane/settings/test.py @@ -1,9 +1,11 @@ """Test Settings""" -from .common import * # noqa +from .common import * # noqa DEBUG = True # Send it in a dummy outbox EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend" -INSTALLED_APPS.append("plane.tests",) +INSTALLED_APPS.append( + "plane.tests", +) diff --git a/apiserver/plane/space/serializer/base.py b/apiserver/plane/space/serializer/base.py index 89c9725d9..4b92b06fc 100644 --- a/apiserver/plane/space/serializer/base.py +++ b/apiserver/plane/space/serializer/base.py @@ -4,8 +4,8 @@ from rest_framework import serializers class BaseSerializer(serializers.ModelSerializer): id = serializers.PrimaryKeyRelatedField(read_only=True) -class DynamicBaseSerializer(BaseSerializer): +class DynamicBaseSerializer(BaseSerializer): def __init__(self, *args, **kwargs): # If 'fields' is provided in the arguments, remove it and store it separately. # This is done so as not to pass this custom argument up to the superclass. @@ -31,7 +31,7 @@ class DynamicBaseSerializer(BaseSerializer): # loop through its keys and values. if isinstance(field_name, dict): for key, value in field_name.items(): - # If the value of this nested field is a list, + # If the value of this nested field is a list, # perform a recursive filter on it. if isinstance(value, list): self._filter_fields(self.fields[key], value) @@ -52,7 +52,7 @@ class DynamicBaseSerializer(BaseSerializer): allowed = set(allowed) # Remove fields from the serializer that aren't in the 'allowed' list. - for field_name in (existing - allowed): + for field_name in existing - allowed: self.fields.pop(field_name) return self.fields diff --git a/apiserver/plane/space/serializer/cycle.py b/apiserver/plane/space/serializer/cycle.py index ab4d9441d..d4f5d86e0 100644 --- a/apiserver/plane/space/serializer/cycle.py +++ b/apiserver/plane/space/serializer/cycle.py @@ -4,6 +4,7 @@ from plane.db.models import ( Cycle, ) + class CycleBaseSerializer(BaseSerializer): class Meta: model = Cycle @@ -15,4 +16,4 @@ class CycleBaseSerializer(BaseSerializer): "updated_by", "created_at", "updated_at", - ] \ No newline at end of file + ] diff --git a/apiserver/plane/space/serializer/inbox.py b/apiserver/plane/space/serializer/inbox.py index 05d99ac55..48ec7c89d 100644 --- a/apiserver/plane/space/serializer/inbox.py +++ b/apiserver/plane/space/serializer/inbox.py @@ -36,12 +36,16 @@ class InboxIssueLiteSerializer(BaseSerializer): class IssueStateInboxSerializer(BaseSerializer): state_detail = StateLiteSerializer(read_only=True, source="state") project_detail = ProjectLiteSerializer(read_only=True, source="project") - label_details = LabelLiteSerializer(read_only=True, source="labels", many=True) - assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True) + label_details = LabelLiteSerializer( + read_only=True, source="labels", many=True + ) + assignee_details = UserLiteSerializer( + read_only=True, source="assignees", many=True + ) sub_issues_count = serializers.IntegerField(read_only=True) bridge_id = serializers.UUIDField(read_only=True) issue_inbox = InboxIssueLiteSerializer(read_only=True, many=True) class Meta: model = Issue - fields = "__all__" \ No newline at end of file + fields = "__all__" diff --git a/apiserver/plane/space/serializer/issue.py b/apiserver/plane/space/serializer/issue.py index 1a9a872ef..c7b044b21 100644 --- a/apiserver/plane/space/serializer/issue.py +++ b/apiserver/plane/space/serializer/issue.py @@ -1,4 +1,3 @@ - # Django imports from django.utils import timezone @@ -47,7 +46,9 @@ class IssueStateFlatSerializer(BaseSerializer): class LabelSerializer(BaseSerializer): - workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) + workspace_detail = WorkspaceLiteSerializer( + source="workspace", read_only=True + ) project_detail = ProjectLiteSerializer(source="project", read_only=True) class Meta: @@ -74,7 +75,9 @@ class IssueProjectLiteSerializer(BaseSerializer): class IssueRelationSerializer(BaseSerializer): - issue_detail = IssueProjectLiteSerializer(read_only=True, source="related_issue") + issue_detail = IssueProjectLiteSerializer( + read_only=True, source="related_issue" + ) class Meta: model = IssueRelation @@ -83,13 +86,14 @@ class IssueRelationSerializer(BaseSerializer): "relation_type", "related_issue", "issue", - "id" + "id", ] read_only_fields = [ "workspace", "project", ] + class RelatedIssueSerializer(BaseSerializer): issue_detail = IssueProjectLiteSerializer(read_only=True, source="issue") @@ -100,7 +104,7 @@ class RelatedIssueSerializer(BaseSerializer): "relation_type", "related_issue", "issue", - "id" + "id", ] read_only_fields = [ "workspace", @@ -159,7 +163,8 @@ class IssueLinkSerializer(BaseSerializer): # Validation if url already exists def create(self, validated_data): if IssueLink.objects.filter( - url=validated_data.get("url"), issue_id=validated_data.get("issue_id") + url=validated_data.get("url"), + issue_id=validated_data.get("issue_id"), ).exists(): raise serializers.ValidationError( {"error": "URL already exists for this Issue"} @@ -183,9 +188,8 @@ class IssueAttachmentSerializer(BaseSerializer): class IssueReactionSerializer(BaseSerializer): - actor_detail = UserLiteSerializer(read_only=True, source="actor") - + class Meta: model = IssueReaction fields = "__all__" @@ -202,9 +206,15 @@ class IssueSerializer(BaseSerializer): state_detail = StateSerializer(read_only=True, source="state") parent_detail = IssueStateFlatSerializer(read_only=True, source="parent") label_details = LabelSerializer(read_only=True, source="labels", many=True) - assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True) - related_issues = IssueRelationSerializer(read_only=True, source="issue_relation", many=True) - issue_relations = RelatedIssueSerializer(read_only=True, source="issue_related", many=True) + assignee_details = UserLiteSerializer( + read_only=True, source="assignees", many=True + ) + related_issues = IssueRelationSerializer( + read_only=True, source="issue_relation", many=True + ) + issue_relations = RelatedIssueSerializer( + read_only=True, source="issue_related", many=True + ) issue_cycle = IssueCycleDetailSerializer(read_only=True) issue_module = IssueModuleDetailSerializer(read_only=True) issue_link = IssueLinkSerializer(read_only=True, many=True) @@ -261,8 +271,12 @@ class IssueCommentSerializer(BaseSerializer): actor_detail = UserLiteSerializer(read_only=True, source="actor") issue_detail = IssueFlatSerializer(read_only=True, source="issue") project_detail = ProjectLiteSerializer(read_only=True, source="project") - workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") - comment_reactions = CommentReactionLiteSerializer(read_only=True, many=True) + workspace_detail = WorkspaceLiteSerializer( + read_only=True, source="workspace" + ) + comment_reactions = CommentReactionLiteSerializer( + read_only=True, many=True + ) is_member = serializers.BooleanField(read_only=True) class Meta: @@ -285,7 +299,9 @@ class IssueCreateSerializer(BaseSerializer): state_detail = StateSerializer(read_only=True, source="state") created_by_detail = UserLiteSerializer(read_only=True, source="created_by") project_detail = ProjectLiteSerializer(read_only=True, source="project") - workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") + workspace_detail = WorkspaceLiteSerializer( + read_only=True, source="workspace" + ) assignees = serializers.ListField( child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()), @@ -313,8 +329,10 @@ class IssueCreateSerializer(BaseSerializer): def to_representation(self, instance): data = super().to_representation(instance) - data['assignees'] = [str(assignee.id) for assignee in instance.assignees.all()] - data['labels'] = [str(label.id) for label in instance.labels.all()] + data["assignees"] = [ + str(assignee.id) for assignee in instance.assignees.all() + ] + data["labels"] = [str(label.id) for label in instance.labels.all()] return data def validate(self, data): @@ -323,7 +341,9 @@ class IssueCreateSerializer(BaseSerializer): and data.get("target_date", None) is not None and data.get("start_date", None) > data.get("target_date", None) ): - raise serializers.ValidationError("Start date cannot exceed target date") + raise serializers.ValidationError( + "Start date cannot exceed target date" + ) return data def create(self, validated_data): @@ -432,12 +452,11 @@ class IssueCreateSerializer(BaseSerializer): # Time updation occues even when other related models are updated instance.updated_at = timezone.now() return super().update(instance, validated_data) - + class IssueReactionSerializer(BaseSerializer): - actor_detail = UserLiteSerializer(read_only=True, source="actor") - + class Meta: model = IssueReaction fields = "__all__" @@ -457,19 +476,27 @@ class CommentReactionSerializer(BaseSerializer): class IssueVoteSerializer(BaseSerializer): - actor_detail = UserLiteSerializer(read_only=True, source="actor") class Meta: model = IssueVote - fields = ["issue", "vote", "workspace", "project", "actor", "actor_detail"] + fields = [ + "issue", + "vote", + "workspace", + "project", + "actor", + "actor_detail", + ] read_only_fields = fields class IssuePublicSerializer(BaseSerializer): project_detail = ProjectLiteSerializer(read_only=True, source="project") state_detail = StateLiteSerializer(read_only=True, source="state") - reactions = IssueReactionSerializer(read_only=True, many=True, source="issue_reactions") + reactions = IssueReactionSerializer( + read_only=True, many=True, source="issue_reactions" + ) votes = IssueVoteSerializer(read_only=True, many=True) class Meta: @@ -500,7 +527,3 @@ class LabelLiteSerializer(BaseSerializer): "name", "color", ] - - - - diff --git a/apiserver/plane/space/serializer/module.py b/apiserver/plane/space/serializer/module.py index 39ce9ec32..dda1861d1 100644 --- a/apiserver/plane/space/serializer/module.py +++ b/apiserver/plane/space/serializer/module.py @@ -4,6 +4,7 @@ from plane.db.models import ( Module, ) + class ModuleBaseSerializer(BaseSerializer): class Meta: model = Module @@ -15,4 +16,4 @@ class ModuleBaseSerializer(BaseSerializer): "updated_by", "created_at", "updated_at", - ] \ No newline at end of file + ] diff --git a/apiserver/plane/space/serializer/state.py b/apiserver/plane/space/serializer/state.py index 903bcc2f4..55064ed0e 100644 --- a/apiserver/plane/space/serializer/state.py +++ b/apiserver/plane/space/serializer/state.py @@ -6,7 +6,6 @@ from plane.db.models import ( class StateSerializer(BaseSerializer): - class Meta: model = State fields = "__all__" diff --git a/apiserver/plane/space/serializer/workspace.py b/apiserver/plane/space/serializer/workspace.py index ecf99079f..a31bb3744 100644 --- a/apiserver/plane/space/serializer/workspace.py +++ b/apiserver/plane/space/serializer/workspace.py @@ -4,6 +4,7 @@ from plane.db.models import ( Workspace, ) + class WorkspaceLiteSerializer(BaseSerializer): class Meta: model = Workspace @@ -12,4 +13,4 @@ class WorkspaceLiteSerializer(BaseSerializer): "slug", "id", ] - read_only_fields = fields \ No newline at end of file + read_only_fields = fields diff --git a/apiserver/plane/space/views/base.py b/apiserver/plane/space/views/base.py index 7a819095b..b75f3dd18 100644 --- a/apiserver/plane/space/views/base.py +++ b/apiserver/plane/space/views/base.py @@ -59,7 +59,9 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): return self.model.objects.all() except Exception as e: capture_exception(e) - raise APIException("Please check the view", status.HTTP_400_BAD_REQUEST) + raise APIException( + "Please check the view", status.HTTP_400_BAD_REQUEST + ) def handle_exception(self, exc): """ @@ -83,7 +85,9 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): ) if isinstance(e, ObjectDoesNotExist): - model_name = str(exc).split(" matching query does not exist.")[0] + model_name = str(exc).split(" matching query does not exist.")[ + 0 + ] return Response( {"error": f"The required object does not exist."}, status=status.HTTP_404_NOT_FOUND, @@ -95,11 +99,13 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): {"error": "The required key does not exist."}, status=status.HTTP_400_BAD_REQUEST, ) - + print(e) if settings.DEBUG else print("Server Error") capture_exception(e) - return Response({"error": "Something went wrong please try again later"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) def dispatch(self, request, *args, **kwargs): try: @@ -176,15 +182,20 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): {"error": f"The required object does not exist."}, status=status.HTTP_404_NOT_FOUND, ) - + if isinstance(e, KeyError): - return Response({"error": "The required key does not exist."}, status=status.HTTP_400_BAD_REQUEST) + return Response( + {"error": "The required key does not exist."}, + status=status.HTTP_400_BAD_REQUEST, + ) if settings.DEBUG: print(e) capture_exception(e) - return Response({"error": "Something went wrong please try again later"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) def dispatch(self, request, *args, **kwargs): try: diff --git a/apiserver/plane/space/views/inbox.py b/apiserver/plane/space/views/inbox.py index 53960f672..2bf8f8303 100644 --- a/apiserver/plane/space/views/inbox.py +++ b/apiserver/plane/space/views/inbox.py @@ -48,7 +48,8 @@ class InboxIssuePublicViewSet(BaseViewSet): super() .get_queryset() .filter( - Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True), + Q(snoozed_till__gte=timezone.now()) + | Q(snoozed_till__isnull=True), project_id=self.kwargs.get("project_id"), workspace__slug=self.kwargs.get("slug"), inbox_id=self.kwargs.get("inbox_id"), @@ -80,7 +81,9 @@ class InboxIssuePublicViewSet(BaseViewSet): .prefetch_related("assignees", "labels") .order_by("issue_inbox__snoozed_till", "issue_inbox__status") .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -92,7 +95,9 @@ class InboxIssuePublicViewSet(BaseViewSet): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -124,7 +129,8 @@ class InboxIssuePublicViewSet(BaseViewSet): if not request.data.get("issue", {}).get("name", False): return Response( - {"error": "Name is required"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Name is required"}, + status=status.HTTP_400_BAD_REQUEST, ) # Check for valid priority @@ -136,7 +142,8 @@ class InboxIssuePublicViewSet(BaseViewSet): "none", ]: return Response( - {"error": "Invalid priority"}, status=status.HTTP_400_BAD_REQUEST + {"error": "Invalid priority"}, + status=status.HTTP_400_BAD_REQUEST, ) # Create or get state @@ -192,7 +199,10 @@ class InboxIssuePublicViewSet(BaseViewSet): ) inbox_issue = InboxIssue.objects.get( - pk=pk, workspace__slug=slug, project_id=project_id, inbox_id=inbox_id + pk=pk, + workspace__slug=slug, + project_id=project_id, + inbox_id=inbox_id, ) # Get the project member if str(inbox_issue.created_by_id) != str(request.user.id): @@ -205,7 +215,9 @@ class InboxIssuePublicViewSet(BaseViewSet): issue_data = request.data.pop("issue", False) issue = Issue.objects.get( - pk=inbox_issue.issue_id, workspace__slug=slug, project_id=project_id + pk=inbox_issue.issue_id, + workspace__slug=slug, + project_id=project_id, ) # viewers and guests since only viewers and guests issue_data = { @@ -216,7 +228,9 @@ class InboxIssuePublicViewSet(BaseViewSet): "description": issue_data.get("description", issue.description), } - issue_serializer = IssueCreateSerializer(issue, data=issue_data, partial=True) + issue_serializer = IssueCreateSerializer( + issue, data=issue_data, partial=True + ) if issue_serializer.is_valid(): current_instance = issue @@ -237,7 +251,9 @@ class InboxIssuePublicViewSet(BaseViewSet): ) issue_serializer.save() return Response(issue_serializer.data, status=status.HTTP_200_OK) - return Response(issue_serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + issue_serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) def retrieve(self, request, slug, project_id, inbox_id, pk): project_deploy_board = ProjectDeployBoard.objects.get( @@ -250,10 +266,15 @@ class InboxIssuePublicViewSet(BaseViewSet): ) inbox_issue = InboxIssue.objects.get( - pk=pk, workspace__slug=slug, project_id=project_id, inbox_id=inbox_id + pk=pk, + workspace__slug=slug, + project_id=project_id, + inbox_id=inbox_id, ) issue = Issue.objects.get( - pk=inbox_issue.issue_id, workspace__slug=slug, project_id=project_id + pk=inbox_issue.issue_id, + workspace__slug=slug, + project_id=project_id, ) serializer = IssueStateInboxSerializer(issue) return Response(serializer.data, status=status.HTTP_200_OK) @@ -269,7 +290,10 @@ class InboxIssuePublicViewSet(BaseViewSet): ) inbox_issue = InboxIssue.objects.get( - pk=pk, workspace__slug=slug, project_id=project_id, inbox_id=inbox_id + pk=pk, + workspace__slug=slug, + project_id=project_id, + inbox_id=inbox_id, ) if str(inbox_issue.created_by_id) != str(request.user.id): diff --git a/apiserver/plane/space/views/issue.py b/apiserver/plane/space/views/issue.py index faab8834d..8f7fc0eaa 100644 --- a/apiserver/plane/space/views/issue.py +++ b/apiserver/plane/space/views/issue.py @@ -128,7 +128,9 @@ class IssueCommentPublicViewSet(BaseViewSet): ) issue_activity.delay( type="comment.activity.created", - requested_data=json.dumps(serializer.data, cls=DjangoJSONEncoder), + requested_data=json.dumps( + serializer.data, cls=DjangoJSONEncoder + ), actor_id=str(request.user.id), issue_id=str(issue_id), project_id=str(project_id), @@ -162,7 +164,9 @@ class IssueCommentPublicViewSet(BaseViewSet): comment = IssueComment.objects.get( workspace__slug=slug, pk=pk, actor=request.user ) - serializer = IssueCommentSerializer(comment, data=request.data, partial=True) + serializer = IssueCommentSerializer( + comment, data=request.data, partial=True + ) if serializer.is_valid(): serializer.save() issue_activity.delay( @@ -191,7 +195,10 @@ class IssueCommentPublicViewSet(BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) comment = IssueComment.objects.get( - workspace__slug=slug, pk=pk, project_id=project_id, actor=request.user + workspace__slug=slug, + pk=pk, + project_id=project_id, + actor=request.user, ) issue_activity.delay( type="comment.activity.deleted", @@ -261,7 +268,9 @@ class IssueReactionPublicViewSet(BaseViewSet): ) issue_activity.delay( type="issue_reaction.activity.created", - requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), + requested_data=json.dumps( + self.request.data, cls=DjangoJSONEncoder + ), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), project_id=str(self.kwargs.get("project_id", None)), @@ -343,7 +352,9 @@ class CommentReactionPublicViewSet(BaseViewSet): serializer = CommentReactionSerializer(data=request.data) if serializer.is_valid(): serializer.save( - project_id=project_id, comment_id=comment_id, actor=request.user + project_id=project_id, + comment_id=comment_id, + actor=request.user, ) if not ProjectMember.objects.filter( project_id=project_id, @@ -357,7 +368,9 @@ class CommentReactionPublicViewSet(BaseViewSet): ) issue_activity.delay( type="comment_reaction.activity.created", - requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), + requested_data=json.dumps( + self.request.data, cls=DjangoJSONEncoder + ), actor_id=str(self.request.user.id), issue_id=None, project_id=str(self.kwargs.get("project_id", None)), @@ -445,7 +458,9 @@ class IssueVotePublicViewSet(BaseViewSet): issue_vote.save() issue_activity.delay( type="issue_vote.activity.created", - requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), + requested_data=json.dumps( + self.request.data, cls=DjangoJSONEncoder + ), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), project_id=str(self.kwargs.get("project_id", None)), @@ -507,13 +522,21 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): # Custom ordering for priority and state priority_order = ["urgent", "high", "medium", "low", "none"] - state_order = ["backlog", "unstarted", "started", "completed", "cancelled"] + state_order = [ + "backlog", + "unstarted", + "started", + "completed", + "cancelled", + ] order_by_param = request.GET.get("order_by", "-created_at") issue_queryset = ( Issue.issue_objects.annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) + sub_issues_count=Issue.issue_objects.filter( + parent=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -544,7 +567,9 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): .values("count") ) .annotate( - attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id")) + attachment_count=IssueAttachment.objects.filter( + issue=OuterRef("id") + ) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -554,7 +579,9 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): # Priority Ordering if order_by_param == "priority" or order_by_param == "-priority": priority_order = ( - priority_order if order_by_param == "priority" else priority_order[::-1] + priority_order + if order_by_param == "priority" + else priority_order[::-1] ) issue_queryset = issue_queryset.annotate( priority_order=Case( @@ -602,7 +629,9 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): else order_by_param ) ).order_by( - "-max_values" if order_by_param.startswith("-") else "max_values" + "-max_values" + if order_by_param.startswith("-") + else "max_values" ) else: issue_queryset = issue_queryset.order_by(order_by_param) @@ -653,4 +682,4 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): "labels": labels, }, status=status.HTTP_200_OK, - ) \ No newline at end of file + ) diff --git a/apiserver/plane/tests/api/base.py b/apiserver/plane/tests/api/base.py index e3209a281..f6843c1b6 100644 --- a/apiserver/plane/tests/api/base.py +++ b/apiserver/plane/tests/api/base.py @@ -8,7 +8,9 @@ from plane.app.views.authentication import get_tokens_for_user class BaseAPITest(APITestCase): def setUp(self): - self.client = APIClient(HTTP_USER_AGENT="plane/test", REMOTE_ADDR="10.10.10.10") + self.client = APIClient( + HTTP_USER_AGENT="plane/test", REMOTE_ADDR="10.10.10.10" + ) class AuthenticatedAPITest(BaseAPITest): diff --git a/apiserver/plane/tests/api/test_asset.py b/apiserver/plane/tests/api/test_asset.py index 51a36ba2f..b15d32e40 100644 --- a/apiserver/plane/tests/api/test_asset.py +++ b/apiserver/plane/tests/api/test_asset.py @@ -1 +1 @@ -# TODO: Tests for File Asset Uploads \ No newline at end of file +# TODO: Tests for File Asset Uploads diff --git a/apiserver/plane/tests/api/test_auth_extended.py b/apiserver/plane/tests/api/test_auth_extended.py index 92ad92d6e..af6450ef4 100644 --- a/apiserver/plane/tests/api/test_auth_extended.py +++ b/apiserver/plane/tests/api/test_auth_extended.py @@ -1 +1 @@ -#TODO: Tests for ChangePassword and other Endpoints \ No newline at end of file +# TODO: Tests for ChangePassword and other Endpoints diff --git a/apiserver/plane/tests/api/test_authentication.py b/apiserver/plane/tests/api/test_authentication.py index 4fc46e008..36a0f7a24 100644 --- a/apiserver/plane/tests/api/test_authentication.py +++ b/apiserver/plane/tests/api/test_authentication.py @@ -21,16 +21,16 @@ class SignInEndpointTests(BaseAPITest): user.save() def test_without_data(self): - url = reverse("sign-in") response = self.client.post(url, {}, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_email_validity(self): - url = reverse("sign-in") response = self.client.post( - url, {"email": "useremail.com", "password": "user@123"}, format="json" + url, + {"email": "useremail.com", "password": "user@123"}, + format="json", ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( @@ -40,7 +40,9 @@ class SignInEndpointTests(BaseAPITest): def test_password_validity(self): url = reverse("sign-in") response = self.client.post( - url, {"email": "user@plane.so", "password": "user123"}, format="json" + url, + {"email": "user@plane.so", "password": "user123"}, + format="json", ) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual( @@ -53,7 +55,9 @@ class SignInEndpointTests(BaseAPITest): def test_user_exists(self): url = reverse("sign-in") response = self.client.post( - url, {"email": "user@email.so", "password": "user123"}, format="json" + url, + {"email": "user@email.so", "password": "user123"}, + format="json", ) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual( @@ -87,15 +91,15 @@ class MagicLinkGenerateEndpointTests(BaseAPITest): user.save() def test_without_data(self): - url = reverse("magic-generate") response = self.client.post(url, {}, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_email_validity(self): - url = reverse("magic-generate") - response = self.client.post(url, {"email": "useremail.com"}, format="json") + response = self.client.post( + url, {"email": "useremail.com"}, format="json" + ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( response.data, {"error": "Please provide a valid email address."} @@ -107,7 +111,9 @@ class MagicLinkGenerateEndpointTests(BaseAPITest): ri = redis_instance() ri.delete("magic_user@plane.so") - response = self.client.post(url, {"email": "user@plane.so"}, format="json") + response = self.client.post( + url, {"email": "user@plane.so"}, format="json" + ) self.assertEqual(response.status_code, status.HTTP_200_OK) def test_max_generate_attempt(self): @@ -131,7 +137,8 @@ class MagicLinkGenerateEndpointTests(BaseAPITest): self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - response.data, {"error": "Max attempts exhausted. Please try again later."} + response.data, + {"error": "Max attempts exhausted. Please try again later."}, ) @@ -143,14 +150,14 @@ class MagicSignInEndpointTests(BaseAPITest): user.save() def test_without_data(self): - url = reverse("magic-sign-in") response = self.client.post(url, {}, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.data, {"error": "User token and key are required"}) + self.assertEqual( + response.data, {"error": "User token and key are required"} + ) def test_expired_invalid_magic_link(self): - ri = redis_instance() ri.delete("magic_user@plane.so") @@ -162,11 +169,11 @@ class MagicSignInEndpointTests(BaseAPITest): ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - response.data, {"error": "The magic code/link has expired please try again"} + response.data, + {"error": "The magic code/link has expired please try again"}, ) def test_invalid_magic_code(self): - ri = redis_instance() ri.delete("magic_user@plane.so") ## Create Token @@ -181,11 +188,11 @@ class MagicSignInEndpointTests(BaseAPITest): ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - response.data, {"error": "Your login code was incorrect. Please try again."} + response.data, + {"error": "Your login code was incorrect. Please try again."}, ) def test_magic_code_sign_in(self): - ri = redis_instance() ri.delete("magic_user@plane.so") ## Create Token diff --git a/apiserver/plane/tests/api/test_cycle.py b/apiserver/plane/tests/api/test_cycle.py index 04c2d6ba2..72b580c99 100644 --- a/apiserver/plane/tests/api/test_cycle.py +++ b/apiserver/plane/tests/api/test_cycle.py @@ -1 +1 @@ -# TODO: Write Test for Cycle Endpoints \ No newline at end of file +# TODO: Write Test for Cycle Endpoints diff --git a/apiserver/plane/tests/api/test_issue.py b/apiserver/plane/tests/api/test_issue.py index 3e59613e0..a45ff36b1 100644 --- a/apiserver/plane/tests/api/test_issue.py +++ b/apiserver/plane/tests/api/test_issue.py @@ -1 +1 @@ -# TODO: Write Test for Issue Endpoints \ No newline at end of file +# TODO: Write Test for Issue Endpoints diff --git a/apiserver/plane/tests/api/test_oauth.py b/apiserver/plane/tests/api/test_oauth.py index e70e4fccb..1e7dac0ef 100644 --- a/apiserver/plane/tests/api/test_oauth.py +++ b/apiserver/plane/tests/api/test_oauth.py @@ -1 +1 @@ -#TODO: Tests for OAuth Authentication Endpoint \ No newline at end of file +# TODO: Tests for OAuth Authentication Endpoint diff --git a/apiserver/plane/tests/api/test_people.py b/apiserver/plane/tests/api/test_people.py index c4750f9b8..624281a2f 100644 --- a/apiserver/plane/tests/api/test_people.py +++ b/apiserver/plane/tests/api/test_people.py @@ -1 +1 @@ -# TODO: Write Test for people Endpoint \ No newline at end of file +# TODO: Write Test for people Endpoint diff --git a/apiserver/plane/tests/api/test_project.py b/apiserver/plane/tests/api/test_project.py index 49dae5581..9a7c50f19 100644 --- a/apiserver/plane/tests/api/test_project.py +++ b/apiserver/plane/tests/api/test_project.py @@ -1 +1 @@ -# TODO: Write Tests for project endpoints \ No newline at end of file +# TODO: Write Tests for project endpoints diff --git a/apiserver/plane/tests/api/test_shortcut.py b/apiserver/plane/tests/api/test_shortcut.py index 2e939af70..5103b5059 100644 --- a/apiserver/plane/tests/api/test_shortcut.py +++ b/apiserver/plane/tests/api/test_shortcut.py @@ -1 +1 @@ -# TODO: Write Test for shortcuts \ No newline at end of file +# TODO: Write Test for shortcuts diff --git a/apiserver/plane/tests/api/test_state.py b/apiserver/plane/tests/api/test_state.py index ef9631bc2..a336d955a 100644 --- a/apiserver/plane/tests/api/test_state.py +++ b/apiserver/plane/tests/api/test_state.py @@ -1 +1 @@ -# TODO: Wrote test for state endpoints \ No newline at end of file +# TODO: Wrote test for state endpoints diff --git a/apiserver/plane/tests/api/test_workspace.py b/apiserver/plane/tests/api/test_workspace.py index a1da2997a..c1e487fbe 100644 --- a/apiserver/plane/tests/api/test_workspace.py +++ b/apiserver/plane/tests/api/test_workspace.py @@ -14,7 +14,6 @@ class WorkSpaceCreateReadUpdateDelete(AuthenticatedAPITest): super().setUp() def test_create_workspace(self): - url = reverse("workspace") # Test with empty data @@ -32,7 +31,9 @@ class WorkSpaceCreateReadUpdateDelete(AuthenticatedAPITest): # Check other values workspace = Workspace.objects.get(pk=response.data["id"]) - workspace_member = WorkspaceMember.objects.get(workspace=workspace, member_id=self.user_id) + workspace_member = WorkspaceMember.objects.get( + workspace=workspace, member_id=self.user_id + ) self.assertEqual(workspace.owner_id, self.user_id) self.assertEqual(workspace_member.role, 20) diff --git a/apiserver/plane/utils/analytics_plot.py b/apiserver/plane/utils/analytics_plot.py index be52bcce4..07d456a1d 100644 --- a/apiserver/plane/utils/analytics_plot.py +++ b/apiserver/plane/utils/analytics_plot.py @@ -6,7 +6,12 @@ from datetime import timedelta from django.db import models from django.db.models.functions import TruncDate from django.db.models import Count, F, Sum, Value, Case, When, CharField -from django.db.models.functions import Coalesce, ExtractMonth, ExtractYear, Concat +from django.db.models.functions import ( + Coalesce, + ExtractMonth, + ExtractYear, + Concat, +) # Module imports from plane.db.models import Issue @@ -21,14 +26,18 @@ def annotate_with_monthly_dimension(queryset, field_name, attribute): # Annotate the dimension return queryset.annotate(**{attribute: dimension}) + def extract_axis(queryset, x_axis): # Format the dimension when the axis is in date if x_axis in ["created_at", "start_date", "target_date", "completed_at"]: - queryset = annotate_with_monthly_dimension(queryset, x_axis, "dimension") + queryset = annotate_with_monthly_dimension( + queryset, x_axis, "dimension" + ) return queryset, "dimension" else: return queryset.annotate(dimension=F(x_axis)), "dimension" + def sort_data(data, temp_axis): # When the axis is in priority order by if temp_axis == "priority": @@ -37,6 +46,7 @@ def sort_data(data, temp_axis): else: return dict(sorted(data.items(), key=lambda x: (x[0] == "none", x[0]))) + def build_graph_plot(queryset, x_axis, y_axis, segment=None): # temp x_axis temp_axis = x_axis @@ -45,9 +55,11 @@ def build_graph_plot(queryset, x_axis, y_axis, segment=None): if x_axis == "dimension": queryset = queryset.exclude(dimension__isnull=True) - # + # if segment in ["created_at", "start_date", "target_date", "completed_at"]: - queryset = annotate_with_monthly_dimension(queryset, segment, "segmented") + queryset = annotate_with_monthly_dimension( + queryset, segment, "segmented" + ) segment = "segmented" queryset = queryset.values(x_axis) @@ -62,21 +74,41 @@ def build_graph_plot(queryset, x_axis, y_axis, segment=None): ), dimension_ex=Coalesce("dimension", Value("null")), ).values("dimension") - queryset = queryset.annotate(segment=F(segment)) if segment else queryset - queryset = queryset.values("dimension", "segment") if segment else queryset.values("dimension") + queryset = ( + queryset.annotate(segment=F(segment)) if segment else queryset + ) + queryset = ( + queryset.values("dimension", "segment") + if segment + else queryset.values("dimension") + ) queryset = queryset.annotate(count=Count("*")).order_by("dimension") # Estimate else: - queryset = queryset.annotate(estimate=Sum("estimate_point")).order_by(x_axis) - queryset = queryset.annotate(segment=F(segment)) if segment else queryset - queryset = queryset.values("dimension", "segment", "estimate") if segment else queryset.values("dimension", "estimate") + queryset = queryset.annotate(estimate=Sum("estimate_point")).order_by( + x_axis + ) + queryset = ( + queryset.annotate(segment=F(segment)) if segment else queryset + ) + queryset = ( + queryset.values("dimension", "segment", "estimate") + if segment + else queryset.values("dimension", "estimate") + ) result_values = list(queryset) - grouped_data = {str(key): list(items) for key, items in groupby(result_values, key=lambda x: x[str("dimension")])} + grouped_data = { + str(key): list(items) + for key, items in groupby( + result_values, key=lambda x: x[str("dimension")] + ) + } return sort_data(grouped_data, temp_axis) + def burndown_plot(queryset, slug, project_id, cycle_id=None, module_id=None): # Total Issues in Cycle or Module total_issues = queryset.total_issues @@ -107,7 +139,9 @@ def burndown_plot(queryset, slug, project_id, cycle_id=None, module_id=None): # Get all dates between the two dates date_range = [ queryset.start_date + timedelta(days=x) - for x in range((queryset.target_date - queryset.start_date).days + 1) + for x in range( + (queryset.target_date - queryset.start_date).days + 1 + ) ] chart_data = {str(date): 0 for date in date_range} diff --git a/apiserver/plane/utils/grouper.py b/apiserver/plane/utils/grouper.py index 853874b31..edc7adc15 100644 --- a/apiserver/plane/utils/grouper.py +++ b/apiserver/plane/utils/grouper.py @@ -40,77 +40,144 @@ def group_results(results_data, group_by, sub_group_by=False): for value in results_data: main_group_attribute = resolve_keys(sub_group_by, value) group_attribute = resolve_keys(group_by, value) - if isinstance(main_group_attribute, list) and not isinstance(group_attribute, list): + if isinstance(main_group_attribute, list) and not isinstance( + group_attribute, list + ): if len(main_group_attribute): for attrib in main_group_attribute: if str(attrib) not in main_responsive_dict: main_responsive_dict[str(attrib)] = {} - if str(group_attribute) in main_responsive_dict[str(attrib)]: - main_responsive_dict[str(attrib)][str(group_attribute)].append(value) + if ( + str(group_attribute) + in main_responsive_dict[str(attrib)] + ): + main_responsive_dict[str(attrib)][ + str(group_attribute) + ].append(value) else: - main_responsive_dict[str(attrib)][str(group_attribute)] = [] - main_responsive_dict[str(attrib)][str(group_attribute)].append(value) + main_responsive_dict[str(attrib)][ + str(group_attribute) + ] = [] + main_responsive_dict[str(attrib)][ + str(group_attribute) + ].append(value) else: if str(None) not in main_responsive_dict: main_responsive_dict[str(None)] = {} if str(group_attribute) in main_responsive_dict[str(None)]: - main_responsive_dict[str(None)][str(group_attribute)].append(value) + main_responsive_dict[str(None)][ + str(group_attribute) + ].append(value) else: - main_responsive_dict[str(None)][str(group_attribute)] = [] - main_responsive_dict[str(None)][str(group_attribute)].append(value) + main_responsive_dict[str(None)][ + str(group_attribute) + ] = [] + main_responsive_dict[str(None)][ + str(group_attribute) + ].append(value) - elif isinstance(group_attribute, list) and not isinstance(main_group_attribute, list): + elif isinstance(group_attribute, list) and not isinstance( + main_group_attribute, list + ): if str(main_group_attribute) not in main_responsive_dict: main_responsive_dict[str(main_group_attribute)] = {} if len(group_attribute): for attrib in group_attribute: - if str(attrib) in main_responsive_dict[str(main_group_attribute)]: - main_responsive_dict[str(main_group_attribute)][str(attrib)].append(value) + if ( + str(attrib) + in main_responsive_dict[str(main_group_attribute)] + ): + main_responsive_dict[str(main_group_attribute)][ + str(attrib) + ].append(value) else: - main_responsive_dict[str(main_group_attribute)][str(attrib)] = [] - main_responsive_dict[str(main_group_attribute)][str(attrib)].append(value) + main_responsive_dict[str(main_group_attribute)][ + str(attrib) + ] = [] + main_responsive_dict[str(main_group_attribute)][ + str(attrib) + ].append(value) else: - if str(None) in main_responsive_dict[str(main_group_attribute)]: - main_responsive_dict[str(main_group_attribute)][str(None)].append(value) + if ( + str(None) + in main_responsive_dict[str(main_group_attribute)] + ): + main_responsive_dict[str(main_group_attribute)][ + str(None) + ].append(value) else: - main_responsive_dict[str(main_group_attribute)][str(None)] = [] - main_responsive_dict[str(main_group_attribute)][str(None)].append(value) + main_responsive_dict[str(main_group_attribute)][ + str(None) + ] = [] + main_responsive_dict[str(main_group_attribute)][ + str(None) + ].append(value) - elif isinstance(group_attribute, list) and isinstance(main_group_attribute, list): + elif isinstance(group_attribute, list) and isinstance( + main_group_attribute, list + ): if len(main_group_attribute): for main_attrib in main_group_attribute: if str(main_attrib) not in main_responsive_dict: main_responsive_dict[str(main_attrib)] = {} if len(group_attribute): for attrib in group_attribute: - if str(attrib) in main_responsive_dict[str(main_attrib)]: - main_responsive_dict[str(main_attrib)][str(attrib)].append(value) + if ( + str(attrib) + in main_responsive_dict[str(main_attrib)] + ): + main_responsive_dict[str(main_attrib)][ + str(attrib) + ].append(value) else: - main_responsive_dict[str(main_attrib)][str(attrib)] = [] - main_responsive_dict[str(main_attrib)][str(attrib)].append(value) + main_responsive_dict[str(main_attrib)][ + str(attrib) + ] = [] + main_responsive_dict[str(main_attrib)][ + str(attrib) + ].append(value) else: - if str(None) in main_responsive_dict[str(main_attrib)]: - main_responsive_dict[str(main_attrib)][str(None)].append(value) + if ( + str(None) + in main_responsive_dict[str(main_attrib)] + ): + main_responsive_dict[str(main_attrib)][ + str(None) + ].append(value) else: - main_responsive_dict[str(main_attrib)][str(None)] = [] - main_responsive_dict[str(main_attrib)][str(None)].append(value) + main_responsive_dict[str(main_attrib)][ + str(None) + ] = [] + main_responsive_dict[str(main_attrib)][ + str(None) + ].append(value) else: if str(None) not in main_responsive_dict: main_responsive_dict[str(None)] = {} if len(group_attribute): for attrib in group_attribute: if str(attrib) in main_responsive_dict[str(None)]: - main_responsive_dict[str(None)][str(attrib)].append(value) + main_responsive_dict[str(None)][ + str(attrib) + ].append(value) else: - main_responsive_dict[str(None)][str(attrib)] = [] - main_responsive_dict[str(None)][str(attrib)].append(value) + main_responsive_dict[str(None)][ + str(attrib) + ] = [] + main_responsive_dict[str(None)][ + str(attrib) + ].append(value) else: if str(None) in main_responsive_dict[str(None)]: - main_responsive_dict[str(None)][str(None)].append(value) + main_responsive_dict[str(None)][str(None)].append( + value + ) else: main_responsive_dict[str(None)][str(None)] = [] - main_responsive_dict[str(None)][str(None)].append(value) + main_responsive_dict[str(None)][str(None)].append( + value + ) else: main_group_attribute = resolve_keys(sub_group_by, value) group_attribute = resolve_keys(group_by, value) @@ -118,13 +185,22 @@ def group_results(results_data, group_by, sub_group_by=False): if str(main_group_attribute) not in main_responsive_dict: main_responsive_dict[str(main_group_attribute)] = {} - if str(group_attribute) in main_responsive_dict[str(main_group_attribute)]: - main_responsive_dict[str(main_group_attribute)][str(group_attribute)].append(value) + if ( + str(group_attribute) + in main_responsive_dict[str(main_group_attribute)] + ): + main_responsive_dict[str(main_group_attribute)][ + str(group_attribute) + ].append(value) else: - main_responsive_dict[str(main_group_attribute)][str(group_attribute)] = [] - main_responsive_dict[str(main_group_attribute)][str(group_attribute)].append(value) + main_responsive_dict[str(main_group_attribute)][ + str(group_attribute) + ] = [] + main_responsive_dict[str(main_group_attribute)][ + str(group_attribute) + ].append(value) - return main_responsive_dict + return main_responsive_dict else: response_dict = {} diff --git a/apiserver/plane/utils/html_processor.py b/apiserver/plane/utils/html_processor.py index 5f61607e9..18d103b64 100644 --- a/apiserver/plane/utils/html_processor.py +++ b/apiserver/plane/utils/html_processor.py @@ -1,15 +1,17 @@ from io import StringIO from html.parser import HTMLParser + class MLStripper(HTMLParser): """ Markup Language Stripper """ + def __init__(self): super().__init__() self.reset() self.strict = False - self.convert_charrefs= True + self.convert_charrefs = True self.text = StringIO() def handle_data(self, d): @@ -18,6 +20,7 @@ class MLStripper(HTMLParser): def get_data(self): return self.text.getvalue() + def strip_tags(html): s = MLStripper() s.feed(html) diff --git a/apiserver/plane/utils/importers/jira.py b/apiserver/plane/utils/importers/jira.py index 5e8c31f97..6f3a7c217 100644 --- a/apiserver/plane/utils/importers/jira.py +++ b/apiserver/plane/utils/importers/jira.py @@ -6,7 +6,12 @@ from urllib.parse import urlparse, urljoin def is_allowed_hostname(hostname): - allowed_domains = ["atl-paas.net", "atlassian.com", "atlassian.net", "jira.com"] + allowed_domains = [ + "atl-paas.net", + "atlassian.com", + "atlassian.net", + "jira.com", + ] parsed_uri = urlparse(f"https://{hostname}") domain = parsed_uri.netloc.split(":")[0] # Ensures no port is included base_domain = ".".join(domain.split(".")[-2:]) @@ -20,14 +25,16 @@ def is_valid_project_key(project_key): if len(project_key) > 30: return False # Check the validity of the key as well - pattern = re.compile(r'^[A-Z0-9]{1,10}$') + pattern = re.compile(r"^[A-Z0-9]{1,10}$") return pattern.match(project_key) is not None else: False + def generate_valid_project_key(project_key): return project_key.strip().upper() + def generate_url(hostname, path): if not is_allowed_hostname(hostname): raise ValueError("Invalid or unauthorized hostname") @@ -44,7 +51,7 @@ def jira_project_issue_summary(email, api_token, project_key, hostname): auth = HTTPBasicAuth(email, api_token) headers = {"Accept": "application/json"} - + # make the project key upper case project_key = generate_valid_project_key(project_key) @@ -59,7 +66,8 @@ def jira_project_issue_summary(email, api_token, project_key, hostname): # modules module_url = generate_url( - hostname, f"/rest/api/3/search?jql=project={project_key} AND issuetype=Epic" + hostname, + f"/rest/api/3/search?jql=project={project_key} AND issuetype=Epic", ) module_response = requests.request( "GET", module_url, headers=headers, auth=auth @@ -104,4 +112,6 @@ def jira_project_issue_summary(email, api_token, project_key, hostname): } except Exception as e: capture_exception(e) - return {"error": "Something went wrong could not fetch information from jira"} + return { + "error": "Something went wrong could not fetch information from jira" + } diff --git a/apiserver/plane/utils/imports.py b/apiserver/plane/utils/imports.py index 5f9f1c98c..89753ef1d 100644 --- a/apiserver/plane/utils/imports.py +++ b/apiserver/plane/utils/imports.py @@ -8,13 +8,12 @@ def import_submodules(context, root_module, path): >>> import_submodules(locals(), __name__, __path__) """ for loader, module_name, is_pkg in pkgutil.walk_packages( - path, - root_module + - '.'): + path, root_module + "." + ): # this causes a Runtime error with model conflicts # module = loader.find_module(module_name).load_module(module_name) - module = __import__(module_name, globals(), locals(), ['__name__']) + module = __import__(module_name, globals(), locals(), ["__name__"]) for k, v in six.iteritems(vars(module)): - if not k.startswith('_'): + if not k.startswith("_"): context[k] = v context[module_name] = module diff --git a/apiserver/plane/utils/integrations/github.py b/apiserver/plane/utils/integrations/github.py index 45cb5925a..5a7ce2aa2 100644 --- a/apiserver/plane/utils/integrations/github.py +++ b/apiserver/plane/utils/integrations/github.py @@ -10,7 +10,9 @@ from django.conf import settings def get_jwt_token(): app_id = os.environ.get("GITHUB_APP_ID", "") - secret = bytes(os.environ.get("GITHUB_APP_PRIVATE_KEY", ""), encoding="utf8") + secret = bytes( + os.environ.get("GITHUB_APP_PRIVATE_KEY", ""), encoding="utf8" + ) current_timestamp = int(datetime.now().timestamp()) due_date = datetime.now() + timedelta(minutes=10) expiry = int(due_date.timestamp()) diff --git a/apiserver/plane/utils/integrations/slack.py b/apiserver/plane/utils/integrations/slack.py index 70f26e160..0cc5b93b2 100644 --- a/apiserver/plane/utils/integrations/slack.py +++ b/apiserver/plane/utils/integrations/slack.py @@ -1,6 +1,7 @@ import os import requests + def slack_oauth(code): SLACK_OAUTH_URL = os.environ.get("SLACK_OAUTH_URL", False) SLACK_CLIENT_ID = os.environ.get("SLACK_CLIENT_ID", False) diff --git a/apiserver/plane/utils/ip_address.py b/apiserver/plane/utils/ip_address.py index 06ca4353d..01789c431 100644 --- a/apiserver/plane/utils/ip_address.py +++ b/apiserver/plane/utils/ip_address.py @@ -1,7 +1,7 @@ def get_client_ip(request): - x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') + x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") if x_forwarded_for: - ip = x_forwarded_for.split(',')[0] + ip = x_forwarded_for.split(",")[0] else: - ip = request.META.get('REMOTE_ADDR') + ip = request.META.get("REMOTE_ADDR") return ip diff --git a/apiserver/plane/utils/issue_filters.py b/apiserver/plane/utils/issue_filters.py index 2da24092a..477e54bd8 100644 --- a/apiserver/plane/utils/issue_filters.py +++ b/apiserver/plane/utils/issue_filters.py @@ -7,6 +7,7 @@ from django.utils import timezone # The date from pattern pattern = re.compile(r"\d+_(weeks|months)$") + # check the valid uuids def filter_valid_uuids(uuid_list): valid_uuids = [] @@ -21,19 +22,29 @@ def filter_valid_uuids(uuid_list): # Get the 2_weeks, 3_months -def string_date_filter(filter, duration, subsequent, term, date_filter, offset): +def string_date_filter( + filter, duration, subsequent, term, date_filter, offset +): now = timezone.now().date() if term == "months": if subsequent == "after": if offset == "fromnow": - filter[f"{date_filter}__gte"] = now + timedelta(days=duration * 30) + filter[f"{date_filter}__gte"] = now + timedelta( + days=duration * 30 + ) else: - filter[f"{date_filter}__gte"] = now - timedelta(days=duration * 30) + filter[f"{date_filter}__gte"] = now - timedelta( + days=duration * 30 + ) else: if offset == "fromnow": - filter[f"{date_filter}__lte"] = now + timedelta(days=duration * 30) + filter[f"{date_filter}__lte"] = now + timedelta( + days=duration * 30 + ) else: - filter[f"{date_filter}__lte"] = now - timedelta(days=duration * 30) + filter[f"{date_filter}__lte"] = now - timedelta( + days=duration * 30 + ) if term == "weeks": if subsequent == "after": if offset == "fromnow": @@ -49,7 +60,7 @@ def string_date_filter(filter, duration, subsequent, term, date_filter, offset): def date_filter(filter, date_term, queries): """ - Handle all date filters + Handle all date filters """ for query in queries: date_query = query.split(";") @@ -75,41 +86,67 @@ def date_filter(filter, date_term, queries): def filter_state(params, filter, method): if method == "GET": - states = [item for item in params.get("state").split(",") if item != 'null'] + states = [ + item for item in params.get("state").split(",") if item != "null" + ] states = filter_valid_uuids(states) if len(states) and "" not in states: filter["state__in"] = states else: - if params.get("state", None) and len(params.get("state")) and params.get("state") != 'null': + if ( + params.get("state", None) + and len(params.get("state")) + and params.get("state") != "null" + ): filter["state__in"] = params.get("state") return filter def filter_state_group(params, filter, method): if method == "GET": - state_group = [item for item in params.get("state_group").split(",") if item != 'null'] + state_group = [ + item + for item in params.get("state_group").split(",") + if item != "null" + ] if len(state_group) and "" not in state_group: filter["state__group__in"] = state_group else: - if params.get("state_group", None) and len(params.get("state_group")) and params.get("state_group") != 'null': + if ( + params.get("state_group", None) + and len(params.get("state_group")) + and params.get("state_group") != "null" + ): filter["state__group__in"] = params.get("state_group") return filter def filter_estimate_point(params, filter, method): if method == "GET": - estimate_points = [item for item in params.get("estimate_point").split(",") if item != 'null'] + estimate_points = [ + item + for item in params.get("estimate_point").split(",") + if item != "null" + ] if len(estimate_points) and "" not in estimate_points: filter["estimate_point__in"] = estimate_points else: - if params.get("estimate_point", None) and len(params.get("estimate_point")) and params.get("estimate_point") != 'null': + if ( + params.get("estimate_point", None) + and len(params.get("estimate_point")) + and params.get("estimate_point") != "null" + ): filter["estimate_point__in"] = params.get("estimate_point") return filter def filter_priority(params, filter, method): if method == "GET": - priorities = [item for item in params.get("priority").split(",") if item != 'null'] + priorities = [ + item + for item in params.get("priority").split(",") + if item != "null" + ] if len(priorities) and "" not in priorities: filter["priority__in"] = priorities return filter @@ -117,59 +154,96 @@ def filter_priority(params, filter, method): def filter_parent(params, filter, method): if method == "GET": - parents = [item for item in params.get("parent").split(",") if item != 'null'] + parents = [ + item for item in params.get("parent").split(",") if item != "null" + ] parents = filter_valid_uuids(parents) if len(parents) and "" not in parents: filter["parent__in"] = parents else: - if params.get("parent", None) and len(params.get("parent")) and params.get("parent") != 'null': + if ( + params.get("parent", None) + and len(params.get("parent")) + and params.get("parent") != "null" + ): filter["parent__in"] = params.get("parent") return filter def filter_labels(params, filter, method): if method == "GET": - labels = [item for item in params.get("labels").split(",") if item != 'null'] + labels = [ + item for item in params.get("labels").split(",") if item != "null" + ] labels = filter_valid_uuids(labels) if len(labels) and "" not in labels: filter["labels__in"] = labels else: - if params.get("labels", None) and len(params.get("labels")) and params.get("labels") != 'null': + if ( + params.get("labels", None) + and len(params.get("labels")) + and params.get("labels") != "null" + ): filter["labels__in"] = params.get("labels") return filter def filter_assignees(params, filter, method): if method == "GET": - assignees = [item for item in params.get("assignees").split(",") if item != 'null'] + assignees = [ + item + for item in params.get("assignees").split(",") + if item != "null" + ] assignees = filter_valid_uuids(assignees) if len(assignees) and "" not in assignees: filter["assignees__in"] = assignees else: - if params.get("assignees", None) and len(params.get("assignees")) and params.get("assignees") != 'null': + if ( + params.get("assignees", None) + and len(params.get("assignees")) + and params.get("assignees") != "null" + ): filter["assignees__in"] = params.get("assignees") return filter + def filter_mentions(params, filter, method): if method == "GET": - mentions = [item for item in params.get("mentions").split(",") if item != 'null'] + mentions = [ + item + for item in params.get("mentions").split(",") + if item != "null" + ] mentions = filter_valid_uuids(mentions) if len(mentions) and "" not in mentions: filter["issue_mention__mention__id__in"] = mentions else: - if params.get("mentions", None) and len(params.get("mentions")) and params.get("mentions") != 'null': + if ( + params.get("mentions", None) + and len(params.get("mentions")) + and params.get("mentions") != "null" + ): filter["issue_mention__mention__id__in"] = params.get("mentions") return filter def filter_created_by(params, filter, method): if method == "GET": - created_bys = [item for item in params.get("created_by").split(",") if item != 'null'] + created_bys = [ + item + for item in params.get("created_by").split(",") + if item != "null" + ] created_bys = filter_valid_uuids(created_bys) if len(created_bys) and "" not in created_bys: filter["created_by__in"] = created_bys else: - if params.get("created_by", None) and len(params.get("created_by")) and params.get("created_by") != 'null': + if ( + params.get("created_by", None) + and len(params.get("created_by")) + and params.get("created_by") != "null" + ): filter["created_by__in"] = params.get("created_by") return filter @@ -184,10 +258,18 @@ def filter_created_at(params, filter, method): if method == "GET": created_ats = params.get("created_at").split(",") if len(created_ats) and "" not in created_ats: - date_filter(filter=filter, date_term="created_at__date", queries=created_ats) + date_filter( + filter=filter, + date_term="created_at__date", + queries=created_ats, + ) else: if params.get("created_at", None) and len(params.get("created_at")): - date_filter(filter=filter, date_term="created_at__date", queries=params.get("created_at", [])) + date_filter( + filter=filter, + date_term="created_at__date", + queries=params.get("created_at", []), + ) return filter @@ -195,10 +277,18 @@ def filter_updated_at(params, filter, method): if method == "GET": updated_ats = params.get("updated_at").split(",") if len(updated_ats) and "" not in updated_ats: - date_filter(filter=filter, date_term="created_at__date", queries=updated_ats) + date_filter( + filter=filter, + date_term="created_at__date", + queries=updated_ats, + ) else: if params.get("updated_at", None) and len(params.get("updated_at")): - date_filter(filter=filter, date_term="created_at__date", queries=params.get("updated_at", [])) + date_filter( + filter=filter, + date_term="created_at__date", + queries=params.get("updated_at", []), + ) return filter @@ -206,7 +296,9 @@ def filter_start_date(params, filter, method): if method == "GET": start_dates = params.get("start_date").split(",") if len(start_dates) and "" not in start_dates: - date_filter(filter=filter, date_term="start_date", queries=start_dates) + date_filter( + filter=filter, date_term="start_date", queries=start_dates + ) else: if params.get("start_date", None) and len(params.get("start_date")): filter["start_date"] = params.get("start_date") @@ -217,7 +309,9 @@ def filter_target_date(params, filter, method): if method == "GET": target_dates = params.get("target_date").split(",") if len(target_dates) and "" not in target_dates: - date_filter(filter=filter, date_term="target_date", queries=target_dates) + date_filter( + filter=filter, date_term="target_date", queries=target_dates + ) else: if params.get("target_date", None) and len(params.get("target_date")): filter["target_date"] = params.get("target_date") @@ -228,10 +322,20 @@ def filter_completed_at(params, filter, method): if method == "GET": completed_ats = params.get("completed_at").split(",") if len(completed_ats) and "" not in completed_ats: - date_filter(filter=filter, date_term="completed_at__date", queries=completed_ats) + date_filter( + filter=filter, + date_term="completed_at__date", + queries=completed_ats, + ) else: - if params.get("completed_at", None) and len(params.get("completed_at")): - date_filter(filter=filter, date_term="completed_at__date", queries=params.get("completed_at", [])) + if params.get("completed_at", None) and len( + params.get("completed_at") + ): + date_filter( + filter=filter, + date_term="completed_at__date", + queries=params.get("completed_at", []), + ) return filter @@ -249,47 +353,73 @@ def filter_issue_state_type(params, filter, method): def filter_project(params, filter, method): if method == "GET": - projects = [item for item in params.get("project").split(",") if item != 'null'] + projects = [ + item for item in params.get("project").split(",") if item != "null" + ] projects = filter_valid_uuids(projects) if len(projects) and "" not in projects: filter["project__in"] = projects else: - if params.get("project", None) and len(params.get("project")) and params.get("project") != 'null': + if ( + params.get("project", None) + and len(params.get("project")) + and params.get("project") != "null" + ): filter["project__in"] = params.get("project") return filter def filter_cycle(params, filter, method): if method == "GET": - cycles = [item for item in params.get("cycle").split(",") if item != 'null'] + cycles = [ + item for item in params.get("cycle").split(",") if item != "null" + ] cycles = filter_valid_uuids(cycles) if len(cycles) and "" not in cycles: filter["issue_cycle__cycle_id__in"] = cycles else: - if params.get("cycle", None) and len(params.get("cycle")) and params.get("cycle") != 'null': + if ( + params.get("cycle", None) + and len(params.get("cycle")) + and params.get("cycle") != "null" + ): filter["issue_cycle__cycle_id__in"] = params.get("cycle") return filter def filter_module(params, filter, method): if method == "GET": - modules = [item for item in params.get("module").split(",") if item != 'null'] + modules = [ + item for item in params.get("module").split(",") if item != "null" + ] modules = filter_valid_uuids(modules) if len(modules) and "" not in modules: filter["issue_module__module_id__in"] = modules else: - if params.get("module", None) and len(params.get("module")) and params.get("module") != 'null': + if ( + params.get("module", None) + and len(params.get("module")) + and params.get("module") != "null" + ): filter["issue_module__module_id__in"] = params.get("module") return filter def filter_inbox_status(params, filter, method): if method == "GET": - status = [item for item in params.get("inbox_status").split(",") if item != 'null'] + status = [ + item + for item in params.get("inbox_status").split(",") + if item != "null" + ] if len(status) and "" not in status: filter["issue_inbox__status__in"] = status else: - if params.get("inbox_status", None) and len(params.get("inbox_status")) and params.get("inbox_status") != 'null': + if ( + params.get("inbox_status", None) + and len(params.get("inbox_status")) + and params.get("inbox_status") != "null" + ): filter["issue_inbox__status__in"] = params.get("inbox_status") return filter @@ -308,13 +438,23 @@ def filter_sub_issue_toggle(params, filter, method): def filter_subscribed_issues(params, filter, method): if method == "GET": - subscribers = [item for item in params.get("subscriber").split(",") if item != 'null'] + subscribers = [ + item + for item in params.get("subscriber").split(",") + if item != "null" + ] subscribers = filter_valid_uuids(subscribers) if len(subscribers) and "" not in subscribers: filter["issue_subscribers__subscriber_id__in"] = subscribers else: - if params.get("subscriber", None) and len(params.get("subscriber")) and params.get("subscriber") != 'null': - filter["issue_subscribers__subscriber_id__in"] = params.get("subscriber") + if ( + params.get("subscriber", None) + and len(params.get("subscriber")) + and params.get("subscriber") != "null" + ): + filter["issue_subscribers__subscriber_id__in"] = params.get( + "subscriber" + ) return filter diff --git a/apiserver/plane/utils/paginator.py b/apiserver/plane/utils/paginator.py index 3563dad34..6b2b49c15 100644 --- a/apiserver/plane/utils/paginator.py +++ b/apiserver/plane/utils/paginator.py @@ -31,8 +31,10 @@ class Cursor: try: bits = value.split(":") if len(bits) != 3: - raise ValueError("Cursor must be in the format 'value:offset:is_prev'") - + raise ValueError( + "Cursor must be in the format 'value:offset:is_prev'" + ) + value = float(bits[0]) if "." in bits[0] else int(bits[0]) return cls(value, int(bits[1]), bool(int(bits[2]))) except (TypeError, ValueError) as e: @@ -178,7 +180,9 @@ class BasePaginator: input_cursor = None if request.GET.get(self.cursor_name): try: - input_cursor = cursor_cls.from_string(request.GET.get(self.cursor_name)) + input_cursor = cursor_cls.from_string( + request.GET.get(self.cursor_name) + ) except ValueError: raise ParseError(detail="Invalid cursor parameter.") @@ -186,7 +190,9 @@ class BasePaginator: paginator = paginator_cls(**paginator_kwargs) try: - cursor_result = paginator.get_result(limit=per_page, cursor=input_cursor) + cursor_result = paginator.get_result( + limit=per_page, cursor=input_cursor + ) except BadPaginationError as e: raise ParseError(detail="Error in parsing") diff --git a/apiserver/plane/web/apps.py b/apiserver/plane/web/apps.py index 76ca3c4e6..a5861f9b5 100644 --- a/apiserver/plane/web/apps.py +++ b/apiserver/plane/web/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class WebConfig(AppConfig): - name = 'plane.web' + name = "plane.web" diff --git a/apiserver/plane/web/urls.py b/apiserver/plane/web/urls.py index 568b99037..24a3e7b57 100644 --- a/apiserver/plane/web/urls.py +++ b/apiserver/plane/web/urls.py @@ -2,6 +2,5 @@ from django.urls import path from django.views.generic import TemplateView urlpatterns = [ - path('about/', TemplateView.as_view(template_name='about.html')) - + path("about/", TemplateView.as_view(template_name="about.html")) ] diff --git a/apiserver/plane/wsgi.py b/apiserver/plane/wsgi.py index ef3ea2780..b3051f9ff 100644 --- a/apiserver/plane/wsgi.py +++ b/apiserver/plane/wsgi.py @@ -9,7 +9,6 @@ import os from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', - 'plane.settings.production') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "plane.settings.production") application = get_wsgi_application() diff --git a/apiserver/pyproject.toml b/apiserver/pyproject.toml new file mode 100644 index 000000000..773d6090e --- /dev/null +++ b/apiserver/pyproject.toml @@ -0,0 +1,18 @@ +[tool.black] +line-length = 79 +target-version = ['py36'] +include = '\.pyi?$' +exclude = ''' + /( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist + | venv + )/ +''' From ec26bf6e68ec0151ade5786e6c8c77c868e21c9a Mon Sep 17 00:00:00 2001 From: guru_sainath Date: Tue, 16 Jan 2024 12:46:03 +0530 Subject: [PATCH 6/9] chore: update in sub-issues component and property validation and issue loaders (#3375) * fix: handled undefined issue_id in list layout * chore: refactor peek overview and user role validation. * chore: sub issues * fix: sub issues state distribution changed * chore: sub_issues implementation in issue detail page * chore: fixes in cycle/ module layout. * Fix progress chart * Module issues's update/ delete. * Peek Overview for Modules/ Cycle. * Fix Cycle Filters not applying bug. --------- Co-authored-by: Prateek Shourya Co-authored-by: NarayanBavisetti --- apiserver/plane/app/views/issue.py | 38 +- .../types/src/issues/issue_sub_issues.d.ts | 10 +- .../core/sidebar/progress-chart.tsx | 2 +- .../core/sidebar/sidebar-progress-stats.tsx | 2 +- web/components/issues/description-form.tsx | 14 +- .../issue-detail/label/label-list-item.tsx | 2 +- .../issues/issue-detail/links/root.tsx | 2 +- .../issues/issue-detail/main-content.tsx | 5 +- .../issues/issue-detail/parent/siblings.tsx | 4 +- web/components/issues/issue-detail/root.tsx | 47 +- .../issues/issue-detail/sidebar.tsx | 8 +- .../issue-layouts/list/roots/module-root.tsx | 4 +- .../issue-layouts/roots/cycle-layout-root.tsx | 31 +- .../roots/module-layout-root.tsx | 32 +- .../roots/project-layout-root.tsx | 6 +- web/components/issues/issue-modal/form.tsx | 29 +- web/components/issues/issue-modal/modal.tsx | 80 ++-- .../issues/peek-overview/issue-detail.tsx | 225 ++-------- .../issues/peek-overview/properties.tsx | 152 +++---- web/components/issues/peek-overview/root.tsx | 177 ++++---- web/components/issues/peek-overview/view.tsx | 210 ++++----- .../issues/sub-issues/issue-list-item.tsx | 199 +++++++++ web/components/issues/sub-issues/issue.tsx | 192 --------- .../issues/sub-issues/issues-list.tsx | 128 +++--- .../issues/sub-issues/properties.tsx | 101 ++--- web/components/issues/sub-issues/root.tsx | 404 +++++++++++------- web/components/modules/sidebar.tsx | 2 +- web/store/issue/cycle/filter.store.ts | 2 +- web/store/issue/issue-details/issue.store.ts | 4 + web/store/issue/issue-details/root.store.ts | 17 +- .../issue/issue-details/sub_issues.store.ts | 163 ++++--- web/store/issue/project/issue.store.ts | 18 +- 32 files changed, 1136 insertions(+), 1174 deletions(-) create mode 100644 web/components/issues/sub-issues/issue-list-item.tsx delete mode 100644 web/components/issues/sub-issues/issue.tsx diff --git a/apiserver/plane/app/views/issue.py b/apiserver/plane/app/views/issue.py index 837aa7e7b..967147aeb 100644 --- a/apiserver/plane/app/views/issue.py +++ b/apiserver/plane/app/views/issue.py @@ -82,7 +82,7 @@ from plane.db.models import ( from plane.bgtasks.issue_activites_task import issue_activity from plane.utils.grouper import group_results from plane.utils.issue_filters import issue_filters - +from collections import defaultdict class IssueViewSet(WebhookMixin, BaseViewSet): def get_serializer_class(self): @@ -784,22 +784,13 @@ class SubIssuesEndpoint(BaseAPIView): queryset=IssueReaction.objects.select_related("actor"), ) ) + .annotate(state_group=F("state__group")) ) - state_distribution = ( - State.objects.filter( - workspace__slug=slug, state_issue__parent_id=issue_id - ) - .annotate(state_group=F("group")) - .values("state_group") - .annotate(state_count=Count("state_group")) - .order_by("state_group") - ) - - result = { - item["state_group"]: item["state_count"] - for item in state_distribution - } + # create's a dict with state group name with their respective issue id's + result = defaultdict(list) + for sub_issue in sub_issues: + result[sub_issue.state_group].append(str(sub_issue.id)) serializer = IssueSerializer( sub_issues, @@ -831,7 +822,7 @@ class SubIssuesEndpoint(BaseAPIView): _ = Issue.objects.bulk_update(sub_issues, ["parent"], batch_size=10) - updated_sub_issues = Issue.issue_objects.filter(id__in=sub_issue_ids) + updated_sub_issues = Issue.issue_objects.filter(id__in=sub_issue_ids).annotate(state_group=F("state__group")) # Track the issue _ = [ @@ -846,11 +837,24 @@ class SubIssuesEndpoint(BaseAPIView): ) for sub_issue_id in sub_issue_ids ] + + # create's a dict with state group name with their respective issue id's + result = defaultdict(list) + for sub_issue in updated_sub_issues: + result[sub_issue.state_group].append(str(sub_issue.id)) + serializer = IssueSerializer( + updated_sub_issues, + many=True, + ) return Response( - IssueSerializer(updated_sub_issues, many=True).data, + { + "sub_issues": serializer.data, + "state_distribution": result, + }, status=status.HTTP_200_OK, ) + class IssueLinkViewSet(BaseViewSet): diff --git a/packages/types/src/issues/issue_sub_issues.d.ts b/packages/types/src/issues/issue_sub_issues.d.ts index 76dcf1288..e604761ed 100644 --- a/packages/types/src/issues/issue_sub_issues.d.ts +++ b/packages/types/src/issues/issue_sub_issues.d.ts @@ -1,11 +1,11 @@ import { TIssue } from "./issue"; export type TSubIssuesStateDistribution = { - backlog: number; - unstarted: number; - started: number; - completed: number; - cancelled: number; + backlog: string[]; + unstarted: string[]; + started: string[]; + completed: string[]; + cancelled: string[]; }; export type TIssueSubIssues = { diff --git a/web/components/core/sidebar/progress-chart.tsx b/web/components/core/sidebar/progress-chart.tsx index 274e5da0c..3d47d8eca 100644 --- a/web/components/core/sidebar/progress-chart.tsx +++ b/web/components/core/sidebar/progress-chart.tsx @@ -41,7 +41,7 @@ const DashedLine = ({ series, lineGenerator, xScale, yScale }: any) => )); const ProgressChart: React.FC = ({ distribution, startDate, endDate, totalIssues }) => { - const chartData = Object.keys(distribution).map((key) => ({ + const chartData = Object.keys(distribution ?? []).map((key) => ({ currentDate: renderFormattedDateWithoutYear(key), pending: distribution[key], })); diff --git a/web/components/core/sidebar/sidebar-progress-stats.tsx b/web/components/core/sidebar/sidebar-progress-stats.tsx index 698695ee4..c37cdf4b9 100644 --- a/web/components/core/sidebar/sidebar-progress-stats.tsx +++ b/web/components/core/sidebar/sidebar-progress-stats.tsx @@ -183,7 +183,7 @@ export const SidebarProgressStats: React.FC = ({ )} - {distribution.labels.length > 0 ? ( + {distribution?.labels.length > 0 ? ( distribution.labels.map((label, index) => ( = (props) => { async (formData: Partial) => { if (!formData?.name || formData?.name.length === 0 || formData?.name.length > 255) return; - await issueOperations.update(workspaceSlug, projectId, issueId, { - name: formData.name ?? "", - description_html: formData.description_html ?? "

", - }); + await issueOperations.update( + workspaceSlug, + projectId, + issueId, + { + name: formData.name ?? "", + description_html: formData.description_html ?? "

", + }, + false + ); }, [workspaceSlug, projectId, issueId, issueOperations] ); diff --git a/web/components/issues/issue-detail/label/label-list-item.tsx b/web/components/issues/issue-detail/label/label-list-item.tsx index 926d287aa..3c3164c5a 100644 --- a/web/components/issues/issue-detail/label/label-list-item.tsx +++ b/web/components/issues/issue-detail/label/label-list-item.tsx @@ -25,7 +25,7 @@ export const LabelListItem: FC = (props) => { const label = getLabelById(labelId); const handleLabel = async () => { - if (issue) { + if (issue && !disabled) { const currentLabels = issue.label_ids.filter((_labelId) => _labelId !== labelId); await labelOperations.updateIssue(workspaceSlug, projectId, issueId, { label_ids: currentLabels }); } diff --git a/web/components/issues/issue-detail/links/root.tsx b/web/components/issues/issue-detail/links/root.tsx index 1c226b7a7..94124085a 100644 --- a/web/components/issues/issue-detail/links/root.tsx +++ b/web/components/issues/issue-detail/links/root.tsx @@ -107,7 +107,7 @@ export const IssueLinkRoot: FC = (props) => { linkOperations={handleLinkOperations} /> -
+

Links

{!disabled && ( diff --git a/web/components/issues/issue-detail/main-content.tsx b/web/components/issues/issue-detail/main-content.tsx index 6e7ac4289..fcbe54a1c 100644 --- a/web/components/issues/issue-detail/main-content.tsx +++ b/web/components/issues/issue-detail/main-content.tsx @@ -87,10 +87,9 @@ export const IssueMainContent: React.FC = observer((props) => { )}
diff --git a/web/components/issues/issue-detail/parent/siblings.tsx b/web/components/issues/issue-detail/parent/siblings.tsx index b8ebc9ec9..bc93ff138 100644 --- a/web/components/issues/issue-detail/parent/siblings.tsx +++ b/web/components/issues/issue-detail/parent/siblings.tsx @@ -23,10 +23,10 @@ export const IssueParentSiblings: FC = (props) => { } = useIssueDetail(); const { isLoading } = useSWR( - peekIssue && parentIssue + peekIssue && parentIssue && parentIssue.project_id ? `ISSUE_PARENT_CHILD_ISSUES_${peekIssue?.workspaceSlug}_${parentIssue.project_id}_${parentIssue.id}` : null, - peekIssue && parentIssue + peekIssue && parentIssue && parentIssue.project_id ? () => fetchSubIssues(peekIssue?.workspaceSlug, parentIssue.project_id, parentIssue.id) : null ); diff --git a/web/components/issues/issue-detail/root.tsx b/web/components/issues/issue-detail/root.tsx index b52857e0a..4243ba03e 100644 --- a/web/components/issues/issue-detail/root.tsx +++ b/web/components/issues/issue-detail/root.tsx @@ -1,6 +1,7 @@ import { FC, useMemo } from "react"; import { useRouter } from "next/router"; // components +import { IssuePeekOverview } from "components/issues"; import { IssueMainContent } from "./main-content"; import { IssueDetailsSidebar } from "./sidebar"; // ui @@ -8,16 +9,23 @@ import { EmptyState } from "components/common"; // images import emptyIssue from "public/empty-state/issue.svg"; // hooks -import { useIssueDetail, useUser } from "hooks/store"; +import { useIssueDetail, useIssues, useUser } from "hooks/store"; import useToast from "hooks/use-toast"; // types import { TIssue } from "@plane/types"; // constants import { EUserProjectRoles } from "constants/project"; +import { EIssuesStoreType } from "constants/issue"; export type TIssueOperations = { fetch: (workspaceSlug: string, projectId: string, issueId: string) => Promise; - update: (workspaceSlug: string, projectId: string, issueId: string, data: Partial) => Promise; + update: ( + workspaceSlug: string, + projectId: string, + issueId: string, + data: Partial, + showToast?: boolean + ) => Promise; remove: (workspaceSlug: string, projectId: string, issueId: string) => Promise; addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => Promise; removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise; @@ -47,6 +55,9 @@ export const IssueDetailRoot: FC = (props) => { addIssueToModule, removeIssueFromModule, } = useIssueDetail(); + const { + issues: { removeIssue: removeArchivedIssue }, + } = useIssues(EIssuesStoreType.ARCHIVED); const { setToastAlert } = useToast(); const { membership: { currentProjectRole }, @@ -61,14 +72,22 @@ export const IssueDetailRoot: FC = (props) => { console.error("Error fetching the parent issue"); } }, - update: async (workspaceSlug: string, projectId: string, issueId: string, data: Partial) => { + update: async ( + workspaceSlug: string, + projectId: string, + issueId: string, + data: Partial, + showToast: boolean = true + ) => { try { await updateIssue(workspaceSlug, projectId, issueId, data); - setToastAlert({ - title: "Issue updated successfully", - type: "success", - message: "Issue updated successfully", - }); + if (showToast) { + setToastAlert({ + title: "Issue updated successfully", + type: "success", + message: "Issue updated successfully", + }); + } } catch (error) { setToastAlert({ title: "Issue update failed", @@ -79,7 +98,8 @@ export const IssueDetailRoot: FC = (props) => { }, remove: async (workspaceSlug: string, projectId: string, issueId: string) => { try { - await removeIssue(workspaceSlug, projectId, issueId); + if (is_archived) await removeArchivedIssue(workspaceSlug, projectId, issueId); + else await removeIssue(workspaceSlug, projectId, issueId); setToastAlert({ title: "Issue deleted successfully", type: "success", @@ -159,9 +179,11 @@ export const IssueDetailRoot: FC = (props) => { }, }), [ + is_archived, fetchIssue, updateIssue, removeIssue, + removeArchivedIssue, addIssueToCycle, removeIssueFromCycle, addIssueToModule, @@ -170,9 +192,9 @@ export const IssueDetailRoot: FC = (props) => { ] ); - // Issue details + // issue details const issue = getIssueById(issueId); - // Check if issue is editable, based on user role + // checking if issue is editable, based on user role const is_editable = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; return ( @@ -211,6 +233,9 @@ export const IssueDetailRoot: FC = (props) => {
)} + + {/* peek overview */} + ); }; diff --git a/web/components/issues/issue-detail/sidebar.tsx b/web/components/issues/issue-detail/sidebar.tsx index a80f88730..6b249f4bd 100644 --- a/web/components/issues/issue-detail/sidebar.tsx +++ b/web/components/issues/issue-detail/sidebar.tsx @@ -162,7 +162,7 @@ export const IssueDetailsSidebar: React.FC = observer((props) => { /> )} - {/* {(fieldsToShow.includes("all") || fieldsToShow.includes("link")) && ( + {(fieldsToShow.includes("all") || fieldsToShow.includes("link")) && ( - )} */} + )} - {/* {isAllowed && (fieldsToShow.includes("all") || fieldsToShow.includes("delete")) && ( + {is_editable && (fieldsToShow.includes("all") || fieldsToShow.includes("delete")) && ( - )} */} + )}
diff --git a/web/components/issues/issue-layouts/list/roots/module-root.tsx b/web/components/issues/issue-layouts/list/roots/module-root.tsx index 947cfe55b..7dbe38090 100644 --- a/web/components/issues/issue-layouts/list/roots/module-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/module-root.tsx @@ -25,12 +25,12 @@ export const ModuleListLayout: React.FC = observer(() => { [EIssueActions.UPDATE]: async (issue: TIssue) => { if (!workspaceSlug || !moduleId) return; - await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue); + await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue, moduleId.toString()); }, [EIssueActions.DELETE]: async (issue: TIssue) => { if (!workspaceSlug || !moduleId) return; - await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id); + await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id, moduleId.toString()); }, [EIssueActions.REMOVE]: async (issue: TIssue) => { if (!workspaceSlug || !moduleId) return; diff --git a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx index e5398ef90..be518ea04 100644 --- a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx @@ -13,6 +13,7 @@ import { CycleKanBanLayout, CycleListLayout, CycleSpreadsheetLayout, + IssuePeekOverview, } from "components/issues"; import { TransferIssues, TransferIssuesModal } from "components/cycles"; // ui @@ -73,19 +74,23 @@ export const CycleLayoutRoot: React.FC = observer(() => { cycleId={cycleId.toString()} /> ) : ( -
- {activeLayout === "list" ? ( - - ) : activeLayout === "kanban" ? ( - - ) : activeLayout === "calendar" ? ( - - ) : activeLayout === "gantt_chart" ? ( - - ) : activeLayout === "spreadsheet" ? ( - - ) : null} -
+ <> +
+ {activeLayout === "list" ? ( + + ) : activeLayout === "kanban" ? ( + + ) : activeLayout === "calendar" ? ( + + ) : activeLayout === "gantt_chart" ? ( + + ) : activeLayout === "spreadsheet" ? ( + + ) : null} +
+ {/* peek overview */} + + )} )} diff --git a/web/components/issues/issue-layouts/roots/module-layout-root.tsx b/web/components/issues/issue-layouts/roots/module-layout-root.tsx index 4478a0faa..808cad91b 100644 --- a/web/components/issues/issue-layouts/roots/module-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/module-layout-root.tsx @@ -6,6 +6,7 @@ import useSWR from "swr"; import { useIssues } from "hooks/store"; // components import { + IssuePeekOverview, ModuleAppliedFiltersRoot, ModuleCalendarLayout, ModuleEmptyState, @@ -16,6 +17,7 @@ import { } from "components/issues"; // ui import { Spinner } from "@plane/ui"; +// constants import { EIssuesStoreType } from "constants/issue"; export const ModuleLayoutRoot: React.FC = observer(() => { @@ -62,19 +64,23 @@ export const ModuleLayoutRoot: React.FC = observer(() => { moduleId={moduleId.toString()} /> ) : ( -
- {activeLayout === "list" ? ( - - ) : activeLayout === "kanban" ? ( - - ) : activeLayout === "calendar" ? ( - - ) : activeLayout === "gantt_chart" ? ( - - ) : activeLayout === "spreadsheet" ? ( - - ) : null} -
+ <> +
+ {activeLayout === "list" ? ( + + ) : activeLayout === "kanban" ? ( + + ) : activeLayout === "calendar" ? ( + + ) : activeLayout === "gantt_chart" ? ( + + ) : activeLayout === "spreadsheet" ? ( + + ) : null} +
+ {/* peek overview */} + + )} )} diff --git a/web/components/issues/issue-layouts/roots/project-layout-root.tsx b/web/components/issues/issue-layouts/roots/project-layout-root.tsx index bfff19cd8..1edba5563 100644 --- a/web/components/issues/issue-layouts/roots/project-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/project-layout-root.tsx @@ -30,7 +30,11 @@ export const ProjectLayoutRoot: FC = observer(() => { useSWR(workspaceSlug && projectId ? `PROJECT_ISSUES_${workspaceSlug}_${projectId}` : null, async () => { if (workspaceSlug && projectId) { await issuesFilter?.fetchFilters(workspaceSlug.toString(), projectId.toString()); - await issues?.fetchIssues(workspaceSlug.toString(), projectId.toString(), issues?.groupedIssueIds ? "mutation" : "init-loader"); + await issues?.fetchIssues( + workspaceSlug.toString(), + projectId.toString(), + issues?.groupedIssueIds ? "mutation" : "init-loader" + ); } }); diff --git a/web/components/issues/issue-modal/form.tsx b/web/components/issues/issue-modal/form.tsx index 1853f9546..d7c543079 100644 --- a/web/components/issues/issue-modal/form.tsx +++ b/web/components/issues/issue-modal/form.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState, useRef } from "react"; +import React, { FC, useState, useRef, useEffect } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Controller, useForm } from "react-hook-form"; @@ -6,7 +6,7 @@ import { LayoutPanelTop, Sparkle, X } from "lucide-react"; // editor import { RichTextEditorWithRef } from "@plane/rich-text-editor"; // hooks -import { useApplication, useEstimate, useMention, useProject } from "hooks/store"; +import { useApplication, useEstimate, useIssueDetail, useMention, useProject } from "hooks/store"; import useToast from "hooks/use-toast"; // services import { AIService } from "services/ai.service"; @@ -85,6 +85,9 @@ export const IssueFormRoot: FC = observer((props) => { const { getProjectById } = useProject(); const { areEstimatesEnabledForProject } = useEstimate(); const { mentionHighlights, mentionSuggestions } = useMention(); + const { + issue: { getIssueById }, + } = useIssueDetail(); // toast alert const { setToastAlert } = useToast(); // form info @@ -179,6 +182,28 @@ export const IssueFormRoot: FC = observer((props) => { const projectDetails = getProjectById(projectId); + // executing this useEffect when the parent_id coming from the component prop + useEffect(() => { + const parentId = watch("parent_id") || undefined; + if (!parentId) return; + if (parentId === selectedParentIssue?.id || selectedParentIssue) return; + + const issue = getIssueById(parentId); + if (!issue) return; + + const projectDetails = getProjectById(issue.project_id); + if (!projectDetails) return; + + setSelectedParentIssue({ + id: issue.id, + name: issue.name, + project_id: issue.project_id, + project__identifier: projectDetails.identifier, + project__name: projectDetails.name, + sequence_id: issue.sequence_id, + } as ISearchIssueResponse); + }, [watch, getIssueById, getProjectById, selectedParentIssue]); + return ( <> {projectId && ( diff --git a/web/components/issues/issue-modal/modal.tsx b/web/components/issues/issue-modal/modal.tsx index 55a25b33d..81a07d761 100644 --- a/web/components/issues/issue-modal/modal.tsx +++ b/web/components/issues/issue-modal/modal.tsx @@ -18,7 +18,7 @@ export interface IssuesModalProps { data?: Partial; isOpen: boolean; onClose: () => void; - onSubmit?: (res: Partial) => Promise; + onSubmit?: (res: TIssue) => Promise; withDraftIssueWrapper?: boolean; } @@ -58,52 +58,46 @@ export const CreateUpdateIssueModal: React.FC = observer((prop onClose(); }; - const handleCreateIssue = async (payload: Partial): Promise => { - if (!workspaceSlug || !payload.project_id) return null; + const handleCreateIssue = async (payload: Partial): Promise => { + if (!workspaceSlug || !payload.project_id) return undefined; - await createIssue(workspaceSlug.toString(), payload.project_id, payload) - .then(async (res) => { - setToastAlert({ - type: "success", - title: "Success!", - message: "Issue created successfully.", - }); - !createMore && handleClose(); - return res; - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Issue could not be created. Please try again.", - }); + try { + const response = await createIssue(workspaceSlug.toString(), payload.project_id, payload); + setToastAlert({ + type: "success", + title: "Success!", + message: "Issue created successfully.", }); - - return null; + !createMore && handleClose(); + return response; + } catch (error) { + setToastAlert({ + type: "error", + title: "Error!", + message: "Issue could not be created. Please try again.", + }); + } }; - const handleUpdateIssue = async (payload: Partial): Promise => { - if (!workspaceSlug || !payload.project_id || !data?.id) return null; + const handleUpdateIssue = async (payload: Partial): Promise => { + if (!workspaceSlug || !payload.project_id || !data?.id) return undefined; - await updateIssue(workspaceSlug.toString(), payload.project_id, data.id, payload) - .then((res) => { - setToastAlert({ - type: "success", - title: "Success!", - message: "Issue updated successfully.", - }); - handleClose(); - return { ...payload, ...res }; - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Issue could not be updated. Please try again.", - }); + try { + const response = await updateIssue(workspaceSlug.toString(), payload.project_id, data.id, payload); + setToastAlert({ + type: "success", + title: "Success!", + message: "Issue updated successfully.", }); - - return null; + handleClose(); + return response; + } catch (error) { + setToastAlert({ + type: "error", + title: "Error!", + message: "Issue could not be created. Please try again.", + }); + } }; const handleFormSubmit = async (formData: Partial) => { @@ -114,7 +108,7 @@ export const CreateUpdateIssueModal: React.FC = observer((prop description_html: formData.description_html ?? "

", }; - let res: TIssue | null = null; + let res: TIssue | undefined = undefined; if (!data?.id) res = await handleCreateIssue(payload); else res = await handleUpdateIssue(payload); @@ -126,7 +120,7 @@ export const CreateUpdateIssueModal: React.FC = observer((prop if (formData.module_id && res && (!data?.id || formData.module_id !== data?.module_id)) await addIssueToModule(workspaceSlug.toString(), formData.project_id, formData.module_id, [res.id]); - if (res && onSubmit) await onSubmit(res); + if (res != undefined && onSubmit) await onSubmit(res); }; const handleFormChange = (formData: Partial | null) => setChangesMade(formData); diff --git a/web/components/issues/peek-overview/issue-detail.tsx b/web/components/issues/peek-overview/issue-detail.tsx index 3eb7037f2..8a0ab0fe7 100644 --- a/web/components/issues/peek-overview/issue-detail.tsx +++ b/web/components/issues/peek-overview/issue-detail.tsx @@ -1,133 +1,32 @@ -import { ChangeEvent, FC, useCallback, useEffect, useState } from "react"; -import { Controller, useForm } from "react-hook-form"; -import debounce from "lodash/debounce"; -// packages -import { RichTextEditor } from "@plane/rich-text-editor"; +import { FC } from "react"; // hooks -import { useMention, useProject, useUser } from "hooks/store"; -import useReloadConfirmations from "hooks/use-reload-confirmation"; +import { useIssueDetail, useProject, useUser } from "hooks/store"; // components -import { IssuePeekOverviewReactions } from "components/issues"; -// ui -import { TextArea } from "@plane/ui"; -// types -import { TIssue, IUser } from "@plane/types"; -// services -import { FileService } from "services/file.service"; -// constants -import { EUserProjectRoles } from "constants/project"; - -const fileService = new FileService(); +import { IssueDescriptionForm, TIssueOperations } from "components/issues"; +import { IssueReaction } from "../issue-detail/reactions"; interface IPeekOverviewIssueDetails { workspaceSlug: string; - issue: TIssue; - issueReactions: any; - user: IUser | null; - issueUpdate: (issue: Partial) => void; - issueReactionCreate: (reaction: string) => void; - issueReactionRemove: (reaction: string) => void; + projectId: string; + issueId: string; + issueOperations: TIssueOperations; + is_archived: boolean; + disabled: boolean; isSubmitting: "submitting" | "submitted" | "saved"; setIsSubmitting: (value: "submitting" | "submitted" | "saved") => void; } export const PeekOverviewIssueDetails: FC = (props) => { - const { - workspaceSlug, - issue, - issueReactions, - user, - issueUpdate, - issueReactionCreate, - issueReactionRemove, - isSubmitting, - setIsSubmitting, - } = props; - // states - const [characterLimit, setCharacterLimit] = useState(false); + const { workspaceSlug, projectId, issueId, issueOperations, disabled, isSubmitting, setIsSubmitting } = props; // store hooks - const { - membership: { currentProjectRole }, - } = useUser(); - const { mentionHighlights, mentionSuggestions } = useMention(); const { getProjectById } = useProject(); - // derived values - const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; - // toast alert - const { setShowAlert } = useReloadConfirmations(); - // form info + const { currentUser } = useUser(); const { - handleSubmit, - watch, - reset, - control, - formState: { errors }, - } = useForm({ - defaultValues: { - name: issue.name, - description_html: issue.description_html, - }, - }); - - const handleDescriptionFormSubmit = useCallback( - async (formData: Partial) => { - if (!formData?.name || formData?.name.length === 0 || formData?.name.length > 255) return; - - await issueUpdate({ - ...issue, - name: formData.name ?? "", - description_html: formData.description_html ?? "

", - }); - }, - [issue, issueUpdate] - ); - - const [localTitleValue, setLocalTitleValue] = useState(""); - const [localIssueDescription, setLocalIssueDescription] = useState({ - id: issue.id, - description_html: issue.description_html, - }); - - // adding issue.description_html or issue.name to dependency array causes - // editor rerendering on every save - useEffect(() => { - if (issue.id) { - setLocalIssueDescription({ id: issue.id, description_html: issue.description_html }); - setLocalTitleValue(issue.name); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [issue.id]); // TODO: Verify the exhaustive-deps warning - - // ADDING handleDescriptionFormSubmit TO DEPENDENCY ARRAY PRODUCES ADVERSE EFFECTS - // TODO: Verify the exhaustive-deps warning - // eslint-disable-next-line react-hooks/exhaustive-deps - const debouncedFormSave = useCallback( - debounce(async () => { - handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted")); - }, 1500), - [handleSubmit] - ); - - useEffect(() => { - if (isSubmitting === "submitted") { - setShowAlert(false); - setTimeout(async () => { - setIsSubmitting("saved"); - }, 2000); - } else if (isSubmitting === "submitting") { - setShowAlert(true); - } - }, [isSubmitting, setShowAlert, setIsSubmitting]); - - // reset form values - useEffect(() => { - if (!issue) return; - - reset({ - ...issue, - }); - }, [issue, reset]); - + issue: { getIssueById }, + } = useIssueDetail(); + // derived values + const issue = getIssueById(issueId); + if (!issue) return <>; const projectDetails = getProjectById(issue?.project_id); return ( @@ -135,82 +34,24 @@ export const PeekOverviewIssueDetails: FC = (props) = {projectDetails?.identifier}-{issue?.sequence_id} - -
- {isAllowed ? ( - ( -