Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: filter users with user_id #1268

Merged
merged 9 commits into from
Oct 29, 2024
9 changes: 9 additions & 0 deletions backend/app/services/mongo/query_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def main_doc_filter_tasks(self, prefix: str = "") -> Dict[str, object]:
- metadata
- user_id
- task_position
- excluded_users
"""
filters = self.filters
match: Dict[str, object] = self._main_doc_filter(prefix=prefix)
Expand Down Expand Up @@ -254,6 +255,9 @@ def main_doc_filter_tasks(self, prefix: str = "") -> Dict[str, object]:
if filters.task_position is not None:
match[f"{prefix}task_position"] = filters.task_position

if filters.excluded_users is not None:
match[f"{prefix}metadata.user_id"] = {"$nin": filters.excluded_users}

if match:
self.pipeline.append({"$match": match})

Expand Down Expand Up @@ -457,6 +461,7 @@ async def session_complex_filters(self) -> Dict[str, object]:
- clustering_id
- clusters_ids
- user_id
- excluded_users
- metadata (filter on the Task's metadata)

Not supported:
Expand Down Expand Up @@ -568,6 +573,10 @@ async def session_complex_filters(self) -> Dict[str, object]:
for key, value in filters.metadata.items():
match[f"metadata.{key}"] = value

if filters.excluded_users is not None:
self.merge_tasks()
match["tasks.metadata.user_id"] = {"$nin": filters.excluded_users}

if match:
self.pipeline.append({"$match": match})

Expand Down
318 changes: 159 additions & 159 deletions backend/poetry.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions phospho-python/phospho/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ class ProjectSettings(BaseModel):
)
analytics_threshold_enabled: bool = False
analytics_threshold: int = 100_000
excluded_users: Optional[List[str]] = None


class Project(DatedBaseModel):
Expand Down Expand Up @@ -771,6 +772,7 @@ class ProjectDataFilters(BaseModel):
task_position: Optional[int] = None
task_id_search: Optional[str] = None
session_id_search: Optional[str] = None
excluded_users: Optional[List[str]] = None


class Cluster(ProjectElementBaseModel):
Expand Down
20 changes: 4 additions & 16 deletions phospho-python/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 24 additions & 5 deletions platform/app/org/settings/project/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { CopyButton } from "@/components/copy-button";
import { InteractiveDatetime } from "@/components/interactive-datetime";
import CreateProjectDialog from "@/components/projects/create-project-form";
import AlertDialogDeleteProject from "@/components/projects/delete-project-popup";
import ExcludeUsersDialog from "@/components/projects/exclude-users-form";
import AnalyticsSettings from "@/components/settings/disable-analytics";
import {
AlertDialog,
Expand All @@ -15,13 +16,14 @@ import { authFetcher } from "@/lib/fetcher";
import { Project } from "@/models/models";
import { navigationStateStore } from "@/store/store";
import { useUser } from "@propelauth/nextjs/client";
import { BriefcaseBusiness, Pencil } from "lucide-react";
import { BriefcaseBusiness, Pencil, UserRound } from "lucide-react";
import { useState } from "react";
import useSWR from "swr";

export default function Page() {
const { accessToken } = useUser();
const [open, setOpen] = useState(false);
const [openRename, setOpenRename] = useState(false);
const [openExcludeUsers, setOpenExcludeUsers] = useState(false);

const project_id = navigationStateStore((state) => state.project_id);
const { data: selectedProject }: { data: Project } = useSWR(
Expand Down Expand Up @@ -64,8 +66,8 @@ export default function Page() {
<InteractiveDatetime timestamp={selectedProject.created_at} />
</div>
</div>
<div>
<AlertDialog open={open} onOpenChange={setOpen}>
<div className="space-x-2">
<AlertDialog open={openRename} onOpenChange={setOpenRename}>
<AlertDialogTrigger asChild>
<Button variant="secondary">
<Pencil className="size-4 mr-2" />
Expand All @@ -74,7 +76,24 @@ export default function Page() {
</AlertDialogTrigger>
<AlertDialogContent className="md:w-1/3">
<CreateProjectDialog
setOpen={setOpen}
setOpen={setOpenRename}
projectToEdit={selectedProject}
/>
</AlertDialogContent>
</AlertDialog>
<AlertDialog
open={openExcludeUsers}
onOpenChange={setOpenExcludeUsers}
>
<AlertDialogTrigger asChild>
<Button variant="secondary">
<UserRound className="size-4 mr-2" />
Exclude users
</Button>
</AlertDialogTrigger>
<AlertDialogContent className="md:w-1/3">
<ExcludeUsersDialog
setOpen={setOpenExcludeUsers}
projectToEdit={selectedProject}
/>
</AlertDialogContent>
Expand Down
2 changes: 1 addition & 1 deletion platform/components/clusters/clusters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const Clusters: React.FC = () => {
const setSelectedClustering = navigationStateStore(
(state) => state.setSelectedClustering,
);
const [dropdownOpen, setDropdownOpen] = React.useState(false);
const [dropdownOpen, setDropdownOpen] = useState(false);

const { data: clusteringsData } = useSWR(
project_id
Expand Down
66 changes: 66 additions & 0 deletions platform/components/filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
TextSearch,
ThumbsDown,
ThumbsUp,
UserRound,
X,
} from "lucide-react";
import Link from "next/link";
Expand Down Expand Up @@ -121,6 +122,8 @@ const FilterComponent = ({
)
: {};

const excludedUsers = selectedProject?.settings?.excluded_users;

const resetPagination = () => {
if (variant === "tasks") {
setTasksPagination((prev) => ({
Expand Down Expand Up @@ -425,6 +428,21 @@ const FilterComponent = ({
<X className="size-4 ml-2" />
</Button>
)}
{dataFilters.excluded_users && (
<Button
variant="outline"
onClick={() => {
setDataFilters({
...dataFilters,
excluded_users: null,
});
resetPagination();
}}
>
Exclude users in settings
<X className="size-4 ml-2" />
</Button>
)}
{dataFilters && activeFilterCount > 0 && (
<HoverCard openDelay={0} closeDelay={0}>
<HoverCardTrigger>
Expand Down Expand Up @@ -948,6 +966,54 @@ const FilterComponent = ({
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<UserRound className="size-4 mr-2" />
<span>Users</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent className="overflow-y-auto max-h-[30rem]">
{excludedUsers && (
<>
<DropdownMenuItem
onClick={() => {
// Override the event_name filter
// TODO: Allow multiple event_name filters
setDataFilters({
...dataFilters,
excluded_users: excludedUsers,
});
resetPagination();
}}
style={{
color: dataFilters.excluded_users ? "green" : "inherit",
}}
>
Exclude users in settings
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link
href="/org/settings/project"
className="flex items-center px-2 py-1.5 text-sm cursor-pointer hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
>
<TextSearch className="size-4 mr-2 flex-shrink-0" />
<span className="truncate">Manage list</span>
</Link>
</DropdownMenuItem>
</>
)}
{!excludedUsers && (
<Link
href="/org/settings/project"
className="flex items-center px-2 py-1.5 text-sm cursor-pointer hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
>
<TextSearch className="size-4 mr-2 flex-shrink-0" />
<span className="truncate">Setup list</span>
</Link>
)}
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
{/* Toggle search bar */}
<DropdownMenuSeparator />
<DropdownMenuItem
Expand Down
Loading
Loading