diff --git a/.gitignore b/.gitignore index e40a1621d..aadc6b299 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,6 @@ exports/ # coverage .coverage coverage.xml + +# pgdata +pgdata/ \ No newline at end of file diff --git a/Makefile b/Makefile index d730a8474..004cf871c 100644 --- a/Makefile +++ b/Makefile @@ -34,10 +34,10 @@ format_diff: ## run code formatters in diff mode poetry run ruff --select I pandasai examples tests spell_check: ## run codespell on the project - poetry run codespell --toml pyproject.toml --skip=$(IGNORE_FORMATS) -L NotIn + poetry run codespell --toml pyproject.toml --ignore-words=ignore-words.txt --skip=$(IGNORE_FORMATS) spell_fix: ## run codespell on the project and fix the errors - poetry run codespell --toml pyproject.toml --skip=$(IGNORE_FORMATS) -L NotIn -w + poetry run codespell --toml pyproject.toml --ignore-words=ignore-words.txt --skip=$(IGNORE_FORMATS) -w ###################### # DOCS diff --git a/README.md b/README.md index 2ccb712be..392481817 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ![PandasAI](images/logo.png) +# ![PandasAI](assets/logo.png) [![Release](https://img.shields.io/pypi/v/pandasai?label=Release&style=flat-square)](https://pypi.org/project/pandasai/) [![CI](https://github.com/gventuri/pandas-ai/actions/workflows/ci.yml/badge.svg)](https://github.com/gventuri/pandas-ai/actions/workflows/ci.yml/badge.svg) @@ -8,13 +8,49 @@ [![Downloads](https://static.pepy.tech/badge/pandasai)](https://pepy.tech/project/pandasai) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1ZnO-njhL7TBOYPZaqvMvGtsjckZKrv2E?usp=sharing) -PandasAI is a Python library that makes it easy to ask questions to your data in natural language. It helps you to explore, clean, and analyze your data using generative AI. +PandasAI is a Python platform that makes it easy to ask questions to your data in natural language. It helps non-technical users to interact with their data in a more natural way, and it helps technical users to save time and effort when working with data. + +# 🚀 Deploying PandasAI + +PandasAI can be used in a variety of ways. You can easily use it in your Jupyter notebooks or streamlit apps, or you can deploy it as a REST API such as with FastAPI or Flask. + +If you are interested in the managed PandasAI Cloud or our self-hosted Enterprise Offering, take a look at [our website](https://pandas-ai.com) or [book a meeting with us](https://zcal.co/gventuri/pandas-ai-demo). # 🔧 Getting started -The documentation for PandasAI to use it with specific LLMs, vector stores and connectors, can be found [here](https://pandas-ai.readthedocs.io/en/latest/). +You can find the full documentation for PandasAI [here](https://pandas-ai.readthedocs.io/en/latest/). + +You can either decide to use PandasAI in your Jupyter notebooks, streamlit apps, or use the client and server architecture from the repo. + +## ☁️ Using the platform + +[![PandasAI platform](assets/demo.gif?raw=true)](https://www.youtube.com/watch?v=kh61wEy9GYM) + +### 📦 Installation + +PandasAI platform is uses a dockerized client-server architecture. You will need to have Docker installed in your machine. + +```bash +git clone https://github.com/sinaptik-ai/pandas-ai/ +cd pandas-ai +docker-compose build +``` + +### 🚀 Running the platform -## 📦 Installation +Once you have built the platform, you can run it with: + +```bash +docker-compose up +``` + +This will start the client and server, and you can access the client at `http://localhost:3000`. + +## 📚 Using the library + +### 📦 Installation + +You can install the PandasAI library using pip or poetry. With pip: @@ -28,21 +64,15 @@ With poetry: poetry add pandasai ``` -## 🔍 Demo +### 🔍 Demo -Try out PandasAI yourself in your browser: +Try out the PandasAI library yourself in your browser: [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1ZnO-njhL7TBOYPZaqvMvGtsjckZKrv2E?usp=sharing) -# 🚀 Deploying PandasAI - -PandasAI can be deployed in a variety of ways. You can easily use it in your Jupyter notebooks or streamlit apps, or you can deploy it as a REST API such as with FastAPI or Flask. - -If you are interested in managed PandasAI Cloud or self-hosted Enterprise Offering, take a look at [our website](https://pandas-ai.com) or [book a meeting with us](https://zcal.co/gventuri/pandas-ai-demo). - -## 💻 Usage +### 💻 Usage -### Ask questions +#### Ask questions ```python import os @@ -52,7 +82,7 @@ from pandasai import Agent # Sample DataFrame sales_by_country = pd.DataFrame({ "country": ["United States", "United Kingdom", "France", "Germany", "Italy", "Spain", "Canada", "Australia", "Japan", "China"], - "sales": [5000, 3200, 2900, 4100, 2300, 2100, 2500, 2600, 4500, 7000] + "revenue": [5000, 3200, 2900, 4100, 2300, 2100, 2500, 2600, 4500, 7000] }) # By default, unless you choose a different LLM, it will use BambooLLM. @@ -81,19 +111,19 @@ agent.chat( The total sales for the top 3 countries by sales is 16500. ``` -### Visualize charts +#### Visualize charts You can also ask PandasAI to generate charts for you: ```python agent.chat( - "Plot the histogram of countries showing for each the gdp, using different colors for each bar", + "Plot the histogram of countries showing for each one the gd. Use different colors for each bar", ) ``` -![Chart](images/histogram-chart.png?raw=true) +![Chart](assets/histogram-chart.png?raw=true) -### Multiple DataFrames +#### Multiple DataFrames You can also pass in multiple dataframes to PandasAI and ask questions relating them. diff --git a/assets/demo.gif b/assets/demo.gif new file mode 100644 index 000000000..93011cd0f Binary files /dev/null and b/assets/demo.gif differ diff --git a/images/histogram-chart.png b/assets/histogram-chart.png similarity index 100% rename from images/histogram-chart.png rename to assets/histogram-chart.png diff --git a/images/logo.png b/assets/logo.png similarity index 100% rename from images/logo.png rename to assets/logo.png diff --git a/client/.env.example b/client/.env.example new file mode 100644 index 000000000..38397e403 --- /dev/null +++ b/client/.env.example @@ -0,0 +1,15 @@ +# THE URL OF YOUR CLIENT +NEXT_PUBLIC_API_URL='http://localhost:8000/' + +# ENVIRONMENT +NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN="development" + +# ANALYTICS +NEXT_PUBLIC_MIXPANEL_TOKEN="04a81d58ade99299e19e5a89fe84c670" +NEXT_PUBLIC_GOOGLE_ANALYTICS_ID="G-SD7MBGS6J9" + +# SUPPORT CHAT +NEXT_PUBLIC_INTERCOM_APP_ID="jzfk3qc5" + +# ENV +NODE_ENV="development" \ No newline at end of file diff --git a/client/.eslintrc.json b/client/.eslintrc.json new file mode 100644 index 000000000..2e25c6214 --- /dev/null +++ b/client/.eslintrc.json @@ -0,0 +1,27 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "next/core-web-vitals", + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2023, + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint", + "react" + ], + "rules": { + "react/no-unescaped-entities": ["error", { "forbid": [">", "}"] }], + "@typescript-eslint/no-explicit-any": "off", + "no-useless-catch": "off", + "react-hooks/exhaustive-deps": 0 + } +} diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 000000000..d1241c144 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,40 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local +.env +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# lock files +package-lock.json +yarn-lock.json \ No newline at end of file diff --git a/client/Dockerfile b/client/Dockerfile new file mode 100644 index 000000000..7cfa1a322 --- /dev/null +++ b/client/Dockerfile @@ -0,0 +1,15 @@ +FROM node:19.4.0-alpine3.17 + +WORKDIR /app + +COPY package*.json ./ + +RUN npm install + +COPY . . + +RUN npm run build + +EXPOSE 3000 + +CMD ["npm", "start"] diff --git a/client/Providers/AppProvider.tsx b/client/Providers/AppProvider.tsx new file mode 100644 index 000000000..9d32f888c --- /dev/null +++ b/client/Providers/AppProvider.tsx @@ -0,0 +1,62 @@ +"use client"; +import React, { ReactNode, useEffect } from "react"; +import ContextProvider from "@/contexts/ContextProvider"; +import { usePathname } from "next/navigation"; +import dynamic from "next/dynamic"; +import { getTitleFromPath } from "@/utils/getTitleFromPath"; +import { + NEXT_PUBLIC_INTERCOM_APP_ID, + NEXT_PUBLIC_MIXPANEL_TOKEN, + NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN, +} from "@/utils/constants"; +import mixpanel from "mixpanel-browser"; +import { Provider as RollbarProvider, ErrorBoundary } from "@rollbar/react"; +import QueryProvider from "./QueryProvider"; +import Intercom from "@/components/Intercom/Intercom"; +import { ToastContainer } from "react-toastify"; +import "styles/globals.css"; +import "styles/App.css"; +import "styles/multi-range-slider.css"; +import "react-toastify/dist/ReactToastify.css"; +import "react-tooltip/dist/react-tooltip.css"; + +const _NoSSR = ({ children }) => {children}; +const NoSSR = dynamic(() => Promise.resolve(_NoSSR), { + ssr: false, +}); + +export default function AppProvider({ children }: { children: ReactNode }) { + const pathname = usePathname(); + const defaultTitle = getTitleFromPath(pathname); + + const rollbarConfig = { + accessToken: NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN, + environment: process.env.NODE_ENV || "development", + }; + + useEffect(() => { + document.title = `${defaultTitle} | PandaBI`; + }, [defaultTitle]); + + useEffect(() => { + mixpanel.init(NEXT_PUBLIC_MIXPANEL_TOKEN, { + autotrack: true, + track_pageview: true, + }); + }, []); + return ( + <> + + + + + + {children} + + + + + + + ); +} diff --git a/client/Providers/QueryProvider.tsx b/client/Providers/QueryProvider.tsx new file mode 100644 index 000000000..fe5c2ad2e --- /dev/null +++ b/client/Providers/QueryProvider.tsx @@ -0,0 +1,17 @@ +"use client"; +import React, { ReactNode, useState } from "react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; + +const QueryProvider = ({ children }: { children: ReactNode }) => { + const [queryClient] = useState(() => new QueryClient()); + return ( + // Provide the client to your App + + + {children} + + ); +}; + +export default QueryProvider; diff --git a/client/README.md b/client/README.md new file mode 100644 index 000000000..c4033664f --- /dev/null +++ b/client/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/client/app/admin/chat/page.tsx b/client/app/admin/chat/page.tsx new file mode 100644 index 000000000..fe112f5b0 --- /dev/null +++ b/client/app/admin/chat/page.tsx @@ -0,0 +1,297 @@ +"use client"; +import { ChatApi } from "services/chat"; +import React, { useState, useEffect, useRef } from "react"; +import { ChatMessageData } from "../../../types/chat-types"; +import { useSearchParams } from "next/navigation"; +import { toast } from "react-toastify"; +import ChatScreen from "components/ChatScreen"; +import { + UPDATE_CONVERSATION, + useConversationsContext, +} from "contexts/ConversationsProvider"; +import mixpanel from "mixpanel-browser"; +import { reorderArray } from "utils/reorderConversations"; +import { appendQueryParamtoURL } from "utils/appendQueryParamtoURL"; +import { useAppStore } from "store"; +import { ConversationMessages } from "@/services/conversations"; + +const ChatPage = () => { + const params = useSearchParams(); + const conversation_id = params.get("conversationId"); + + const [conversationId, setConversationId] = useState( + conversation_id || "" + ); + const [sendQuery, setSendQuery] = useState(false); + const [chat, setChat] = useState([]); + const [isTyping, setIsTyping] = useState(false); + const setIsNewChatClicked = useAppStore((state) => state.setIsNewChatClicked); + const isNewChatClicked = useAppStore((state) => state.isNewChatClicked); + + const [scrollLoad, setScrollLoad] = useState(false); + const [rawApiData, setRawApiData] = useState([]); + const [hasMore, setHasMore] = useState(false); + const [mounted, setMounted] = useState(true); + const [isGetConversationsLoading, setIsGetConversationsLoading] = + useState(false); + + const queryRef = useRef(null); + const spaceId = localStorage.getItem("spaceId"); + const firstRequest = React.useRef(false); + const searchQuery = localStorage.getItem("searchQuery"); + const { state, dispatch } = useConversationsContext(); + + const rawApiDataLengthRef = useRef(rawApiData.length); + const scrollDivRef = useRef(null); + const firstRender = useRef(false); + + useEffect(() => { + if (queryRef.current) { + queryRef.current.focus(); + } + }, []); + + useEffect(() => { + if (sendQuery) { + const query = queryRef.current.value; + chat.push({ + space_id: spaceId, + conversation_id: conversationId, + query: query, + }); + setIsTyping(true); + setChat(chat); + process.nextTick(() => { + scrollToBottom(); + }); + fetchChatData(queryRef.current.value); + queryRef.current.value = ""; + } + }, [sendQuery]); + + useEffect(() => { + if (searchQuery) { + chat.push({ + space_id: spaceId, + conversation_id: conversationId, + query: searchQuery, + }); + setIsTyping(true); + setChat(chat); + fetchChatData(searchQuery); + localStorage.removeItem("searchQuery"); + } + }, []); + + const fetchChatData = async (userQuery: string) => { + await ChatApi({ + workspace_id: spaceId, + conversation_id: conversationId, + query: userQuery, + }) + .then((response) => { + const conId = response?.data?.data?.conversation_id; + const messageId = response?.data?.data?.message_id; + const responseQuery = response?.data?.data?.query; + const code = response?.data?.data?.code; + mixpanel.track("New Chat Message", { userQuery: userQuery }); + + if (response?.data?.data?.response) { + chat.push({ + response: response?.data?.data?.response, + id: messageId, + thumbs_up: null, + code, + reaction_id: null, + space_id: spaceId, + conversation_id: conId, + }); + if (!firstRequest.current && !conversation_id) { + AddNewMessageToConversations(conId, messageId, responseQuery); + firstRequest.current = true; + appendQueryParamtoURL(`conversationId=${conId}`); + } + } else { + chat.push({ + response: response?.data?.data, + thumbs_up: null, + reaction_id: null, + space_id: spaceId, + conversation_id: conversationId, + code: null, + }); + } + setConversationId(conId); + setChat([...chat]); + }) + .catch((error) => { + console.log(JSON.stringify(error)); + toast.error( + error?.response?.data?.message + ? error?.response?.data?.message + : error?.message + ); + }) + .finally(() => { + setSendQuery(false); + setIsTyping(false); + process.nextTick(() => { + scrollToBottom(); + }); + }); + }; + + const AddNewMessageToConversations = ( + conId: string, + messageId: string, + query: string + ) => { + const newConversations = state?.conversations; + const today = new Date().toISOString(); + const newMessage = { + id: messageId, + query, + }; + const todayIndex = newConversations.findIndex( + (entry) => entry.date === "Today" + ); + if (todayIndex !== -1) { + const todayConversations = newConversations[todayIndex].conversations; + todayConversations.unshift({ + id: conId, + space_id: spaceId, + user_id: null, + created_at: today, + space: { + id: spaceId, + }, + messages: [newMessage], + }); + } else { + newConversations.unshift({ + date: "Today", + conversations: [ + { + id: conId, + space_id: spaceId, + user_id: null, + created_at: today, + space: { + id: spaceId, + }, + messages: [newMessage], + }, + ], + }); + } + dispatch({ + type: UPDATE_CONVERSATION, + payload: newConversations, + }); + }; + + useEffect(() => { + if (conversation_id) { + fetchConversationHistory([], []); + } + }, []); + + const fetchConversationHistory = async (NewChat, newRawApiData) => { + if (NewChat?.length < 1) { + setScrollLoad(true); + } + + setIsGetConversationsLoading(true); + await ConversationMessages(conversation_id) + .then((response) => { + const responseData = response?.data?.data?.messages; + if (responseData != undefined) { + setRawApiData([...newRawApiData, ...responseData]); + } + const conversation_history_query = response?.data?.data?.messages?.map( + (data) => { + return { + query: data?.query, + createdAt: data?.created_at, + id: `${data.id}1`, + conversation_id: conversationId, + }; + } + ); + const conversation_history_resp = response?.data?.data?.messages?.map( + (data) => { + return { + response: data?.response, + createdAt: data?.created_at, + id: data.id, + code: data?.code_generated, + conversation_id: conversationId, + thumbs_up: data?.user_rating, + plotSettings: data?.settings, + }; + } + ); + let conversation_history = conversation_history_query != undefined && [ + ...conversation_history_query, + ...conversation_history_resp, + ]; + conversation_history = reorderArray(conversation_history); + setMounted(false); + setChat([...conversation_history, ...NewChat]); + firstRender.current = true; + rawApiDataLengthRef.current = newRawApiData.length; + }) + .catch((error) => { + toast.error( + error?.response?.data?.message + ? error.response.data.message + : error.message + ); + }) + .finally(() => { + setHasMore(false); + setScrollLoad(false); + setIsGetConversationsLoading(false); + }); + }; + + useEffect(() => { + // state cleanup + if (isNewChatClicked) { + setConversationId(""); + setSendQuery(false); + setChat([]); + setIsTyping(false); + setRawApiData([]); + setHasMore(false); + setMounted(true); + firstRequest.current = false; + setIsNewChatClicked(false); + firstRender.current = false; + } + }, [isNewChatClicked]); + + const scrollToBottom = () => { + if (scrollDivRef.current) { + scrollDivRef.current.scrollTop = scrollDivRef.current.scrollHeight; + } + }; + + return ( +
+ +
+ ); +}; +export default ChatPage; diff --git a/client/app/admin/conversations/[id]/messages/page.tsx b/client/app/admin/conversations/[id]/messages/page.tsx new file mode 100644 index 000000000..f3d163a63 --- /dev/null +++ b/client/app/admin/conversations/[id]/messages/page.tsx @@ -0,0 +1,268 @@ +"use client"; +import { ChatApi } from "services/chat"; +import React, { useState, useEffect, useRef } from "react"; +import { useRouter } from "next/navigation"; +import { useParams } from "next/navigation"; +import { ChatMessageData } from "@/types/chat-types"; +import { toast } from "react-toastify"; +import ChatScreen from "components/ChatScreen"; +import { reorderArray } from "utils/reorderConversations"; +import { ConversationMessages } from "@/services/conversations"; + +const ChatDetails = () => { + const rowsPerPage = 8; + const [sendQuery, setSendQuery] = useState(false); + const [chat, setChat] = useState([]); + const [isTyping, setIsTyping] = useState(false); + const [currentPage, setCurrentPage] = useState(1); + const [scrollLoad, setScrollLoad] = useState(false); + const [rawApiData, setRawApiData] = useState([]); + const [hasMore, setHasMore] = useState(false); + const [mounted, setMounted] = useState(true); + const [totalPages, setTotalPages] = useState(1); + const [skip, setSkip] = useState(0); + const [isLoading, setIsLoading] = useState(true); + const firstRender = useRef(false); + + const router = useRouter(); + + const params: { id: string } = useParams(); + const conversation_id = params.id; + const space_id = localStorage.getItem("spaceId"); + + const rawApiDataLengthRef = useRef(rawApiData.length); + const scrollDivRef = useRef(null); + const queryRef = useRef(null); + + useEffect(() => { + rawApiDataLengthRef.current = rawApiData.length; + }, [rawApiData]); + + const fetchConversationHistory = async ( + NewChat, + newRawApiData, + skip: number + ) => { + if (NewChat?.length < 1) { + setScrollLoad(true); + } + + setIsLoading(true); + await ConversationMessages(conversation_id, skip, rowsPerPage) + .then((response) => { + const totalCount = response.data?.data?.count; + if (mounted) { + setTotalPages(Math.ceil(totalCount / rowsPerPage)); + } + + const responseData = response?.data?.data?.messages; + + if (responseData != undefined) { + setRawApiData([...newRawApiData, ...responseData]); + } + const conversation_history_query = response?.data?.data?.messages?.map( + (data) => { + return { + query: data?.query, + createdAt: data?.created_at, + id: `${data.id}1`, + conversation_id: conversation_id, + }; + } + ); + const conversation_history_resp = response?.data?.data?.messages?.map( + (data) => { + return { + response: data?.response, + createdAt: data?.created_at, + id: data.id, + code: data?.code_generated, + conversation_id: conversation_id, + thumbs_up: data?.user_rating, + plotSettings: data?.settings, + }; + } + ); + let conversation_history = conversation_history_query != undefined && [ + ...conversation_history_query, + ...conversation_history_resp, + ]; + conversation_history = reorderArray(conversation_history); + setMounted(false); + setChat([...conversation_history, ...NewChat]); + firstRender.current = true; + rawApiDataLengthRef.current = newRawApiData.length; + localStorage.setItem("prevConversationId", conversation_id); + }) + .catch((error) => { + toast.error( + error?.response?.data?.message + ? error.response.data.message + : error.message + ); + }) + .finally(() => { + setHasMore(false); + setScrollLoad(false); + setIsLoading(false); + process.nextTick(() => { + scrollToBottom(); + }); + }); + }; + + const handleScroll = (e) => { + const target = e.target; + if (target.scrollTop === 0 && !scrollLoad) { + if (currentPage < totalPages) { + setHasMore(true); + setCurrentPage((prevPage) => prevPage + 1); + setSkip(currentPage * rowsPerPage); + } + } + }; + + useEffect(() => { + if (firstRender?.current) { + if (Number(rawApiDataLengthRef.current) <= rowsPerPage) { + const scrollDiv = document.getElementById("chat-div"); + // scrollDiv.scrollTop = 90; + if (scrollDiv) { + scrollDiv.scrollTop = scrollDiv.scrollHeight; + firstRender.current = false; + } + } + } + }, [chat]); + + useEffect(() => { + const scrollDiv = document.getElementById("chat-div"); + + if (scrollDiv) { + scrollDiv.addEventListener("scroll", handleScroll); + + return () => { + scrollDiv.removeEventListener("scroll", handleScroll); + }; + } + }, [currentPage, totalPages]); + + useEffect(() => { + const fetchDataOnScroll = async () => { + if (!mounted) { + const scrollDiv = document.getElementById("chat-div"); + + // Store previous scroll height and current scroll position + const previousScrollHeight = scrollDiv.scrollHeight; + const previousScrollTop = scrollDiv.scrollTop; + + fetchConversationHistory(chat, rawApiData, skip).then(() => { + process.nextTick(() => { + scrollDiv.classList.remove("chat-div"); + scrollDiv.scrollTop = + scrollDiv.scrollHeight - previousScrollHeight + previousScrollTop; + }); + }); + } + }; + + fetchDataOnScroll(); + }, [currentPage]); + + useEffect(() => { + fetchConversationHistory([], [], 0); + }, [conversation_id, space_id]); + + useEffect(() => { + if (sendQuery) { + const query = queryRef?.current?.value; + chat.push({ + space_id: space_id, + conversation_id: conversation_id, + query: query, + }); + setIsTyping(true); + setChat(chat); + process.nextTick(() => { + scrollToBottom(); + }); + const fetchChatData = async () => { + await ChatApi({ + workspace_id: space_id, + conversation_id: conversation_id, + query: query, + }) + .then((response) => { + const conId = response?.data?.data?.conversation_id; + const messageId = response?.data?.data?.message_id; + const code = response?.data?.data?.code; + if (response?.data?.data?.response) { + chat.push({ + response: response?.data?.data?.response, + id: messageId, + thumbs_up: null, + code, + reaction_id: null, + space_id: response?.data?.data?.space_id, + conversation_id: conId, + }); + } else { + chat.push({ + response: response?.data?.data, + thumbs_up: null, + reaction_id: null, + space_id: space_id, + conversation_id: conversation_id, + code: null, + }); + } + setChat([...chat]); + }) + .catch((error) => { + console.log(JSON.stringify(error)); + toast.error( + error?.response?.data?.message + ? error?.response?.data?.message + : error?.message + ); + }) + .finally(() => { + setSendQuery(false); + setIsTyping(false); + process.nextTick(() => { + scrollToBottom(); + }); + }); + }; + fetchChatData(); + queryRef!.current!.value = ""; + } + }, [sendQuery]); + + if (!conversation_id && !space_id) { + return router.push(`/admin/chat`); + } + const scrollToBottom = () => { + if (scrollDivRef.current) { + scrollDivRef.current.scrollTop = scrollDivRef.current.scrollHeight; + } + }; + + return ( +
+ +
+ ); +}; +export default ChatDetails; diff --git a/client/app/admin/layout.tsx b/client/app/admin/layout.tsx new file mode 100644 index 000000000..ef3fa378e --- /dev/null +++ b/client/app/admin/layout.tsx @@ -0,0 +1,59 @@ +"use client"; +import { isWindowAvailable } from "utils/navigation"; +import React from "react"; +import LeftBar from "components/LayoutComponents/LeftBar"; +import VerticalLineSeperator from "components/VerticalLineSeperator"; +import RightBar from "components/LayoutComponents/RightBar"; +import Navbar from "components/Navbar"; +import { useAppStore } from "store"; +import Drawer from "react-modern-drawer"; +import useWindowWidth from "hooks/useWindowWidth"; +import "react-modern-drawer/dist/index.css"; + +export default function Admin({ children }: { children: React.ReactNode }) { + if (isWindowAvailable()) document.documentElement.dir = "ltr"; + const isRightSidebarOpen = useAppStore((state) => state.isRightSidebarOpen); + const setIsRightSidebarOpen = useAppStore( + (state) => state.setIsRightSidebarOpen + ); + const width = useWindowWidth(); + const isMobile = width <= 1200; + + return ( + <> +
+ +
+
+ {/* Previous Conversations Sidebar */} +
+ + +
+ + {/* Main Content */} +
+ {children} +
+ + {/* Right Sidebar */} + {/*
+ + +
*/} +
+
+
+ {isMobile && ( + setIsRightSidebarOpen(false)} + direction="right" + size={350} + > + + + )} + + ); +} diff --git a/client/app/admin/page.tsx b/client/app/admin/page.tsx new file mode 100644 index 000000000..711165dba --- /dev/null +++ b/client/app/admin/page.tsx @@ -0,0 +1,4 @@ +import { redirect } from "next/navigation"; +export default function Home() { + redirect("/admin/chat"); +} diff --git a/client/app/favicon.ico b/client/app/favicon.ico new file mode 100644 index 000000000..170c9a7a7 Binary files /dev/null and b/client/app/favicon.ico differ diff --git a/client/app/layout.tsx b/client/app/layout.tsx new file mode 100644 index 000000000..2b9980a9d --- /dev/null +++ b/client/app/layout.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import { Poppins } from "next/font/google"; +import { GoogleAnalytics } from "@next/third-parties/google"; +import AppProvider from "@/Providers/AppProvider"; +import { NEXT_PUBLIC_GOOGLE_ANALYTICS_ID } from "@/utils/constants"; + +const poppins = Poppins({ + subsets: ["latin"], + weight: ["300", "400", "500", "600", "700"], +}); + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + + ); +} diff --git a/client/app/page.tsx b/client/app/page.tsx new file mode 100644 index 000000000..8985fcfcb --- /dev/null +++ b/client/app/page.tsx @@ -0,0 +1,48 @@ +"use client"; +import { useRouter } from "next/navigation"; +import React, { useEffect } from "react"; +import { ROUTE_ADMIN } from "utils/constants"; +import { useGetMe } from "@/hooks/useUsers"; +import { Loader } from "@/components/loader/Loader"; + +const MainPage = () => { + const router = useRouter(); + const { data: workspaceResponse, isLoading, isError } = useGetMe(); + const myDetails = workspaceResponse?.data; + + const handleSpaceClick = () => { + localStorage.setItem("firstName", myDetails?.first_name); + localStorage.setItem("email", myDetails?.email); + localStorage.setItem("user_id", myDetails?.id); + localStorage.setItem( + "selectedOrganization", + JSON.stringify(myDetails?.organizations[0]) + ); + localStorage.setItem("spaceId", myDetails?.space?.id); + localStorage.setItem("spaceName", myDetails?.space?.name); + router.push(ROUTE_ADMIN); + }; + + useEffect(() => { + if (workspaceResponse?.data) { + handleSpaceClick(); + } + }, [workspaceResponse]); + + return ( + <> + {isLoading && ( +
+ +
+ )} + {isError && ( +
+ Something went wrong fetching credentials, please refresh the page +
+ )} + + ); +}; + +export default MainPage; diff --git a/client/components.json b/client/components.json new file mode 100644 index 000000000..a1af1e15f --- /dev/null +++ b/client/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/styles/globals.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} \ No newline at end of file diff --git a/client/components/AddUserModal/index.tsx b/client/components/AddUserModal/index.tsx new file mode 100644 index 000000000..dc360cad4 --- /dev/null +++ b/client/components/AddUserModal/index.tsx @@ -0,0 +1,201 @@ +import React, { useState } from "react"; +import { AiFillDelete } from "react-icons/ai"; +import Image from "next/image"; +import { useAddSpaceUsers, useDeleteSpaceUsers } from "hooks/useSpaces"; +import { Loader } from "components/loader/Loader"; +import { toast } from "react-toastify"; +import { useGetMembersList, useGetOrganizations } from "hooks/useOrganizations"; +import { Tooltip } from "react-tooltip"; +import { Input } from "../ui/input"; + +interface ISpaceUser { + space_id: string; + user_id: string; + Users: { + email: string; + first_name: string; + last_name: string; + }; +} + +interface IProps { + setIsModelOpen: (e) => void; + spaceUsers: ISpaceUser[]; + spaceId: string; +} + +const AddUserModal = ({ setIsModelOpen, spaceUsers, spaceId }: IProps) => { + const [isSearchOpen, setIsSearchOpen] = useState(false); + const [memberData, setMemberData] = useState(""); + const { data: organizationListResponse } = useGetOrganizations(); + const orgId = organizationListResponse?.data?.data[0]?.id; + const { data: memberList } = useGetMembersList(orgId); + const { mutateAsync: deleteSpaceUser, isPending: isDeletePending } = + useDeleteSpaceUsers(); + const { mutateAsync: addSpaceUser, isPending: isAddPending } = + useAddSpaceUsers(); + + let unmatchedElements; + if (spaceUsers?.length === 0) { + unmatchedElements = memberList?.data?.data; + } else { + unmatchedElements = memberList?.data?.data?.filter( + (item1) => !spaceUsers?.some((item2) => item1?.user_id === item2?.user_id) + ); + } + + const handleDeleteSpaceUser = async (user_id) => { + const payload = { + user_id: user_id, + }; + deleteSpaceUser( + { spaceId, payload }, + { + onSuccess(response) { + toast.success(response?.data?.message); + setIsModelOpen(false); + }, + onError(error: any) { + toast.error( + error?.response?.data?.message + ? error?.response?.data?.message + : error.message + ); + setIsModelOpen(false); + }, + } + ); + }; + const addUserToSpace = async (data) => { + const payload = { + new_member_email: data.email, + }; + addSpaceUser( + { spaceId, payload }, + { + onSuccess(response) { + toast.success(response?.data?.message); + setIsModelOpen(false); + }, + onError(error) { + toast.error(error?.message); + setIsModelOpen(false); + }, + } + ); + }; + + return ( + <> +

Add users

+ {isAddPending || isDeletePending ? ( + + ) : ( +
+
+ {spaceUsers?.map((user) => ( + <> +
+

+ Gabriele Venturi{" "} + {user?.Users?.first_name} {user?.Users?.last_name} +

+ {spaceUsers?.length === 1 ? ( + handleDeleteSpaceUser(user?.user_id)} + > + + + + You cannot remove the sole user from this workspace + + + ) : ( + handleDeleteSpaceUser(user?.user_id)} + > + + + )} +
+ + ))} +
+
+ { + setIsSearchOpen(true); + }} + placeholder="Search for users" + autoComplete="off" + onChange={(e) => { + setMemberData(e.target.value); + }} + /> +
+ + {isSearchOpen && ( +
+ {unmatchedElements?.length > 0 && + unmatchedElements + ?.filter((Mdata) => { + if (memberData === "") { + return true; + } else if ( + Mdata?.first_name + ?.toLowerCase() + ?.includes(memberData.toLowerCase()) + ) { + return true; + } + return false; + }) + .map((data, key) => { + return ( +

{ + addUserToSpace(data); + }} + > + dummy image{" "} + {data?.first_name} {data?.last_name} +

+ ); + })} +
+ )} +
+ )} + + ); +}; + +export default AddUserModal; diff --git a/client/components/AppAccordion/index.tsx b/client/components/AppAccordion/index.tsx new file mode 100644 index 000000000..be665a475 --- /dev/null +++ b/client/components/AppAccordion/index.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import { useAppStore } from "store"; + +interface IProps { + title: string; + children: React.ReactNode; + islastItem?: boolean; +} + +const AppAccordion = ({ title, children, islastItem = false }: IProps) => { + const darkMode = useAppStore((state) => state.darkMode); + + return ( +
+ + {title} +
+ + + + + +
+
+
{children}
+
+ ); +}; + +export default AppAccordion; diff --git a/client/components/AppModal/index.tsx b/client/components/AppModal/index.tsx new file mode 100644 index 000000000..7f4437987 --- /dev/null +++ b/client/components/AppModal/index.tsx @@ -0,0 +1,80 @@ +import React from "react"; +import { createPortal } from "react-dom"; +import { AiOutlineClose } from "react-icons/ai"; +import { Button } from "../ui/button"; +import { Loader } from "../loader/Loader"; + +interface IProps { + closeModal?: () => void; + handleSubmit?: () => void; + children: React.ReactNode; + modalWidth?: string; + actionButtonText?: string; + isLoading?: boolean; + isFooter?: boolean; +} + +export const AppModal = ({ + closeModal, + children, + modalWidth, + handleSubmit, + actionButtonText, + isLoading, + isFooter = true, +}: IProps) => { + return ( + <> + {createPortal( +
{ + if (e.target.className === "modal-container") closeModal(); + }} + > +
+ { + closeModal(); + }} + > + + + +
{children}
+ {isFooter && ( +
+ + +
+ )} +
+
, + document.body + )} + + ); +}; diff --git a/client/components/Buttons/Button.tsx b/client/components/Buttons/Button.tsx new file mode 100644 index 000000000..bb89d26ce --- /dev/null +++ b/client/components/Buttons/Button.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import Link from "next/link"; + +const Button = ({ + link, + text, + className, +}: { + link?: string; + text: string; + className?: string; +}) => ( + +
+ {text} +
+ +); +export default Button; diff --git a/client/components/CardConnector/index.tsx b/client/components/CardConnector/index.tsx new file mode 100644 index 000000000..b4d6bbb9e --- /dev/null +++ b/client/components/CardConnector/index.tsx @@ -0,0 +1,101 @@ +import React from "react"; +import { BiLogoPostgresql } from "react-icons/bi"; +import { FaFileCsv, FaSnowflake } from "react-icons/fa"; +import { SiAirtable, SiSqlite } from "react-icons/si"; +import { + TbBrandDatabricks, + TbBrandMysql, + TbBrandGoogleBigQuery, +} from "react-icons/tb"; + +const CardConnector = ({ + id, + isSelected, + onClick, + pointerEvents, +}: { + id: number; + isSelected: boolean; + onClick: (cardId: number) => void; + pointerEvents: React.CSSProperties["pointerEvents"]; +}) => { + const cardStyle: React.CSSProperties = { + backgroundColor: isSelected ? "black" : "white", + color: isSelected ? "white" : "black", + opacity: 0.5, + pointerEvents: pointerEvents, + }; + return ( +
onClick(id)} + className="border flex items-center justify-center md:h-20 md:w-24 md:p-2 py-2 px-4 rounded cursor-pointer" + > + {id === 1 && ( +
+ + + + CSV +
+ )} + {id === 2 && ( +
+ + + + PostgreSql +
+ )} + {id === 3 && ( +
+ + + + MySQL +
+ )} + {id === 4 && ( +
+ + + + Sqlite +
+ )} + {id === 5 && ( +
+ + + + Snowflake +
+ )} + {id === 6 && ( +
+ + + + Databricks +
+ )} + {id === 7 && ( +
+ + + + BigQuery +
+ )} + {id === 8 && ( +
+ + + + Airtable +
+ )} +
+ ); +}; +export default CardConnector; diff --git a/client/components/ChatLoader/page.tsx b/client/components/ChatLoader/page.tsx new file mode 100644 index 000000000..619e46cac --- /dev/null +++ b/client/components/ChatLoader/page.tsx @@ -0,0 +1,17 @@ +"use client"; +import React from "react"; + +const ChatLoader = () => { + return ( +
+
+
+
+
+
+
+
+
+ ); +}; +export default ChatLoader; diff --git a/client/components/ChatScreen/AIChatBubble.tsx b/client/components/ChatScreen/AIChatBubble.tsx new file mode 100644 index 000000000..4e6cc6ec3 --- /dev/null +++ b/client/components/ChatScreen/AIChatBubble.tsx @@ -0,0 +1,103 @@ +import React, { memo } from "react"; +import Image from "next/image"; +import panda from "public/svg/panda.svg"; +import { ChatMessageData } from "@/types/chat-types"; +import ChatDataFrame from "./ChatDataFrame"; +import ChatPlot from "./ChatPlot"; + +interface IProps { + chat: ChatMessageData; + lastIndex?: boolean; +} + +const AIChatBubble = ({ chat, lastIndex }: IProps) => { + const checkType = (type: string) => { + if (typeof chat.response !== "string") { + return chat?.response?.every((item) => item?.type === type); + } + }; + const isMultiple = () => { + return chat?.response?.length > 1; + }; + + return ( +
+
+
+ logo +
+ +
+ PandaBI +
+ {typeof chat.response !== "string" && + chat?.response?.map((response, indx) => ( +
+ + {response?.type === "dataframe" ? ( + response?.value?.headers?.length > 0 && + response?.value?.rows?.length > 0 ? ( + <> + + + ) : ( +

No Table data available

+ ) + ) : response?.type === "plot" ? ( + + ) : response?.type === "string" ? ( +
+ {response?.message} +
+ ) : response.type === "number" ? ( +

+ {response?.value as string} +

+ ) : ( + "" + )} + +
+
+
+ ))} +
+
+
+
+ ); +}; + +export default memo(AIChatBubble); diff --git a/client/components/ChatScreen/ChatDataFrame.tsx b/client/components/ChatScreen/ChatDataFrame.tsx new file mode 100644 index 000000000..b3388a223 --- /dev/null +++ b/client/components/ChatScreen/ChatDataFrame.tsx @@ -0,0 +1,204 @@ +"use client"; +import { FetchDataframe } from "services/chat"; +import { ChatResponseItem } from "@/types/chat-types"; +import React, { useState, useRef, useEffect } from "react"; +import { FaChevronDown, FaChevronUp } from "react-icons/fa"; +import { toast } from "react-toastify"; + +interface IProps { + chatResponse: ChatResponseItem; + index: number; + chatId: string; +} + +const StyledRecord = ({ record }: { record: string }) => { + if (!record) return record; + + if (record === "true" || record === "True") { + return ( + + + + + + ); + } else if (record === "false" || record === "False") { + return ( + + + + + + ); + } else if (record.includes("http") || record.includes("www")) { + return ( + + {record} + + ); + } else if (record.includes("@") && record.includes(".")) { + return ( + + {record} + + ); + } else { + const dateRegex = /^\d{4}-\d{2}-\d{2}$/; + if (dateRegex.test(record)) { + const date = new Date(record); + const options: Intl.DateTimeFormatOptions = { + year: "numeric", + month: "long", + day: "numeric", + hour: "numeric", + minute: "numeric", + }; + return date.toLocaleDateString("en-US", options); + } else { + return record; + } + } +}; + +const ChatDataFrame = ({ chatResponse, index, chatId }: IProps) => { + const [expandedCardId, setExpandedCardId] = useState(null); + const [contentHeight, setContentHeight] = useState("300px"); + const [isDownloading, setIsDownloading] = useState(false); + const contentRef = useRef(null); + + useEffect(() => { + if (expandedCardId === index && contentRef.current) { + setContentHeight(`${contentRef.current.scrollHeight}px`); + } else { + setContentHeight("300px"); + } + }, [expandedCardId, index]); + + const handleToggleExpand = (index) => { + setExpandedCardId((prevId) => (prevId === index ? null : index)); + }; + + const handleDownload = async (id: string) => { + setIsDownloading(true); + await FetchDataframe(id) + .then((response) => { + const fileUrl = response?.data?.data?.download_url; + const link = document.createElement("a"); + link.href = fileUrl; + link.setAttribute("download", `dataframe-${id}`); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }) + .catch((error) => { + toast.error( + error?.response?.data?.message + ? error.response.data.message + : error.message + ); + }) + .finally(() => setIsDownloading(false)); + }; + + return ( +
+
+ + + + {chatResponse?.value?.headers?.map((item, index) => ( + + ))} + + + + {chatResponse?.value?.rows?.map((rows, index) => ( + + {Object.values(rows).map((item, index) => ( + + ))} + + ))} + +
+ {item.split("_").join(" ")} +
+ +
+ {chatResponse?.value?.rows?.length > 3 && ( +
+ )} + + {expandedCardId === index && ( +
+ +
+ )} +
+
+ {chatResponse?.value?.rows?.length > 3 && ( + + )} +
+
+ ); +}; + +export default ChatDataFrame; diff --git a/client/components/ChatScreen/ChatInput.tsx b/client/components/ChatScreen/ChatInput.tsx new file mode 100644 index 000000000..0407b7c93 --- /dev/null +++ b/client/components/ChatScreen/ChatInput.tsx @@ -0,0 +1,70 @@ +"use client"; + +import ChatSearchIcon from "components/Icons/ChatSearchIcon"; +import { useRouter } from "next/navigation"; +import React from "react"; +import { ROUTE_CHAT_SCREEN } from "utils/constants"; + +interface IProps { + queryRef: React.RefObject; + sendQuery?: boolean; + onNewMessage?: (message: string) => void; + extra?: string; + handleSearch?: (e) => void; + handleKeyDown?: (e) => void; + type?: "feed" | "chat"; + isAutoCompleteOpen?: boolean; +} + +const ChatInput = ({ + queryRef, + sendQuery = false, + onNewMessage, + extra, + handleKeyDown, + handleSearch, + type = "chat", + isAutoCompleteOpen, +}: IProps) => { + const router = useRouter(); + + return ( +
{ + e.preventDefault(); + if (onNewMessage) onNewMessage(queryRef.current.value); + if (type === "feed") { + router.push(ROUTE_CHAT_SCREEN); + localStorage.setItem("searchQuery", queryRef.current.value); + } + }} + > +
+ + + + +
+
+ ); +}; + +export default ChatInput; diff --git a/client/components/ChatScreen/ChatLabel.tsx b/client/components/ChatScreen/ChatLabel.tsx new file mode 100644 index 000000000..f996cba9a --- /dev/null +++ b/client/components/ChatScreen/ChatLabel.tsx @@ -0,0 +1,32 @@ +import { GetChatLabelLoader } from "components/Skeletons"; +import { useGetChatLabel } from "hooks/useConversations"; +import React from "react"; + +interface IProps { + chatId: string; + message: number | string; +} + +const ChatLabel = ({ chatId, message }: IProps) => { + const { isLoading, data, isError, error } = useGetChatLabel(chatId && chatId); + + return ( +
+ + {isLoading ? ( + + ) : isError ? ( + error.message + ) : ( + data?.data?.data?.["result-label"] + )} + +
+

+ {message && message} +

+
+ ); +}; + +export default ChatLabel; diff --git a/client/components/ChatScreen/ChatPlot.tsx b/client/components/ChatScreen/ChatPlot.tsx new file mode 100644 index 000000000..0b2591de9 --- /dev/null +++ b/client/components/ChatScreen/ChatPlot.tsx @@ -0,0 +1,66 @@ +"use client"; +import { ChatResponseItem } from "@/types/chat-types"; +import ChartRenderer from "components/LoadChartJs/ChartRenderer"; +import Image from "next/image"; +import React, { useState } from "react"; +import { AppModal } from "../AppModal"; + +interface IProps { + chatResponse: ChatResponseItem; + plotSettings: any; +} + +const ChatPlot = ({ chatResponse, plotSettings }: IProps) => { + const [isModalOpen, setIsModalOpen] = useState(false); + const [selectedImage, setSelectedImage] = useState(null); + const onOpenModal = () => setIsModalOpen(true); + const handleCloseModal = () => { + setIsModalOpen(false); + setSelectedImage(""); + }; + const handleImageClick = (imageUrl) => { + setSelectedImage(imageUrl); + onOpenModal(); + }; + + return ( + <> + {typeof chatResponse?.value === "string" ? ( +
handleImageClick(chatResponse?.value)} + className="cursor-pointer" + > + {chatResponse?.value +
+ ) : ( + + )} + + {isModalOpen && ( + + {selectedImage} + + )} + + ); +}; + +export default ChatPlot; diff --git a/client/components/ChatScreen/UserChatBubble.tsx b/client/components/ChatScreen/UserChatBubble.tsx new file mode 100644 index 000000000..483d64832 --- /dev/null +++ b/client/components/ChatScreen/UserChatBubble.tsx @@ -0,0 +1,32 @@ +import Image from "next/image"; +import React from "react"; + +interface IProps { + query: string; +} + +const UserChatBubble = ({ query }: IProps) => { + return ( +
+
+
+ logo +
+
+ You +
+

{query}

+
+
+
+
+ ); +}; + +export default UserChatBubble; diff --git a/client/components/ChatScreen/chat-types.ts b/client/components/ChatScreen/chat-types.ts new file mode 100644 index 000000000..f0ffc867a --- /dev/null +++ b/client/components/ChatScreen/chat-types.ts @@ -0,0 +1,17 @@ +import { ChatMessageData } from "@/types/chat-types"; + +export interface ChatScreenProps { + scrollLoad: boolean; + isTyping: boolean; + sendQuery: boolean; + setSendQuery: (value: boolean) => void; + chatData: ChatMessageData[]; + setChatData: (e) => void; + hasMore: boolean; + scrollDivRef?: React.RefObject + queryRef: React.RefObject; + setFollowUpQuestionDiv?: (e) => void; + followUpQuestionDiv?: boolean; + followUpQuestions?: { id: string, question: string }[]; + loading?: boolean; +} diff --git a/client/components/ChatScreen/index.tsx b/client/components/ChatScreen/index.tsx new file mode 100644 index 000000000..7712d5f10 --- /dev/null +++ b/client/components/ChatScreen/index.tsx @@ -0,0 +1,116 @@ +"use client"; +import React from "react"; +import Image from "next/image"; +import ChatLoader from "components/ChatLoader/page"; +import panda from "public/svg/panda.svg"; +import { ChatScreenProps } from "./chat-types"; +import ChatInput from "./ChatInput"; +import UserChatBubble from "./UserChatBubble"; +import AIChatBubble from "./AIChatBubble"; +import { ChatPlaceholderLoader } from "components/Skeletons"; + +const ChatScreen = ({ + scrollLoad, + chatData, + isTyping, + queryRef, + sendQuery, + setSendQuery, + hasMore, + loading, + scrollDivRef, +}: ChatScreenProps) => { + const handleNewMessage = () => { + setSendQuery(true); + }; + + return ( +
+
+
+
+
+
+ {scrollLoad ? ( + + ) : ( + <> + {hasMore && ( +
+ +
+ )} + {loading ? ( + + ) : ( + <> + {chatData?.length === 0 && ( +

+ How can I help you today? +

+ )} + {chatData?.map((chat, chatIndex) => ( +
+ {chat?.query ? ( + + ) : ( + + )} +
+ ))} + {isTyping && ( + <> +
+
+
+ logo +
+
+
+ +
+
+
+
+ + )} + + )} + + )} +
+
+
+
+
+ +
+
+
+ ); +}; + +export default ChatScreen; diff --git a/client/components/ConfirmationDialog/index.tsx b/client/components/ConfirmationDialog/index.tsx new file mode 100644 index 000000000..03ff253ca --- /dev/null +++ b/client/components/ConfirmationDialog/index.tsx @@ -0,0 +1,33 @@ +"use client"; +import { AppModal } from "components/AppModal"; +import React from "react"; + +interface IProps { + text: string; + onCancel?: () => void; + onSubmit?: () => void; + isLoading?: boolean; + actionButtonText?: string; +} + +const ConfirmationDialog = ({ + text, + onCancel, + onSubmit, + isLoading, + actionButtonText = "Yes", +}: IProps) => { + return ( + +

{text}

+
+ ); +}; + +export default ConfirmationDialog; diff --git a/client/components/Icons/AddWorkSpace.tsx b/client/components/Icons/AddWorkSpace.tsx new file mode 100644 index 000000000..3eef65d87 --- /dev/null +++ b/client/components/Icons/AddWorkSpace.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +const AddWorkSpace = ({ color, size }: { color: string; size?: number }) => { + return ( + + + + ); +}; + +export default AddWorkSpace; diff --git a/client/components/Icons/ChatSearchIcon.tsx b/client/components/Icons/ChatSearchIcon.tsx new file mode 100644 index 000000000..a6c53668f --- /dev/null +++ b/client/components/Icons/ChatSearchIcon.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +const ChatSearchIcon = ({ color = "white" }: { color?: string }) => { + return ( + + + + ); +}; + +export default ChatSearchIcon; diff --git a/client/components/Icons/EmptyHeart.tsx b/client/components/Icons/EmptyHeart.tsx new file mode 100644 index 000000000..3e4d546e7 --- /dev/null +++ b/client/components/Icons/EmptyHeart.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +const EmptyHeart = ({ color = "white" }: { color?: string }) => { + return ( + + + + ); +}; + +export default EmptyHeart; diff --git a/client/components/Icons/ExplainIcon.tsx b/client/components/Icons/ExplainIcon.tsx new file mode 100644 index 000000000..c1f5e3990 --- /dev/null +++ b/client/components/Icons/ExplainIcon.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +const ExplainIcon = ({ color = "white" }: { color?: string }) => { + return ( + + + + ); +}; + +export default ExplainIcon; diff --git a/client/components/Icons/GenerateCodeIcon.tsx b/client/components/Icons/GenerateCodeIcon.tsx new file mode 100644 index 000000000..e5790293e --- /dev/null +++ b/client/components/Icons/GenerateCodeIcon.tsx @@ -0,0 +1,29 @@ +import React from "react"; + +const GenerateCodeIcon = ({ color = "black" }: { color?: string }) => { + return ( + + + + + + + + ); +}; + +export default GenerateCodeIcon; diff --git a/client/components/Icons/LogoDark.tsx b/client/components/Icons/LogoDark.tsx new file mode 100644 index 000000000..1415b2b04 --- /dev/null +++ b/client/components/Icons/LogoDark.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import Image from "next/image"; + +const LogoDark = ({ + width = 100, + height = 100, +}: { + width?: number; + height?: number; +}) => { + return ( + Logo + ); +}; + +export default LogoDark; diff --git a/client/components/Icons/ReloadChatIcon.tsx b/client/components/Icons/ReloadChatIcon.tsx new file mode 100644 index 000000000..6bd3ca7ae --- /dev/null +++ b/client/components/Icons/ReloadChatIcon.tsx @@ -0,0 +1,28 @@ +import React from "react"; + +const ReloadChatIcon = ({ color = "white" }: { color?: string }) => { + return ( + + + + + + ); +}; + +export default ReloadChatIcon; diff --git a/client/components/Icons/StartChatIcon.tsx b/client/components/Icons/StartChatIcon.tsx new file mode 100644 index 000000000..8ed8593ed --- /dev/null +++ b/client/components/Icons/StartChatIcon.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +const StartChatIcon = ({ color = "white" }: { color?: string }) => { + return ( + + + + ); +}; + +export default StartChatIcon; diff --git a/client/components/Intercom/Intercom.tsx b/client/components/Intercom/Intercom.tsx new file mode 100644 index 000000000..d479ae468 --- /dev/null +++ b/client/components/Intercom/Intercom.tsx @@ -0,0 +1,56 @@ +import { useEffect } from "react"; +import PropTypes from "prop-types"; + +const Intercom = ({ appID, ...userProps }) => { + useEffect(() => { + (function () { + const w = window; + const ic = w.Intercom; + if (typeof ic === "function") { + ic("reattach_activator"); + ic("update", w.intercomSettings); + } else { + const d = document; + const i = function () { + // eslint-disable-next-line prefer-rest-params + i.c(arguments); + }; + i.q = []; + i.c = function (args) { + i.q.push(args); + }; + w.Intercom = i; + + const l = function () { + const s = d.createElement("script"); + s.type = "text/javascript"; + s.async = true; + s.src = `https://widget.intercom.io/widget/${appID}`; + const x = d.getElementsByTagName("script")[0]; + x.parentNode.insertBefore(s, x); + }; + + if (d.readyState === "complete") { + l(); + } else if (w.attachEvent) { + w.attachEvent("onload", l); + } else { + w.addEventListener("load", l, false); + } + } + + window.intercomSettings = { + app_id: appID, + ...userProps, + }; + })(); + }, [appID, userProps]); + + return null; +}; + +Intercom.propTypes = { + appID: PropTypes.string.isRequired, +}; + +export default Intercom; diff --git a/client/components/LayoutComponents/ConversationItem.tsx b/client/components/LayoutComponents/ConversationItem.tsx new file mode 100644 index 000000000..e9836cd10 --- /dev/null +++ b/client/components/LayoutComponents/ConversationItem.tsx @@ -0,0 +1,234 @@ +"use client"; +import { + FETCH_CONVERSATION, + useConversationsContext, +} from "contexts/ConversationsProvider"; +import { useRouter } from "next/navigation"; +import React, { useCallback, useEffect, useState } from "react"; +import { MdOutlineArchive } from "react-icons/md"; +import { toast } from "react-toastify"; +import { useAppStore } from "store"; +import { isToday, isYesterday, startOfWeek, isWithinInterval } from "date-fns"; +import { ConversationsHistoryLoader } from "components/Skeletons"; +import ConfirmationDialog from "components/ConfirmationDialog"; +import { + ArchiveConversationApi, + GetConversations, +} from "@/services/conversations"; + +interface IProps { + collapsed: boolean; +} + +const ConversationItem = ({ collapsed }: IProps) => { + const setIsSidebarOpen = useAppStore((state) => state.setIsSidebarOpen); + const [page, setPage] = useState(0); + const itemsPerPage = 25; + const [totalPages, setTotalPages] = useState(0); + const spaceId = localStorage.getItem("spaceId"); + const [isLoading, setIsLoading] = useState(false); + const [firstRender, setFirstRender] = useState(true); + const [newConversations, setNewConversations] = useState([]); + const darkMode = useAppStore((state) => state.darkMode); + const { state, dispatch } = useConversationsContext(); + const router = useRouter(); + const [isModelOpen, setIsModelOpen] = useState(false); + const [deleteLoader, setDeleteLoader] = useState(false); + const [currentConversation, setCurrentConversation] = useState({ + conversationId: "", + conversationDate: "", + }); + + const getAllConversations = useCallback(async () => { + try { + setIsLoading(true); + const response = await GetConversations(page, itemsPerPage); + setTotalPages(Math.ceil(response?.data?.data?.count / itemsPerPage)); + setIsLoading(false); + return response?.data?.data?.conversations; + } catch (error) { + toast.error("Something went wrong fetching conversations history!"); + console.log(JSON.stringify(error)); + setIsLoading(false); + } + }, [totalPages, page]); + + useEffect(() => { + if (spaceId) { + getAllConversations() + .then((newConversations) => { + setNewConversations(newConversations); + setFirstRender(false); + }) + .catch((error) => { + console.log(JSON.stringify(error)); + }); + } + }, [page]); + + useEffect(() => { + const updatedConversations = [...state.conversations]; + newConversations?.forEach((conversation) => { + const date = new Date(conversation.created_at); + const formattedDate = formatDate(date); + const existingGroupIndex = updatedConversations.findIndex( + (group) => group.date === formattedDate + ); + + // Check if conversation already exists in updatedConversations + const conversationExists = updatedConversations.some( + (group) => + group.date === formattedDate && + group.conversations.some((conv) => conv.id === conversation.id) + ); + + if (existingGroupIndex === -1 && !conversationExists) { + updatedConversations.push({ + date: formattedDate, + conversations: [conversation], + }); + } else if (!conversationExists) { + updatedConversations[existingGroupIndex].conversations.push( + conversation + ); + } + }); + dispatch({ + type: FETCH_CONVERSATION, + payload: updatedConversations, + }); + }, [newConversations]); + + const formatDate = (date) => { + if (isToday(date)) { + return "Today"; + } else if (isYesterday(date)) { + return "Yesterday"; + } else { + const startOfLastWeek = startOfWeek(new Date(), { weekStartsOn: 1 }); + if (isWithinInterval(date, { start: startOfLastWeek, end: new Date() })) { + return "Last Week"; + } else { + return "Older"; + } + } + }; + + const handleScroll = (e) => { + const bottom = + e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 1; + if (bottom && !isLoading && page < totalPages) { + setPage((prevPage) => prevPage + 1); + } + }; + + const confirmDelete = async () => { + setDeleteLoader(true); + await ArchiveConversationApi(currentConversation.conversationId) + .then((response) => { + toast.success(response?.data?.message); + setIsModelOpen(false); + handleOptimisticUpdate(); + }) + .catch((error) => { + toast.error( + error?.response?.data?.message + ? error?.response?.data?.message + : error.message + ); + }) + .finally(() => setDeleteLoader(false)); + }; + + const handleOptimisticUpdate = () => { + const { conversationDate, conversationId } = currentConversation; + const updatedConversations = state.conversations.map((conv) => { + if (conv.date === conversationDate) { + return { + ...conv, + conversations: conv.conversations.filter( + (c) => c.id !== conversationId + ), + }; + } + return conv; + }); + + dispatch({ + type: FETCH_CONVERSATION, + payload: updatedConversations, + }); + }; + const closeSidebar = () => { + setIsSidebarOpen(false); + }; + return ( + <> + {firstRender && isLoading ? ( + + ) : ( +
+ {state?.conversations?.map(({ date, conversations }) => ( +
+
+ {date} +
+ {conversations + ?.filter((conv) => conv.messages.length > 0) + .map((conversation) => ( +
+
{ + router.push( + `/admin/conversations/${conversation?.id}/messages` + ); + closeSidebar(); + }} + > + {conversation?.messages?.[0]?.query} +
{ + e.stopPropagation(); + setIsModelOpen(true); + setCurrentConversation({ + conversationDate: date, + conversationId: conversation.id, + }); + }} + className="absolute flex justify-end pr-1 dark:bg-[#111111f2] bg-[#ededede6] w-10 right-0 top-1/2 transform -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-300" + > + +
+
+
+ ))} +
+ ))} + {isLoading && } + + {isModelOpen && ( + { + setIsModelOpen(false); + }} + onSubmit={confirmDelete} + isLoading={deleteLoader} + /> + )} +
+ )} + + ); +}; + +export default ConversationItem; diff --git a/client/components/LayoutComponents/LeftBar.tsx b/client/components/LayoutComponents/LeftBar.tsx new file mode 100644 index 000000000..e65a22b08 --- /dev/null +++ b/client/components/LayoutComponents/LeftBar.tsx @@ -0,0 +1,185 @@ +"use client"; +import LogoDark from "components/Icons/LogoDark"; +import React, { useState } from "react"; +import ToggleDarkModeComponent from "components/ToggleDarkMode"; +import StartChatIcon from "components/Icons/StartChatIcon"; +import { useRouter } from "next/navigation"; +import { useAppStore } from "store"; +import { ROUTE_CHAT_SCREEN } from "utils/constants"; +import ConversationItem from "./ConversationItem"; +import Link from "next/link"; + +const StartChatButton = () => { + const setIsNewChatClicked = useAppStore((state) => state.setIsNewChatClicked); + const router = useRouter(); + + return ( +
{ + router.push(ROUTE_CHAT_SCREEN); + setIsNewChatClicked(true); + }} + > + +
New chat
+
+ ); +}; + +interface IProps { + isMobileView?: boolean; +} + +const LeftBar = ({ isMobileView = false }: IProps) => { + const setIsSidebarOpen = useAppStore((state) => state.setIsSidebarOpen); + const isSidebarOpen = useAppStore((state) => state.isSideBarOpen); + const router = useRouter(); + const [collapsed, setCollapsed] = useState(false); + const darkMode = useAppStore((state) => state.darkMode); + + const closeSidebar = () => { + setIsSidebarOpen(false); + }; + + return ( + <> + {!isMobileView ? ( +
+ + {/* Conversation */} + +
+
+
+
+

+ Chats +

+ +
+ +
+
+ + 🚀 Public Roadmap + + + 💬 Need help? Talk to us + +
+
+
+
+ ) : ( +
+
{ + router.push("/admin"); + closeSidebar(); + }} + > + +
+
+
{ + router.push(ROUTE_CHAT_SCREEN); + closeSidebar(); + }} + className="cursor-pointer" + > +
+
+ +
+
Start New Chat
+
+
+
+ {/* Conversation */} + + {isSidebarOpen && } + + {/* Theme Toggle */} +
+ +
+
+ )} + + ); +}; + +export default LeftBar; diff --git a/client/components/LayoutComponents/RightBar.tsx b/client/components/LayoutComponents/RightBar.tsx new file mode 100644 index 000000000..cbd125c53 --- /dev/null +++ b/client/components/LayoutComponents/RightBar.tsx @@ -0,0 +1,76 @@ +"use client"; +import { useGetSpaceUsers } from "hooks/useSpaces"; +import React, { useState } from "react"; +import { useAppStore } from "store"; +import AddWorkSpace from "components/Icons/AddWorkSpace"; +import { RightSidebarLoader } from "components/Skeletons"; +import AddUserModal from "components/AddUserModal"; +import { AppModal } from "components/AppModal"; + +const RightBar = () => { + const darkMode = useAppStore((state) => state.darkMode); + const spaceId = localStorage.getItem("spaceId"); + const isAdmin = localStorage.getItem("is_admin"); + const [isModalOpen, setIsModalOpen] = useState(false); + + const { data: usersResponse, isLoading: isGetSpaceUserLoading } = + useGetSpaceUsers(spaceId); + + return ( + <> +
+
+
+ {isGetSpaceUserLoading ? ( +
+ +
+ ) : ( +
+

+ Team +

+
+ {usersResponse?.data?.data?.map((user) => ( +

+ {user?.Users?.first_name} +

+ ))} + {isAdmin && ( +
setIsModalOpen(true)} + className="max-w-[50px] p-2 dark:border-[#262626] border rounded-[10px] flex items-center justify-center mt-2 cursor-pointer dark:bg-[#262626]" + > + +
+ )} +
+
+ )} +
+
+
+ + {isModalOpen && ( + setIsModalOpen(false)} + modalWidth="w-[350px]" + isFooter={false} + > + setIsModalOpen(false)} + spaceUsers={usersResponse?.data?.data} + spaceId={spaceId} + /> + + )} + + ); +}; + +export default RightBar; diff --git a/client/components/LoadChartJs/ChartRenderer.tsx b/client/components/LoadChartJs/ChartRenderer.tsx new file mode 100644 index 000000000..cc9c3c9ea --- /dev/null +++ b/client/components/LoadChartJs/ChartRenderer.tsx @@ -0,0 +1,50 @@ +"use client"; +import React from "react"; +import { Chart } from "react-chartjs-2"; +import { Chart as ChartJS, registerables } from "chart.js"; +import { useAppStore } from "store"; +import { + getChartConfig, + preprocessLineChartData, + preprocessPieChartData, +} from "./lineChartUtils"; +import "chartjs-adapter-date-fns"; + +ChartJS.register(...registerables); + +interface IProps { + chartData: any; +} + +const ChartRenderer = ({ chartData }: IProps) => { + const darkMode = useAppStore((state) => state.darkMode); + const isLineChart = chartData?.type === "line"; + const isPieChart = chartData?.type === "pie"; + const lineChartData = preprocessLineChartData(chartData, darkMode); + const pieChartData = preprocessPieChartData(chartData); + + let chartConfig: any = {}; + + if (isLineChart) { + chartConfig = getChartConfig(lineChartData, darkMode); + } else if (isPieChart) { + chartConfig = getChartConfig(pieChartData, darkMode); + } else { + chartConfig = getChartConfig(chartData, darkMode); + } + + return ( +
+ +
+ ); +}; + +export default ChartRenderer; diff --git a/client/components/LoadChartJs/lineChartUtils.ts b/client/components/LoadChartJs/lineChartUtils.ts new file mode 100644 index 000000000..241428a12 --- /dev/null +++ b/client/components/LoadChartJs/lineChartUtils.ts @@ -0,0 +1,172 @@ +import { ChartConfiguration } from "chart.js"; +import { hexToRgba } from "utils/hexToRgba"; + +export const preprocessLineChartData = (data: any, darkMode: boolean) => { + const datasets = data?.data?.datasets; + const allDates: Set = new Set(); + const allYValues: number[] = []; + + // Collect all dates from all datasets + datasets?.forEach((dataset: any) => { + dataset?.data?.forEach((point: any) => { + allDates.add(point.x); + allYValues.push(point.y); + }); + }); + + // Convert Set to array and sort dates + const sortedDates = Array.from(allDates).sort(); + + // Remove redundant dates + const uniqueDates = sortedDates.filter( + (date, index) => date !== sortedDates[index - 1], + ); + // Ensure first and last dates are retained + // const firstDate = sortedDates[0]; + const lastDate = sortedDates[sortedDates.length - 1]; + + // Calculate the highest and lowest y-values + // const highestValue = Math.max(...allYValues); + // const lowestValue = Math.min(...allYValues); + + // Remove dates from the middle + + const maxPoints = 20; + if (uniqueDates.length > maxPoints) { + const step = Math.ceil(uniqueDates.length / maxPoints); + const trimmedDates = []; + + if (datasets.length > 1) { + for (let i = 1; i < uniqueDates.length - 1; i += step) { + trimmedDates.push(uniqueDates[i]); + } + } else { + for (let i = 0; i < uniqueDates.length; i += step) { + trimmedDates.push(uniqueDates[i]); + } + } + trimmedDates.push(lastDate); + + const updatedDatasets = datasets.map((dataset: any) => ({ + ...dataset, + data: dataset.data.filter((point: any) => trimmedDates.includes(point.x)), + fill: true, + pointRadius: 6, + pointHoverRadius: 8, + backgroundColor: hexToRgba(dataset.borderColor, 0.1), + pointBackgroundColor: darkMode ? "white" : "black", + pointBorderColor: dataset.borderColor, + pointBorderWidth: 2, + label: "" + })); + + return { + ...data, + data: { + ...data.data, + datasets: updatedDatasets, + labels: trimmedDates + }, + // options: { + // ...data.options, + // scales: { + // ...data.options.scales, + // y: { + // ...data.options.scales.y, + // ticks: { + // // stepSize: calculateStepWidth(highestValue, lowestValue), + // // callback: function (tick, index, array) { + // // return index % 3 ? 0 : tick; + // // }, + // }, + // }, + // }, + // }, + }; + } + + return data; +}; + +export const preprocessPieChartData = (data: any) => { + return { + ...data, + options: { + ...data?.options, + scales: { + x: { + display: false, + }, + y: { + display: false, + } + }, + }, + }; +}; + +export const calculateStepWidth = (highestValue: number, lowestValue: number) => { + let stepWidth = 1; + const range = highestValue - lowestValue; + + if (range < 10) { + stepWidth = 1; + } else if (range < 50) { + stepWidth = 5; + } else if (range < 100) { + stepWidth = 10; + } else if (range < 500) { + stepWidth = 50; + } else if (range < 1000) { + stepWidth = 100; + } else { + stepWidth = 100; // Default to 100 if range exceeds defined thresholds + } + + return stepWidth; +}; + +export const getChartConfig = (chartData: any, darkMode: boolean) => { + const defaultGridColor = darkMode ? "#878787" : "rgba(0, 0, 0, 0.1)"; + const chartConfig: ChartConfiguration = { + type: chartData?.type, + data: { + ...chartData?.data, + datasets: [...chartData?.data?.datasets || []], + }, + options: { + ...chartData?.options, + plugins: { + ...chartData?.options?.plugins, + title: { + ...chartData?.options?.plugins?.title, + } + }, + scales: { + ...chartData?.options?.scales, + x: { + ...chartData?.options?.scales?.x, + grid: { + ...chartData?.options?.scales?.x?.grid, + color: chartData?.options?.scales?.x?.grid?.color || defaultGridColor, + display: chartData?.options?.scales?.x?.grid?.display || false, + }, + title: { + ...chartData?.options?.scales?.x?.title, + } + }, + y: { + ...chartData?.options?.scales?.y, + grid: { + ...chartData?.options?.scales?.y?.grid, + color: chartData?.options?.scales?.y?.grid?.color || defaultGridColor, + }, + title: { + ...chartData?.options?.scales?.y?.title, + } + }, + }, + }, + }; + return chartConfig +} \ No newline at end of file diff --git a/client/components/Navbar/index.tsx b/client/components/Navbar/index.tsx new file mode 100644 index 000000000..ce9400080 --- /dev/null +++ b/client/components/Navbar/index.tsx @@ -0,0 +1,31 @@ +"use client"; +import React from "react"; +import "react-modern-drawer/dist/index.css"; +import LogoDark from "components/Icons/LogoDark"; +import SettingsMenu from "components/SettingsMenu"; +import Link from "next/link"; +import { BsList } from "react-icons/bs"; +import { useAppStore } from "store"; + +const Navbar = () => { + const setIsSidebarOpen = useAppStore((state) => state.setIsSidebarOpen); + const isSideBarOpen = useAppStore((state) => state.isSideBarOpen); + + return ( + + ); +}; + +export default Navbar; diff --git a/client/components/SettingsMenu/index.tsx b/client/components/SettingsMenu/index.tsx new file mode 100644 index 000000000..d262c4bc6 --- /dev/null +++ b/client/components/SettingsMenu/index.tsx @@ -0,0 +1,65 @@ +import React from "react"; +import Image from "next/image"; +import Dropdown from "../../components/dropdown"; +import { useRouter } from "next/navigation"; +import { useQueryClient } from "@tanstack/react-query"; +import ToggleDarkModeComponent from "components/ToggleDarkMode"; + +const SettingsMenu = () => { + const router = useRouter(); + const queryClient = useQueryClient(); + + const handleLogOut = () => { + const mode = localStorage.getItem("mode"); + localStorage.clear(); + localStorage.setItem("mode", mode); + queryClient.removeQueries(); + router.push("/auth/sign-in"); + }; + return ( +
+
+ null} + width="40" + height="40" + className="rounded-full cursor-pointer" + src="https://www.shutterstock.com/image-vector/user-profile-icon-vector-avatar-600nw-2247726673.jpg" + alt="Gabriele Venturi" + /> + } + classNames={"py-2 top-8 -left-[180px] w-max"} + > +
+
+
+

+ 👋 Welcome back! +

+
+
+
+ + + +
+ +
+ +
+
+ +
+ ); +}; + +export default SettingsMenu; diff --git a/client/components/Skeletons/index.tsx b/client/components/Skeletons/index.tsx new file mode 100644 index 000000000..091d6b2b7 --- /dev/null +++ b/client/components/Skeletons/index.tsx @@ -0,0 +1,136 @@ +import React from "react"; +import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; +import "react-loading-skeleton/dist/skeleton.css"; +import { useAppStore } from "store"; + +export const WorkSpaceLoader = () => { + const darkMode = useAppStore((state) => state.darkMode); + return ( + + + + ); +}; + +export const ConversationsHistoryLoader = ({ + amount = 3, +}: { + amount?: number; +}) => { + const darkMode = useAppStore((state) => state.darkMode); + return ( +
+ + + + {Array.from({ length: amount - 1 }, (_, index) => ( + + + + + ))} + +
+ ); +}; + +export const RightSidebarLoader = () => { + const darkMode = useAppStore((state) => state.darkMode); + return ( +
+ + + + +
+ ); +}; + +export const GetChatLabelLoader = () => { + const darkMode = useAppStore((state) => state.darkMode); + return ( + + + + ); +}; + +export const ChatReactionLoader = () => { + const darkMode = useAppStore((state) => state.darkMode); + return ( + + + + + + ); +}; + +export const SingleChatMessageLoader = () => { + const darkMode = useAppStore((state) => state.darkMode); + return ( + +
+ + + + +
+
+ ); +}; + +export const ChatPlaceholderLoader = () => { + const darkMode = useAppStore((state) => state.darkMode); + return ( + +
+
+ +
+
+ +
+
+ +
+
+
+ ); +}; + +export const ExplainMessageLoader = () => { + const darkMode = useAppStore((state) => state.darkMode); + return ( + + + + + + + ); +}; diff --git a/client/components/ToggleDarkMode/index.tsx b/client/components/ToggleDarkMode/index.tsx new file mode 100644 index 000000000..74bfd15cf --- /dev/null +++ b/client/components/ToggleDarkMode/index.tsx @@ -0,0 +1,52 @@ +import React, { useEffect } from "react"; +import { RiSunFill } from "react-icons/ri"; +import { GoMoon } from "react-icons/go"; +import { useAppStore } from "store"; + +const ToggleDarkModeComponent = () => { + const dm = useAppStore((state) => state.toggleDarkMode); + const darkmode = useAppStore((state) => state.darkMode); + const [checked, setChecked] = React.useState(false); + + useEffect(() => { + const mode = localStorage.getItem("mode"); + const isDarkMode = mode === "dark"; + if (isDarkMode) { + document.body.classList.add("dark"); + } + dm(isDarkMode); + setChecked(isDarkMode); + }, [checked, darkmode]); + + const toggleDarkMode = () => { + if (checked) { + document.body.classList.remove("dark"); + localStorage.removeItem("mode"); + dm(false); + setChecked(!checked); + } else { + document.body.classList.add("dark"); + localStorage.setItem("mode", "dark"); + dm(true); + setChecked(checked); + } + }; + return ( +
+ + +
+ ); +}; + +export default ToggleDarkModeComponent; diff --git a/client/components/VerticalLineSeperator/index.tsx b/client/components/VerticalLineSeperator/index.tsx new file mode 100644 index 000000000..ac510a77d --- /dev/null +++ b/client/components/VerticalLineSeperator/index.tsx @@ -0,0 +1,9 @@ +import React from "react"; + +const VerticalLineSeperator = () => { + return ( +
+ ); +}; + +export default VerticalLineSeperator; diff --git a/client/components/WorkSpace/header.tsx b/client/components/WorkSpace/header.tsx new file mode 100644 index 000000000..dffe9e9ba --- /dev/null +++ b/client/components/WorkSpace/header.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import LogoDark from "components/Icons/LogoDark"; +import SettingsMenu from "components/SettingsMenu"; + +const WorkSpaceHeader = () => { + return ( +
+
+ +
+ +
+ ); +}; + +export default WorkSpaceHeader; diff --git a/client/components/dropdown/index.tsx b/client/components/dropdown/index.tsx new file mode 100644 index 000000000..abbe75a36 --- /dev/null +++ b/client/components/dropdown/index.tsx @@ -0,0 +1,64 @@ +import React from "react"; + +function useOutsideAlerter( + ref: React.RefObject, + setX: React.Dispatch> +): void { + React.useEffect(() => { + /** + * Alert if clicked on outside of element + */ + // function handleClickOutside(event: React.MouseEvent) { + function handleClickOutside(event: MouseEvent) { + if (ref.current && !ref.current.contains(event["target"] as Node)) { + setX(false); + } + } + // Bind the event listener + document.addEventListener("mousedown", handleClickOutside); + return () => { + // Unbind the event listener on clean up + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [ref, setX]); +} + +const Dropdown = (props: { + button: React.ReactNode; + children: React.ReactNode; + classNames: string; + animation?: string; +}) => { + const { button, children, classNames, animation } = props; + const wrapperRef = React.useRef(null); + const [openWrapper, setOpenWrapper] = React.useState(false); + useOutsideAlerter(wrapperRef, setOpenWrapper); + + const closeDropdown = () => { + setOpenWrapper(false); + }; + + return ( +
+
setOpenWrapper(!openWrapper)} + > + {button} +
+
+ {React.Children.map(children, (child: any) => + React.cloneElement(child, { onClick: closeDropdown }) + )} +
+
+ ); +}; + +export default Dropdown; diff --git a/client/components/loader/Loader.tsx b/client/components/loader/Loader.tsx new file mode 100644 index 000000000..775955b66 --- /dev/null +++ b/client/components/loader/Loader.tsx @@ -0,0 +1,26 @@ +import React from "react"; + +interface IProps { + height?: string; + width?: string; +} + +export const Loader = ({ height = "h-96", width = "w-14" }: IProps) => { + return ( +
+ + + + +
+ ); +}; diff --git a/client/components/ui/autocomplete.tsx b/client/components/ui/autocomplete.tsx new file mode 100644 index 000000000..ef64c3728 --- /dev/null +++ b/client/components/ui/autocomplete.tsx @@ -0,0 +1,126 @@ +"use client"; +import React, { useRef, useEffect, useState } from "react"; +import { cn } from "@/lib/utils"; +import ChatSearchIcon from "../Icons/ChatSearchIcon"; + +interface DataType { + id: string | number; + name: string; +} + +export interface AutoCompleteProps + extends React.InputHTMLAttributes { + onItemClick: (item: DataType) => void; + onSubmit: (e) => void; + data: DataType[]; +} + +const AutoComplete = React.forwardRef( + ({ className, onItemClick, onSubmit, data, ...props }, ref) => { + const hoverRef = useRef(null); + const [cursor, setCursor] = useState(0); + const [options, setOptions] = useState([]); + + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if ( + hoverRef.current && + !hoverRef.current.contains(event.target as Node) + ) { + setOptions([]); + setCursor(0); + } + } + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "ArrowUp" && cursor > 0) { + setCursor(cursor - 1); + } else if ( + e.key === "ArrowDown" && + cursor < options.slice(0, 5).length - 1 + ) { + setCursor(cursor + 1); + } else if (e.key === "Enter") { + e.preventDefault(); + onItemClick(options[cursor]); + setOptions([]); + setCursor(0); + } + }; + + const handleChange = (e: React.ChangeEvent) => { + const value = e.target.value.toLowerCase(); + const filteredOptions = data.filter((item) => + item.name.toLowerCase().includes(value) + ); + setOptions(filteredOptions || []); + }; + + return ( +
+
+
+ 0 ? "rounded-t-[18px]" : "rounded-[25px]"} + `, + className + )} + ref={ref} + onKeyDown={handleKeyDown} + onChange={handleChange} + {...props} + /> + + + +
+
+ {options.length > 0 && ( +
+ {options?.slice(0, 5).map((item: DataType, index: number) => ( +
{ + onItemClick(item); + setOptions([]); + setCursor(0); + }} + > +

{item?.name}

+
+ ))} +
+ )} +
+ ); + } +); +AutoComplete.displayName = "Input"; + +export { AutoComplete }; diff --git a/client/components/ui/button.tsx b/client/components/ui/button.tsx new file mode 100644 index 000000000..e451ceceb --- /dev/null +++ b/client/components/ui/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap font-semibold ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-white neon-on-hover text-black border", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90 border border-destructive", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "px-6 py-1 min-w-[100px] rounded-[10px]", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + } +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/client/components/ui/input.tsx b/client/components/ui/input.tsx new file mode 100644 index 000000000..62bc87916 --- /dev/null +++ b/client/components/ui/input.tsx @@ -0,0 +1,25 @@ +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ); + } +); +Input.displayName = "Input"; + +export { Input }; diff --git a/client/components/ui/select.tsx b/client/components/ui/select.tsx new file mode 100644 index 000000000..253283039 --- /dev/null +++ b/client/components/ui/select.tsx @@ -0,0 +1,49 @@ +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +export interface SelectProps + extends React.SelectHTMLAttributes {} + +const Select = React.forwardRef( + ({ className, ...props }, ref) => { + return ( +
+ +
+ ); + } +); +Select.displayName = "Select"; + +export interface OptionProps + extends React.OptionHTMLAttributes {} + +const Option: React.FC = ({ className, children, ...props }) => { + return ( + + ); +}; + +Option.displayName = "Option"; + +export { Select, Option }; diff --git a/client/contexts/ContextProvider.tsx b/client/contexts/ContextProvider.tsx new file mode 100644 index 000000000..6459e9e59 --- /dev/null +++ b/client/contexts/ContextProvider.tsx @@ -0,0 +1,9 @@ +"use client"; +import React from "react"; +import { ConversationsProvider } from "./ConversationsProvider"; + +const ContextProvider = ({ children }: { children: React.ReactNode }) => { + return {children}; +}; + +export default ContextProvider; diff --git a/client/contexts/ConversationsProvider.tsx b/client/contexts/ConversationsProvider.tsx new file mode 100644 index 000000000..d887ffa28 --- /dev/null +++ b/client/contexts/ConversationsProvider.tsx @@ -0,0 +1,40 @@ +import React, { createContext, useContext, useReducer } from "react"; + +const initialState = { + conversations: [], +}; + +export const FETCH_CONVERSATION = "FETCH_CONVERSATION"; +export const UPDATE_CONVERSATION = "UPDATE_CONVERSATION"; + +const conversationsReducer = (state, action) => { + switch (action.type) { + case FETCH_CONVERSATION: + return { + conversations: action.payload, + }; + case UPDATE_CONVERSATION: + return { + conversations: action.payload, + }; + default: + return state; + } +}; + +const ConversationsContext = createContext(null); + +export const ConversationsProvider = ({ + children, +}: { + children: React.ReactNode; +}) => { + const [state, dispatch] = useReducer(conversationsReducer, initialState); + + return ( + + {children} + + ); +}; +export const useConversationsContext = () => useContext(ConversationsContext); diff --git a/client/hooks/useConversations.tsx b/client/hooks/useConversations.tsx new file mode 100644 index 000000000..44c18016e --- /dev/null +++ b/client/hooks/useConversations.tsx @@ -0,0 +1,20 @@ +import { useQuery } from "@tanstack/react-query"; +import { ExplainConversationMessage, GetChatLabel } from "@/services/chat"; + +export const useGetChatLabel = (chatId: string) => { + const { data, isLoading, error, isError } = useQuery({ + queryKey: ["useGetChatLabel", chatId], + queryFn: () => GetChatLabel(chatId), + }); + return { data, isLoading, error, isError }; +}; +export const useExplainConversationMessage = ( + conversationId: string, + chatId: string +) => { + const { data, isLoading, error, isError } = useQuery({ + queryKey: ["useExplainConversationMessage", conversationId, chatId], + queryFn: () => ExplainConversationMessage(conversationId, chatId), + }); + return { data, isLoading, error, isError }; +}; diff --git a/client/hooks/useOrganizations.tsx b/client/hooks/useOrganizations.tsx new file mode 100644 index 000000000..5c4209568 --- /dev/null +++ b/client/hooks/useOrganizations.tsx @@ -0,0 +1,50 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { + AddOrganizationsSettings, + GetMembersList, + GetOrganizations, + GetOrganizationsDetail, + GetOrganizationsSettings, +} from "@/services/organization"; + +export const useGetOrganizations = () => { + const { data, isLoading, error, isError } = useQuery({ + queryKey: ["useGetOrganizations"], + queryFn: GetOrganizations, + }); + return { data, isLoading, error, isError }; +}; +export const useGetOrganizationsDetail = (orgId: string) => { + const { data, isLoading, error, isError } = useQuery({ + queryKey: ["useGetOrganizationsDetail", orgId], + queryFn: () => GetOrganizationsDetail(orgId), + }); + return { data, isLoading, error, isError }; +}; +export const useGetMembersList = (orgId: string) => { + const { data, isLoading, error, isError } = useQuery({ + queryKey: ["useGetMembersList", orgId], + queryFn: () => GetMembersList(orgId), + enabled: !!orgId, + }); + return { data, isLoading, error, isError }; +}; +export const useGetOrganizationsSettings = () => { + const { data, isLoading, error, isError } = useQuery({ + queryKey: ["useGetOrganizationsSettings"], + queryFn: () => GetOrganizationsSettings(), + }); + return { data, isLoading, error, isError }; +}; +export const useAddOrganizationsSettings = () => { + const queryClient = useQueryClient(); + const { data, isPending, error, isError, mutateAsync } = useMutation({ + mutationFn: (body: any) => AddOrganizationsSettings(body), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["useGetOrganizationsSettings", "useGetOrganizationsDetail"], + }); + }, + }); + return { data, isPending, error, isError, mutateAsync }; +}; diff --git a/client/hooks/useSpaces.tsx b/client/hooks/useSpaces.tsx new file mode 100644 index 000000000..42bf47db2 --- /dev/null +++ b/client/hooks/useSpaces.tsx @@ -0,0 +1,47 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { + AddNewUserSpace, + DeleteSpaceUser, + GetAllSpacesList, + GetSpaceUser, +} from "@/services/spaces"; + +export const useGetAllSpaces = () => { + const { data, isLoading, error, isError } = useQuery({ + queryKey: ["useGetAllSpaces"], + queryFn: GetAllSpacesList, + }); + return { data, isLoading, error, isError }; +}; + +export const useGetSpaceUsers = (spaceId: string) => { + const { data, isLoading, error, isError } = useQuery({ + queryKey: ["useGetSpaceUsers", spaceId], + queryFn: () => GetSpaceUser(spaceId), + enabled: !!spaceId, + }); + return { data, isLoading, error, isError }; +}; + +export const useDeleteSpaceUsers = () => { + const queryClient = useQueryClient(); + const { data, isPending, error, isError, mutateAsync } = useMutation({ + mutationFn: (params: any) => + DeleteSpaceUser(params.spaceId, params.payload), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["useGetSpaceUsers"] }); + }, + }); + return { data, isPending, error, isError, mutateAsync }; +}; +export const useAddSpaceUsers = () => { + const queryClient = useQueryClient(); + const { data, isPending, error, isError, mutateAsync } = useMutation({ + mutationFn: (params: any) => + AddNewUserSpace(params.spaceId, params.payload), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["useGetSpaceUsers"] }); + }, + }); + return { data, isPending, error, isError, mutateAsync }; +}; diff --git a/client/hooks/useUsers.tsx b/client/hooks/useUsers.tsx new file mode 100644 index 000000000..351211bc7 --- /dev/null +++ b/client/hooks/useUsers.tsx @@ -0,0 +1,10 @@ +import { useQuery } from "@tanstack/react-query"; +import { getMe } from "@/services/users"; + +export const useGetMe = () => { + const { data, isLoading, error, isError } = useQuery({ + queryKey: ["useGetMe"], + queryFn: getMe, + }); + return { data, isLoading, error, isError }; +}; diff --git a/client/hooks/useWindowWidth.tsx b/client/hooks/useWindowWidth.tsx new file mode 100644 index 000000000..e7446c00a --- /dev/null +++ b/client/hooks/useWindowWidth.tsx @@ -0,0 +1,20 @@ +import { useState, useEffect } from "react"; + +function useWindowWidth() { + const [width, setWidth] = useState(window.innerWidth); + + function handleWindowSizeChange() { + setWidth(window.innerWidth); + } + + useEffect(() => { + window.addEventListener("resize", handleWindowSizeChange); + return () => { + window.removeEventListener("resize", handleWindowSizeChange); + }; + }, []); + + return width; +} + +export default useWindowWidth; diff --git a/client/lib/utils.ts b/client/lib/utils.ts new file mode 100644 index 000000000..d084ccade --- /dev/null +++ b/client/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/client/next.config.mjs b/client/next.config.mjs new file mode 100644 index 000000000..3e7fc91c1 --- /dev/null +++ b/client/next.config.mjs @@ -0,0 +1,22 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: false, + swcMinify: true, + basePath: process.env.NEXT_PUBLIC_BASE_PATH, + assetPrefix: process.env.NEXT_PUBLIC_BASE_PATH, + distDir: "build", + images: { + domains: [ + "images.unsplash.com", + "i.ibb.co", + "scontent.fotp8-1.fna.fbcdn.net", + ], + // Make ENV + unoptimized: true, + }, + eslint: { + ignoreDuringBuilds: true, + }, +}; + +export default nextConfig; diff --git a/client/package.json b/client/package.json new file mode 100644 index 000000000..cf8cb601a --- /dev/null +++ b/client/package.json @@ -0,0 +1,54 @@ +{ + "name": "client", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@next/third-parties": "^14.2.3", + "@radix-ui/react-slot": "^1.0.2", + "@rollbar/react": "^0.12.0-beta", + "@table-library/react-table-library": "^4.1.7", + "@tanstack/react-query": "^5.39.0", + "axios": "^1.7.2", + "chart.js": "^4.4.3", + "chartjs-adapter-date-fns": "^3.0.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "date-fns": "^3.6.0", + "mixpanel-browser": "^2.50.0", + "multiselect-react-dropdown": "^2.0.25", + "next": "14.2.3", + "react": "^18", + "react-ace": "^11.0.1", + "react-chartjs-2": "^5.2.0", + "react-dom": "^18", + "react-icons": "^5.2.1", + "react-loading-skeleton": "^3.4.0", + "react-modern-drawer": "^1.3.1", + "react-toastify": "^10.0.5", + "react-tooltip": "^5.26.4", + "rollbar": "^2.26.4", + "tailwind-merge": "^2.3.0", + "tailwindcss-animate": "^1.0.7", + "zustand": "^4.5.2" + }, + "devDependencies": { + "@tanstack/react-query-devtools": "^5.39.0", + "@types/mixpanel-browser": "^2.49.0", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "@typescript-eslint/eslint-plugin": "^7.11.0", + "@typescript-eslint/parser": "^7.11.0", + "eslint": "^8", + "eslint-config-next": "14.2.3", + "postcss": "^8", + "tailwindcss": "^3.4.1", + "typescript": "^5" + } +} diff --git a/client/postcss.config.mjs b/client/postcss.config.mjs new file mode 100644 index 000000000..1a69fd2a4 --- /dev/null +++ b/client/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + }, +}; + +export default config; diff --git a/client/public/favicon.ico b/client/public/favicon.ico new file mode 100644 index 000000000..170c9a7a7 Binary files /dev/null and b/client/public/favicon.ico differ diff --git a/client/public/img/logo/logo.png b/client/public/img/logo/logo.png new file mode 100644 index 000000000..622fa9990 Binary files /dev/null and b/client/public/img/logo/logo.png differ diff --git a/client/public/next.svg b/client/public/next.svg new file mode 100644 index 000000000..5174b28c5 --- /dev/null +++ b/client/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/public/robots.txt b/client/public/robots.txt new file mode 100644 index 000000000..e69de29bb diff --git a/client/public/svg/drop-down.svg b/client/public/svg/drop-down.svg new file mode 100644 index 000000000..fa7c5a1b3 --- /dev/null +++ b/client/public/svg/drop-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/public/svg/logs.svg b/client/public/svg/logs.svg new file mode 100644 index 000000000..12ea965a8 --- /dev/null +++ b/client/public/svg/logs.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/public/svg/panda.svg b/client/public/svg/panda.svg new file mode 100644 index 000000000..ec499ec7a --- /dev/null +++ b/client/public/svg/panda.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/client/public/vercel.svg b/client/public/vercel.svg new file mode 100644 index 000000000..d2f842227 --- /dev/null +++ b/client/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/services/chat.ts b/client/services/chat.ts new file mode 100644 index 000000000..a31d9c048 --- /dev/null +++ b/client/services/chat.ts @@ -0,0 +1,47 @@ +import { DeleteRequest, GetRequest, PatchRequest, PostRequest } from "@/utils/apiUtils"; + +const chatApiUrl = `/v1/chat`; + + +export const ChatApi = async (chatdata) => { + try { + const response = await PostRequest(`${chatApiUrl}/`, chatdata); + return response; + } catch (error) { + console.error("Get request failed", error); + throw error; + } +}; + +export const FetchDataframe = async (id: number | string) => { + const apiUrl = `${chatApiUrl}/conversation-message-dataframe/${id}`; + try { + const response = await GetRequest(apiUrl); + return response; + } catch (error) { + console.error("Get request failed", error); + throw error; + } +}; + +export const GetChatLabel = async (chatId: string) => { + const apiUrl = `${chatApiUrl}/conversation-message/${chatId}/result-label`; + try { + const response = await GetRequest(apiUrl); + return response; + } catch (error) { + console.error("Get request failed", error); + throw error; + } +}; + +export const ExplainConversationMessage = async (conversationId: string, messageId: string) => { + const apiUrl = `${chatApiUrl}/conversation/${conversationId}/conversation-message/${messageId}/explain`; + try { + const response = await GetRequest(apiUrl); + return response; + } catch (error) { + console.error("Get request failed", error); + throw error; + } +}; \ No newline at end of file diff --git a/client/services/conversations.ts b/client/services/conversations.ts new file mode 100644 index 000000000..33b1dd34f --- /dev/null +++ b/client/services/conversations.ts @@ -0,0 +1,44 @@ +import { DeleteRequest, GetRequest } from "@/utils/apiUtils"; + +const apiUrl = `/v1/conversations`; + + +export const GetConversations = async (skip: number, limit: number) => { + try { + const response = await GetRequest(`${apiUrl}/?skip=${skip}&limit=${limit}`); + return response; + } catch (error) { + console.error("Get request failed", error); + throw error; + } +}; + +export const ConversationMessages = async ( + conversationId: string, + skip?: number, + limit?: number, +) => { + let url = '' + if (skip && limit) { + url = `${apiUrl}/${conversationId}/messages?skip=${skip}&limit=${limit}` + } else { + url = `${apiUrl}/${conversationId}/messages` + } + try { + const response = await GetRequest(url); + return response; + } catch (error) { + console.error('Get request failed', error); + throw error; + } +}; + +export const ArchiveConversationApi = async (conversationId) => { + try { + const response = await DeleteRequest(`${apiUrl}/${conversationId}`); + return response; + } catch (error) { + console.error('Get request failed', error); + throw error; + } +}; \ No newline at end of file diff --git a/client/services/organization.ts b/client/services/organization.ts new file mode 100644 index 000000000..d164e550c --- /dev/null +++ b/client/services/organization.ts @@ -0,0 +1,72 @@ +import { GetRequest, PostRequest } from "@/utils/apiUtils"; +import { BASE_API_URL } from "utils/constants"; +interface DataFrameData { + name: string; + url: string; +} + +const orgApiUrl = `${BASE_API_URL}/api/organization`; + +export const AddOrganization = async (dataToAdd: DataFrameData) => { + const addUrl = `${orgApiUrl}`; + try { + const result = await PostRequest(addUrl, dataToAdd); + return result; + } catch (error) { + console.error("Add request failed", error); + throw error; + } +}; + +export const GetOrganizations = async () => { + const apiUrl = `${orgApiUrl}`; + try { + const response = await GetRequest(apiUrl); + return response; + } catch (error) { + console.error("Get request failed", error); + throw error; + } +}; +export const GetMembersList = async (id: string) => { + const apiUrl = `${orgApiUrl}/${id}/members`; + try { + const data = await GetRequest(apiUrl); + return data; + } catch (error) { + console.error("Delete request failed", error); + throw error; + } +}; + +export const GetOrganizationsDetail = async (id: string) => { + const apiUrl = `${orgApiUrl}/${id}`; + try { + const data = await GetRequest(apiUrl); + return data; + } catch (error) { + console.error("Get request failed", error); + throw error; + } +}; + +export const GetOrganizationsSettings = async () => { + const apiUrl = `${orgApiUrl}/settings/fetch`; + try { + const data = await GetRequest(apiUrl); + return data; + } catch (error) { + console.error("Get request failed", error); + throw error; + } +}; +export const AddOrganizationsSettings = async (body) => { + const apiUrl = `${orgApiUrl}/settings`; + try { + const data = await PostRequest(apiUrl, body); + return data; + } catch (error) { + console.error("Get request failed", error); + throw error; + } +}; diff --git a/client/services/spaces.ts b/client/services/spaces.ts new file mode 100644 index 000000000..1ef76fc21 --- /dev/null +++ b/client/services/spaces.ts @@ -0,0 +1,40 @@ +import { DeleteRequestWithBody, GetRequest, PostRequest } from "@/utils/apiUtils"; +import { BASE_API_URL } from "utils/constants"; + +const spaceApiUrl = `${BASE_API_URL}/api/spaces`; + +type userSpace = { + new_member_email: string; +}; + +export const GetAllSpacesList = async () => { + const apiUrl = `${spaceApiUrl}/list`; + const data = await GetRequest(apiUrl); + return data; +}; + +export const GetSpaceUser = async (id: string) => { + const apiUrl = `${spaceApiUrl}/${id}/user`; + const data = await GetRequest(apiUrl); + return data; +}; +export const AddNewUserSpace = async (id: string, dataToAdd: userSpace) => { + const addUrl = `${spaceApiUrl}/${id}/user`; + try { + const result = await PostRequest(addUrl, dataToAdd); + return result; + } catch (error) { + console.error("Add request failed", error); + } +}; + +export const DeleteSpaceUser = async (id: string, user_id) => { + const apiUrl = `${spaceApiUrl}/${id}/user`; + try { + const data = await DeleteRequestWithBody(apiUrl, user_id); + return data; + } catch (error) { + console.error("Delete request failed", error); + throw error; + } +}; \ No newline at end of file diff --git a/client/services/users.ts b/client/services/users.ts new file mode 100644 index 000000000..e09fc320d --- /dev/null +++ b/client/services/users.ts @@ -0,0 +1,13 @@ +import { GetRequest } from "@/utils/apiUtils"; + +const apiUrl = `/v1/users`; + +export const getMe = async () => { + try { + const response = await GetRequest(`${apiUrl}/me`); + return response; + } catch (error) { + console.error("Get request failed", error); + throw error; + } +}; \ No newline at end of file diff --git a/client/store/index.ts b/client/store/index.ts new file mode 100644 index 000000000..4f1474e15 --- /dev/null +++ b/client/store/index.ts @@ -0,0 +1,59 @@ +import { ChatMessageData } from '@/types/chat-types'; +import { create } from 'zustand' + +type StoreType = { + darkMode: boolean; + toggleDarkMode: (e) => void; + setSelectedChatItem?: (feedItem: ChatMessageData) => void; + selectedChatItem?: ChatMessageData, + isExplainViewOpen?: boolean; + isChartEditOpen?: boolean; + setIsExplainViewOpen?: (e: boolean) => void, + setIsChartEditOpen?: (e: boolean) => void, + isSideBarOpen?: boolean; + setIsSidebarOpen?: (e: boolean) => void; + isNewChatClicked?: boolean; + setIsNewChatClicked?: (e: boolean) => void; + isRightSidebarOpen?: boolean; + setIsRightSidebarOpen?: (e: boolean) => void; + isFeedFilterOpen?: boolean + setIsFeedFilterOpen?: (e: boolean) => void; +} + +export const useAppStore = create((set) => ({ + darkMode: false, + selectedFeedItem: {}, + selectedChatItem: {}, + isExplainViewOpen: false, + isChartEditOpen: false, + selectedChartItem: {}, + isSideBarOpen: false, + isNewChatClicked: false, + isRightSidebarOpen: false, + isFeedFilterOpen: false, + + toggleDarkMode: (mode) => { + set(() => ({ darkMode: mode })) + }, + setSelectedChatItem: (chatItem: ChatMessageData) => { + set(() => ({ selectedChatItem: chatItem })); + }, + setIsExplainViewOpen: (option: boolean) => { + set(() => ({ isExplainViewOpen: option })); + }, + setIsChartEditOpen: (option: boolean) => { + set(() => ({ isChartEditOpen: option })); + }, + setIsSidebarOpen: (option: boolean) => { + set(() => ({ isSideBarOpen: option })); + }, + setIsNewChatClicked: (option: boolean) => { + set(() => ({ isNewChatClicked: option })); + }, + setIsRightSidebarOpen: (option: boolean) => { + set(() => ({ isRightSidebarOpen: option })); + }, + setIsFeedFilterOpen: (option: boolean) => { + set(() => ({ isFeedFilterOpen: option })); + }, +})) \ No newline at end of file diff --git a/client/styles/App.css b/client/styles/App.css new file mode 100644 index 000000000..eff1c4f3c --- /dev/null +++ b/client/styles/App.css @@ -0,0 +1,85 @@ +@import url("https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;500;600;700;800;900&display=swap"); +body { + font-family: "DM Sans", sans-serif; +} + +option { + color: black; +} + +.font-Montserrat { + font-family: "Montserrat", sans-serif; +} + +/* width */ +.custom-scroll::-webkit-scrollbar { + width: 5px; + height: 5px; +} + +/* Track */ +.custom-scroll::-webkit-scrollbar-track { + background: transparent; + border-radius: 15px; +} + +/* Handle */ +.custom-scroll::-webkit-scrollbar-thumb { + background: #888; + border-radius: 15px; +} + +/* Handle on hover */ +.custom-scroll::-webkit-scrollbar-thumb:hover { + background: #555; +} +body { + height: 100vh; + overflow-y: scroll; +} +body::-webkit-scrollbar { + width: 8px; + height: 8px; + background-color: #f4f7fe; +} +body::-webkit-scrollbar-track, +::-webkit-scrollbar-thumb { + border-radius: 16px; + border: solid 5px transparent; +} +body::-webkit-scrollbar-track { + /* box-shadow: inset 0 0 5px 5px rgba(0, 0, 0, 0.075); */ + background-color: #f4f7fe; +} +body::-webkit-scrollbar-thumb { + background-color: #aaa; +} +body.dark::-webkit-scrollbar { + width: 8px; + height: 8px; + background-color: #000; +} +body.dark::-webkit-scrollbar-track, +body.dark::-webkit-scrollbar-thumb { + border-radius: 16px; + border: solid 5px transparent; +} +body.dark::-webkit-scrollbar-track { + /* box-shadow: inset 0 0 5px 5px rgba(0, 0, 0, 0.075); */ + background-color: #000; +} +body.dark::-webkit-scrollbar-thumb { + background-color: #191919; +} +code > div { + max-width: calc(100vw - 50px); +} +@media only screen and (max-width: 1024px) { + .singleSlick .slick-track > div:last-child { + display: none; + } + .singleSlick .slick-track > div:first-child { + display: block; + } +} diff --git a/client/styles/globals.css b/client/styles/globals.css new file mode 100644 index 000000000..af0c498dd --- /dev/null +++ b/client/styles/globals.css @@ -0,0 +1,773 @@ +@tailwind base; + @tailwind components; + @tailwind utilities; + + @layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + + --radius: 0.5rem; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + } + } + + @layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } + } + +input.defaultCheckbox { + color: white; +} + +input.defaultCheckbox::before { + content: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.66662 10.115L12.7946 3.98633L13.7379 4.92899L6.66662 12.0003L2.42395 7.75766L3.36662 6.81499L6.66662 10.115Z' fill='white'/%3E%3C/svg%3E%0A"); + fill: currentColor; + opacity: 0; + height: 16px; + width: 16px; + top: 0; + position: absolute; + left: 50%; + transform: translate(-50%, 0px); +} + +input.defaultCheckbox::before path { + fill: currentColor; +} + +input:checked.defaultCheckbox::before { + opacity: 1; +} + +.accordion-table { + box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px; +} + +.copy-btn-logs svg { + height: 1.5em; + width: 1.5em; +} +.copied-text { + position: absolute; + top: 30px; + right: 82px; + color: white; +} + +.code-syntax-highlight { + padding: 0 !important; +} +.code-syntax-highlight .SyntaxHighlighter { + padding: 0 5px; +} +.code-syntax-highlight .language-python { + white-space: pre-wrap !important; +} +.test-detail-compo th, +.test-detail-compo td { + padding-left: 15px; +} + +.optionListContainer li { + color: #000 !important; +} +.optionListContainer li:hover { + color: #fff !important; + background-color: #191919 !important; +} +.optionListContainer li.highlightOption { + color: #fff !important; + background-color: #191919 !important; +} +.generate-table-wrapper .w-max { + padding: 31px !important; +} +.add-col-btn { + bottom: -36px; +} +.active_page { + background-color: #191919 !important; + color: white !important; +} +.testingLoader svg { + height: auto; + margin: 0; +} +.selectBox .notFound { + display: block !important; +} +.customPagination ul { + background-color: #fff; + border-radius: 8px; +} +.customSelect select { + padding-right: 30px; + position: relative; + background-color: transparent; +} +.customSelect::before { + content: ""; + position: absolute; + width: 10px; + height: 10px; + border: 2px solid #a7a7a7; + border-top: none; + border-left: none; + right: 10px; + top: 13px; + transform: rotate(45deg); + z-index: 0; +} +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} +input[type="number"] { + -moz-appearance: textfield; +} +input[type="color"] { + -webkit-appearance: none; + width: 30px; + height: 30px; + cursor: pointer; +} +input[type="color"]::-webkit-color-swatch-wrapper { + padding: 0; + border: 2px solid #333333; + border-radius: 50%; +} +input[type="color"]::-webkit-color-swatch { + border-radius: 50%; +} +.role { + padding: 11px 5px; + background-color: white; + border-radius: 7px; + outline: none; + cursor: pointer; + width: 100%; +} + +.translateX { + transform: translateX(-100%); + transition: transform 0.5s ease-in-out; +} +.cards_spaces { + height: 80px; + display: flex; + justify-content: center; + align-items: center; +} +/* .mainLayout { + height: calc(100vh - 210px); +} */ +.cards_wrapper div { + display: flex; + justify-content: center; + align-items: center; +} +/* file upload design */ +.et_pb_contact_form_label { + display: block; + color: black; + font-weight: bold; + letter-spacing: 1.2px; + font-size: 18px; + padding-bottom: 5px; +} +input[id="et_pb_contact_brand_file_request_0"] { + display: none; +} +label[for="et_pb_contact_brand_file_request_0"] { + background: #fff; + height: 145px; + background-image: url("https://www.svgrepo.com/show/9488/cloud-upload-signal.svg"); + background-repeat: no-repeat; + background-position: top 18px center; + /* position: absolute; */ + background-size: 7%; + color: transparent; + margin: auto; + width: 450px; + top: 50%; + left: 0; + right: 0; + transform: translateY(-50%); + border: 1px solid #a2a1a7; + box-sizing: border-box; + margin-top: 8rem; +} +label[for="et_pb_contact_brand_file_request_0"]:before { + display: block; + position: absolute; + top: 50%; + transform: translateY(-50%); + font-size: 14px; + color: #202020; + font-weight: 400; + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; + text-align: center; +} +label[for="et_pb_contact_brand_file_request_0"]:after { + display: block; + content: "Browse"; + background: #16a317; + width: 86px; + height: 27px; + line-height: 27px; + position: absolute; + bottom: 19px; + font-size: 14px; + color: white; + font-weight: 500; + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; + text-align: center; +} +label[for="et_pb_contact_brand_request_0"]:after { + content: " (Provide link or Upload files if you already have guidelines)"; + font-size: 12px; + letter-spacing: -0.31px; + color: #7a7a7a; + font-weight: normal; +} +label[for="et_pb_contact_design_request_0"]:after { + content: " (Provide link or Upload design files)"; + font-size: 12px; + letter-spacing: -0.31px; + color: #7a7a7a; + font-weight: normal; +} +label[for="et_pb_contact_brand_file_request_0"].changed, +label[for="et_pb_contact_brand_file_request_0"]:hover { + background-color: #e3f2fd; +} +label[for="et_pb_contact_brand_file_request_0"] { + cursor: pointer; + transition: 400ms ease; +} +.file_names { + display: block; + position: absolute; + color: black; + left: 0; + bottom: -30px; + font-size: 13px; + font-weight: 300; +} +.file_names { + text-align: center; +} + +@keyframes typingAnimation { + 0% { + opacity: 0.5; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0.5; + } +} + +.typing-text { + animation: typingAnimation 0.5s infinite; +} + +.stroke-black path { + stroke: #000; +} +.dark .stroke-black path { + stroke: #fff; +} +.dark .border-white { + border-color: #000; +} +.dark .editBtn { + color: #fff !important; +} +.cardBox::after { + transition: all 0.2s ease; +} +.dark .cardBox { + background: #191919 !important; +} +.selectedCard.cardBox::after { + width: 30px; +} +.cardBox.selectedCard { + border-color: #faa250 !important; + color: #191919 !important; + background: #fff !important; + font-weight: 700 !important; +} + +.dark .optionListContainer { + background: #191919 !important; +} +.dark .optionListContainer li { + color: #fff !important; +} +/*custom scroll bar css */ + +.style-1::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + border-radius: 10px; + background-color: #f5f5f5; +} +.style-1::-webkit-scrollbar { + width: 12px; + height: 6px !important; + background-color: #f5f5f5; +} + +.style-1::-webkit-scrollbar-thumb { + border-radius: 10px; + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + background-color: #555; +} + +/* chat loader css */ + +#bubble { + top: 45vh; + margin-left: auto; + margin-right: auto; + width: 70px; + height: 40px; + border-top-right-radius: 35%; + border-top-left-radius: 35%; + border-bottom-left-radius: 35%; + border-bottom-right-radius: 35%; + background: white; +} +#circleWrapper { + width: 50px; + position: relative; + margin-left: auto; + margin-right: auto; +} +.circle { + position: relative; + height: 10px; + width: 10px; + margin-left: 2px; + margin-right: 2px; + top: 15px; + background: white; + display: inline-block; + border-radius: 50%; +} +.circle { + animation-duration: 2s; + animation-iteration-count: infinite; + animation-timing-function: linear; +} +#circle1 { + animation-name: circle1; + background: #f43c86; +} +#circle2 { + animation-name: circle2; + background: rgba(244, 60, 134, 0.5); +} +#circle3 { + animation-name: circle3; + background: rgba(244, 60, 134, 0.21); +} +@keyframes circle1 { + 0% { + top: 15px; + } + 15% { + top: 5px; + } + 25% { + top: 15px; + } +} +@keyframes circle2 { + 10% { + top: 15px; + } + 25% { + top: 5px; + } + 35% { + top: 15px; + } +} + +@keyframes circle3 { + 15% { + top: 15px; + } + 30% { + top: 5px; + } + 40% { + top: 15px; + } +} + +/* toggle mode css */ + +.checkbox-toggle { + opacity: 0; + position: absolute; +} + +.checkbox-label-toggle { + background-color: transparent; + width: 52px; + height: 26px; + border-radius: 50px; + position: relative; + padding: 5px; + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; + border: 1px solid #aaa; +} +.checkbox-label-toggle svg { + position: relative; + z-index: 1; +} + +.fa-moon { + color: #f1c40f; +} + +.fa-sun { + color: #f39c12; +} + +.checkbox-label-toggle .ball { + background-color: #000; + width: 26px; + height: 26px; + position: absolute; + left: 0; + top: -1px; + border-radius: 50%; + transition: transform 0.2s linear; +} +.dark .checkbox-label-toggle { + border: 1px solid rgba(255, 255, 255, 0.8); +} +.dark .checkbox-label-toggle .ball { + background-color: #faa250; +} + +.checkbox-toggle:checked + .checkbox-label-toggle .ball { + transform: translateX(24px); +} + +.menuIcon svg path { + fill: #191919; +} +.menuIcon.text-white svg path { + fill: #fff; +} +.dark .menuIcon svg path { + fill: #fff; +} +.dark .menuIcon.text-white svg path { + fill: #fff; +} + +svg.iconText path { + fill: #000000; +} +.dark svg.iconText path { + fill: #fff; +} +.SelectLoader svg { + width: auto; + height: auto; + margin: 0; +} +.btnLoading div div svg { + height: 24px; +} + +.slick-next:before { + content: "" !important; +} +.slick-prev:before { + content: "" !important; +} + +.slick-arrow.slick-next.slick-disabled.iconText { + opacity: 0.5 !important; +} + +.oneLine-limit { + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 1; + line-clamp: 1; + -webkit-box-orient: vertical; +} +.slick-prev { + margin-top: -5px; +} +.slick-next { + margin-top: -5px; +} +.slick-prev { + left: 0px !important; +} +.slick-next { + right: 0px !important; +} +.slick-list { + width: calc(100% - 60px); + margin-left: 30px !important; +} +.slick-disabled { + opacity: 0.3; + pointer-events: none; +} +body { + height: 100vh; + overflow-y: scroll; +} +body::-webkit-scrollbar { + width: 8px; + height: 8px; +} +body::-webkit-scrollbar-track, +::-webkit-scrollbar-thumb { + border-radius: 16px; + border: solid 5px transparent; +} + +body::-webkit-scrollbar-thumb { + background-color: #aaa; +} + +.reaction_Loader { + width: 12px; + height: 12px; + display: inline-block; + position: relative; +} +.reaction_Loader::after, +.reaction_Loader::before { + content: ""; + box-sizing: border-box; + width: 12px; + height: 12px; + border-radius: 50%; + border: 5px solid #4da2db; + position: absolute; + left: 0; + top: 0; + animation: animloader 2s linear infinite; +} +.reaction_Loader::after { + animation-delay: 1s; +} +.react-responsive-modal-modal:has(.code_view) { + background-color: #011627 !important; +} + +@keyframes animloader { + 0% { + transform: scale(0); + opacity: 1; + } + 100% { + transform: scale(1); + opacity: 0; + } +} +.loader_verify div div svg { + margin: auto !important; +} +.react-responsive-modal-modal { + padding-top: 35px !important; + padding-right: 35px !important; + width: 90%; + max-width: 1000px !important; + border-radius: 12px !important; +} +.code_view ~ .react-responsive-modal-closeButton { + top: 10px; + right: 10px; + width: 20px; + height: 20px; +} +.code_view ~ .react-responsive-modal-closeButton svg path { + fill: white !important; +} +.text_editabel #code-editor { + background: #191919; +} +.text_editabel .ace_gutter { + background: #191919; +} +.text_editabel .ace_content { + background: #191919; +} +.text_editabel .ace_scrollbar-h { + height: 8px !important; + background-color: #ccc; +} +.text_editabel .ace_scrollbar-h::-webkit-scrollbar-thumb { + background-color: #191919; +} +.text_editabel .ace_scrollbar-h::-webkit-scrollbar { + height: 8px; +} + +.text_editabel .ace_scrollbar-v { + width: 8px !important; + background-color: #ccc; +} +.text_editabel .ace_scrollbar-v::-webkit-scrollbar-thumb { + background-color: #191919; +} +.text_editabel .ace_scrollbar-v::-webkit-scrollbar { + width: 8px; +} +.edit_loader div div svg { + margin: auto !important; +} + +@media only screen and (max-width: 575px) { + .text_editabel #code-editor { + width: 300px !important; + } +} +@media only screen and (max-width: 375px) { + .text_editabel #code-editor { + width: 250px !important; + } +} + +.neon-on-hover { + transition: box-shadow 0.2s ease-in-out; +} +.neon-on-hover:hover { + /* Box shadow style on hover */ + box-shadow: 0px 0px 30px 0px rgba(250, 162, 80, 0.6); +} +.customellipsis { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; /* Number of lines to show */ + -webkit-box-orient: vertical; /* Orient lines vertically */ +} +.cusselect { + -webkit-appearance: none; + appearance: none; +} +.select-wrapper { + position: relative; +} + +.select-wrapper::after { + content: url("/svg/drop-down.svg"); + top: 6px; + right: 14px; + position: absolute; +} +.chat-div { + animation: scrollAnimation 1s forwards; +} + +@keyframes scrollAnimation { + from { + scroll-behavior: auto; + scroll-behavior: smooth; /* Fallback for browsers not supporting smooth scrolling */ + scroll-behavior: initial; /* Reset scroll behavior after animation */ + } + to { + scroll-behavior: smooth; + } +} + +.modal-container { + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(0, 0, 0, 0.9); + z-index: 999; +} + diff --git a/client/styles/multi-range-slider.css b/client/styles/multi-range-slider.css new file mode 100644 index 000000000..4e241af5b --- /dev/null +++ b/client/styles/multi-range-slider.css @@ -0,0 +1,18 @@ +.custom-slider { + width: 100%; + height: 2px; + background-color: white; +} + +.custom-slider .thumb { + width: 10px; + height: 10px; + cursor: grab; + background: white; + border-radius: 50%; + border: 2px solid white; + top: -4px; +} +.custom-slider .track-1 { + background-color: blue; +} diff --git a/client/tailwind.config.ts b/client/tailwind.config.ts new file mode 100644 index 000000000..a0e2f971d --- /dev/null +++ b/client/tailwind.config.ts @@ -0,0 +1,88 @@ +import type { Config } from "tailwindcss" + +const config = { + darkMode: ["class"], + content: [ + './pages/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + './app/**/*.{ts,tsx}', + './src/**/*.{ts,tsx}', + ], + prefix: "", + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + fontFamily: { + poppins: ["Poppins", "sans-serif"], + dm: ["DM Sans", "sans-serif"], + montserrat: ["Montserrat", "sans-serif"], + }, + colors: { + white: "#ffffff", + black: "#000000", + darkMain: "#191919", + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +} satisfies Config + +export default config \ No newline at end of file diff --git a/client/tsconfig.json b/client/tsconfig.json new file mode 100644 index 000000000..139a2b3ea --- /dev/null +++ b/client/tsconfig.json @@ -0,0 +1,45 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "noImplicitAny": false, + "noImplicitThis": true, + "strictNullChecks": false, + "downlevelIteration": true, + "plugins": [ + { + "name": "next" + } + ], + "baseUrl": ".", + "paths": { + "@/*": [ + "./*" + ] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + "build/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/client/types/chat-types.ts b/client/types/chat-types.ts new file mode 100644 index 000000000..219a98d47 --- /dev/null +++ b/client/types/chat-types.ts @@ -0,0 +1,24 @@ +export interface ChatMessageData { + reaction_id?: string | null; + id?: string; + space_id?: string; + conversation_id?: string; + query?: string; + response?: ChatResponseItem[]; + error?: boolean; + type?: string; + value?: string; + thumbs_up?: boolean; + code?: string; + plotSettings?: any +} + +export interface ChatResponseItem { + type?: string; + message?: string; + value?: { + headers?: string[] | null; + rows?: (string | number)[][] | { [key: string]: React.ReactNode }[]; + id?: string + } +} diff --git a/client/types/hui-types.d.ts b/client/types/hui-types.d.ts new file mode 100644 index 000000000..cde49cd9e --- /dev/null +++ b/client/types/hui-types.d.ts @@ -0,0 +1,17 @@ +export {}; + +declare global { + /** + * Now declare things that go in the global namespace, + * or augment existing declarations in the global namespace. + */ + + interface RoutesType { + name: string; + layout: string; + icon: JSX.Element | string; + path: string; + secondary?: boolean | undefined; + visible?: boolean | undefined; + } +} diff --git a/client/types/images.d.ts b/client/types/images.d.ts new file mode 100644 index 000000000..4c2cc34ed --- /dev/null +++ b/client/types/images.d.ts @@ -0,0 +1,4 @@ +declare module "*.svg"; +declare module "*.png"; +declare module "*.jpg"; +declare module "*.jpeg"; diff --git a/client/types/navigation.d.ts b/client/types/navigation.d.ts new file mode 100644 index 000000000..95847d09c --- /dev/null +++ b/client/types/navigation.d.ts @@ -0,0 +1,15 @@ +export interface IRoute { + items?: [] | IRoute[]; + name?: string; + layout?: string; + icon?: JSX.Element | string; + path?: string; + secondary?: boolean | undefined; +} +export interface RoutesType { + name: string; + layout: string; + icon: JSX.Element | string; + path: string; + secondary?: boolean | undefined; +} diff --git a/client/types/react-table-config.d.ts b/client/types/react-table-config.d.ts new file mode 100644 index 000000000..1a3d89ee6 --- /dev/null +++ b/client/types/react-table-config.d.ts @@ -0,0 +1,147 @@ +import { + UseColumnOrderInstanceProps, + UseColumnOrderState, + UseExpandedHooks, + UseExpandedInstanceProps, + UseExpandedOptions, + UseExpandedRowProps, + UseExpandedState, + UseFiltersColumnOptions, + UseFiltersColumnProps, + UseFiltersInstanceProps, + UseFiltersOptions, + UseFiltersState, + UseGlobalFiltersColumnOptions, + UseGlobalFiltersInstanceProps, + UseGlobalFiltersOptions, + UseGlobalFiltersState, + UseGroupByCellProps, + UseGroupByColumnOptions, + UseGroupByColumnProps, + UseGroupByHooks, + UseGroupByInstanceProps, + UseGroupByOptions, + UseGroupByRowProps, + UseGroupByState, + UsePaginationInstanceProps, + UsePaginationOptions, + UsePaginationState, + UseResizeColumnsColumnOptions, + UseResizeColumnsColumnProps, + UseResizeColumnsOptions, + UseResizeColumnsState, + UseRowSelectHooks, + UseRowSelectInstanceProps, + UseRowSelectOptions, + UseRowSelectRowProps, + UseRowSelectState, + UseRowStateCellProps, + UseRowStateInstanceProps, + UseRowStateOptions, + UseRowStateRowProps, + UseRowStateState, + UseSortByColumnOptions, + UseSortByColumnProps, + UseSortByHooks, + UseSortByInstanceProps, + UseSortByOptions, + UseSortByState, + Column, +} from "react-table"; + +declare module "react-table" { + // take this file as-is, or comment out the sections that don't apply to your plugin configuration + + export interface TableOptions> + extends UseExpandedOptions, + UseFiltersOptions, + UseGlobalFiltersOptions, + UseGroupByOptions, + UsePaginationOptions, + UseResizeColumnsOptions, + UseRowSelectOptions, + UseRowStateOptions, + UseSortByOptions, + // note that having Record here allows you to add anything to the options, this matches the spirit of the + // underlying js library, but might be cleaner if it's replaced by a more specific type that matches your + // feature set, this is a safe default. + Record {} + + export interface Hooks< + D extends Record = Record, + > extends UseExpandedHooks, + UseGroupByHooks, + UseRowSelectHooks, + UseSortByHooks {} + + export interface TableInstance< + D extends Record = Record, + > extends UseColumnOrderInstanceProps, + UseExpandedInstanceProps, + UseFiltersInstanceProps, + UseGlobalFiltersInstanceProps, + UseGroupByInstanceProps, + UsePaginationInstanceProps, + UseRowSelectInstanceProps, + UseRowStateInstanceProps, + UseSortByInstanceProps {} + + export interface TableState< + D extends Record = Record, + > extends UseColumnOrderState, + UseExpandedState, + UseFiltersState, + UseGlobalFiltersState, + UseGroupByState, + UsePaginationState, + UseResizeColumnsState, + UseRowSelectState, + UseRowStateState, + UseSortByState {} + + export interface ColumnInterface< + D extends Record = Record, + > extends UseFiltersColumnOptions, + UseGlobalFiltersColumnOptions, + UseGroupByColumnOptions, + UseResizeColumnsColumnOptions, + UseSortByColumnOptions {} + + export interface ColumnInstance< + D extends Record = Record, + > extends UseFiltersColumnProps, + UseGroupByColumnProps, + UseResizeColumnsColumnProps, + UseSortByColumnProps {} + + export interface Cell< + D extends Record = Record, + > extends UseGroupByCellProps, + UseRowStateCellProps {} + + export interface Row< + D extends Record = Record, + > extends UseExpandedRowProps, + UseGroupByRowProps, + UseRowSelectRowProps, + UseRowStateRowProps {} +} + +export type ColumnData = Column[]; + +export type TableDatum = Column<{ + name: (string | boolean)[]; + date: string; + progress: number; + quantity?: number; + status?: string; + artworks?: string; + rating?: number; +}>; + +export type TableData = TableDatum[]; + +export type TableProps = { + columnsData: ColumnData; + tableData: TableData; +}; diff --git a/client/types/stylis.d.ts b/client/types/stylis.d.ts new file mode 100644 index 000000000..e69de29bb diff --git a/client/utils/apiUtils.ts b/client/utils/apiUtils.ts new file mode 100644 index 000000000..aa5d565ef --- /dev/null +++ b/client/utils/apiUtils.ts @@ -0,0 +1,73 @@ +import axios, { AxiosResponse } from 'axios'; +import { BASE_API_URL } from './constants'; + +const axiosInstance = axios.create({ + baseURL: BASE_API_URL, + headers: { + 'Content-Type': 'application/json', + }, +}); + +// axiosInstance.interceptors.request.use((config) => { +// const token = localStorage.getItem('accessToken'); +// if (token) { +// config.headers['Authorization'] = `Bearer ${token}`; +// } +// return config; +// }, (error) => { +// return Promise.reject(error); +// }); + +export async function GetRequest(url: string): Promise> { + try { + const response = await axiosInstance.get(url); + return response; + } catch (error) { + throw error; + } +} + +export async function PostRequest(url: string, requestData: object): Promise> { + try { + const response = await axiosInstance.post(url, requestData); + return response; + } catch (error) { + throw error; + } +} + +export async function DeleteRequest(url: string): Promise> { + try { + const response = await axiosInstance.delete(url); + return response; + } catch (error) { + throw error; + } +} + +export async function PutRequest(url: string, data: object): Promise> { + try { + const response = await axiosInstance.put(url, data); + return response; + } catch (error) { + throw error; + } +} + +export async function PatchRequest(url: string, data: object): Promise> { + try { + const response = await axiosInstance.patch(url, data); + return response; + } catch (error) { + throw error; + } +} + +export async function DeleteRequestWithBody(url: string, requestData: object): Promise> { + try { + const response = await axiosInstance.delete(url, { data: requestData }); + return response; + } catch (error) { + throw error; + } +} diff --git a/client/utils/appendQueryParamtoURL.ts b/client/utils/appendQueryParamtoURL.ts new file mode 100644 index 000000000..b88e5f138 --- /dev/null +++ b/client/utils/appendQueryParamtoURL.ts @@ -0,0 +1,15 @@ +export const appendQueryParamtoURL = (newQueryParam: string) => { + let currentUrl = window.location.href; + + if (currentUrl.indexOf('?') !== -1) { + // If there are, remove the old parameter (if it exists) and append the new one + const urlWithoutParams = currentUrl.split('?')[0]; + currentUrl = urlWithoutParams + '?' + newQueryParam; + } else { + // If not, simply append the new parameter with a "?" + currentUrl += '?' + newQueryParam; + } + + // Update the URL without refreshing the page + window.history.pushState({ path: currentUrl }, '', currentUrl); +} \ No newline at end of file diff --git a/client/utils/constants.ts b/client/utils/constants.ts new file mode 100644 index 000000000..a725bff08 --- /dev/null +++ b/client/utils/constants.ts @@ -0,0 +1,9 @@ +export const BASE_API_URL = process.env.NEXT_PUBLIC_API_URL; +export const NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN = process.env.NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN; +export const NEXT_PUBLIC_MIXPANEL_TOKEN = process.env.NEXT_PUBLIC_MIXPANEL_TOKEN; +export const NEXT_PUBLIC_GOOGLE_ANALYTICS_ID = process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_ID; +export const NEXT_PUBLIC_INTERCOM_APP_ID = process.env.NEXT_PUBLIC_INTERCOM_APP_ID; + +export const ROUTE_SIGNIN = "/auth/sign-in" +export const ROUTE_ADMIN = "/admin" +export const ROUTE_CHAT_SCREEN = `${ROUTE_ADMIN}/chat` \ No newline at end of file diff --git a/client/utils/getTitleFromPath.ts b/client/utils/getTitleFromPath.ts new file mode 100644 index 000000000..dd58f5145 --- /dev/null +++ b/client/utils/getTitleFromPath.ts @@ -0,0 +1,16 @@ +export const getTitleFromPath = (pathname: string) => { + if (pathname.includes("chat")) return "Chat"; + if (pathname.includes("datasets")) return "Datasets"; + if (pathname.includes("workspace")) return "Workspace"; + if (pathname.includes("test")) return "Test"; + if (pathname.includes("logs")) return "Logs"; + if (pathname.includes("spaces")) return "Spaces"; + if (pathname.includes("api-keys")) return "Api Keys"; + if (pathname.includes("organization")) return "Organization"; + if (pathname.includes("sign-up")) return "Sign-up"; + if (pathname.includes("conversation")) return "Conversation"; + if (pathname.includes("train")) return "Train"; + if (pathname.includes("feed")) return "Feed"; + if (pathname.includes("admin")) return "Admin"; + return "Sign In"; +}; diff --git a/client/utils/hexToRgba.ts b/client/utils/hexToRgba.ts new file mode 100644 index 000000000..6071c1259 --- /dev/null +++ b/client/utils/hexToRgba.ts @@ -0,0 +1,11 @@ +export function hexToRgba(hex: string, opacity: number) { + const r = parseInt(hex.slice(1, 3), 16), + g = parseInt(hex.slice(3, 5), 16), + b = parseInt(hex.slice(5, 7), 16); + + if (opacity) { + return "rgba(" + r + ", " + g + ", " + b + ", " + opacity + ")"; + } else { + return "rgb(" + r + ", " + g + ", " + b + ")"; + } +} \ No newline at end of file diff --git a/client/utils/navigation.ts b/client/utils/navigation.ts new file mode 100644 index 000000000..65e1189d0 --- /dev/null +++ b/client/utils/navigation.ts @@ -0,0 +1,39 @@ +import { IRoute } from "../types/navigation"; + +// NextJS Requirement +export const isWindowAvailable = () => typeof window !== "undefined"; + +export const findCurrentRoute = ( + routes: IRoute[], + pathname: string, +): IRoute => { + if (!isWindowAvailable()) return null; + + for (const route of routes) { + if (route.items) { + const found = findCurrentRoute(route.items, pathname); + if (found) return found; + } + if (pathname?.match(route.path) && route) return route; + } +}; + +export const getActiveRoute = (routes: IRoute[], pathname: string): string => { + const route = findCurrentRoute(routes, pathname); + return route?.name || ""; +}; + +export const getActiveNavbar = ( + routes: IRoute[], + pathname: string, +): boolean => { + const route = findCurrentRoute(routes, pathname); + return route?.secondary; +}; + +export const getActiveNavbarText = ( + routes: IRoute[], + pathname: string, +): string | boolean => { + return getActiveRoute(routes, pathname) || false; +}; diff --git a/client/utils/reorderConversations.ts b/client/utils/reorderConversations.ts new file mode 100644 index 000000000..784b27ee7 --- /dev/null +++ b/client/utils/reorderConversations.ts @@ -0,0 +1,19 @@ +export function reorderArray(originalArray) { + const newArray = []; + const queries = []; + const responses = []; + if (originalArray != undefined) { + originalArray?.forEach((item) => { + if ("query" in item) { + queries.push(item); + } else if ("response" in item) { + responses.push(item); + } + }); + } + for (let i = 0; i < Math.max(queries?.length, responses?.length); i++) { + if (queries[i]) newArray?.push(queries[i]); + if (responses[i]) newArray?.push(responses[i]); + } + return newArray; +} \ No newline at end of file diff --git a/client/yarn.lock b/client/yarn.lock new file mode 100644 index 000000000..a5b772b16 --- /dev/null +++ b/client/yarn.lock @@ -0,0 +1,3231 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + +"@babel/runtime@^7.0.0", "@babel/runtime@^7.13.10", "@babel/runtime@^7.23.2", "@babel/runtime@^7.24.1": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.6.tgz#5b76eb89ad45e2e4a0a8db54c456251469a3358e" + integrity sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw== + dependencies: + regenerator-runtime "^0.14.0" + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": + version "4.10.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.1.tgz#361461e5cb3845d874e61731c11cfedd664d83a0" + integrity sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== + +"@floating-ui/core@^1.0.0": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.2.tgz#d37f3e0ac1f1c756c7de45db13303a266226851a" + integrity sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg== + dependencies: + "@floating-ui/utils" "^0.2.0" + +"@floating-ui/dom@^1.6.1": + version "1.6.5" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.5.tgz#323f065c003f1d3ecf0ff16d2c2c4d38979f4cb9" + integrity sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw== + dependencies: + "@floating-ui/core" "^1.0.0" + "@floating-ui/utils" "^0.2.0" + +"@floating-ui/utils@^0.2.0": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.2.tgz#d8bae93ac8b815b2bd7a98078cf91e2724ef11e5" + integrity sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw== + +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + dependencies: + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.24": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@kurkle/color@^0.3.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f" + integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw== + +"@next/env@14.2.3": + version "14.2.3" + resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.3.tgz#d6def29d1c763c0afb397343a15a82e7d92353a0" + integrity sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA== + +"@next/eslint-plugin-next@14.2.3": + version "14.2.3" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.3.tgz#287ad8620e7061ba01e8d3313d464db6d217b6df" + integrity sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw== + dependencies: + glob "10.3.10" + +"@next/swc-darwin-arm64@14.2.3": + version "14.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz#db1a05eb88c0224089b815ad10ac128ec79c2cdb" + integrity sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A== + +"@next/swc-darwin-x64@14.2.3": + version "14.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz#a3f8af05b5f9a52ac3082e66ac29e125ab1d7b9c" + integrity sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA== + +"@next/swc-linux-arm64-gnu@14.2.3": + version "14.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz#4e63f43879285b52554bfd39e6e0cc78a9b27bbf" + integrity sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA== + +"@next/swc-linux-arm64-musl@14.2.3": + version "14.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz#ebdaed26214448b1e6f2c3e8b3cd29bfba387990" + integrity sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw== + +"@next/swc-linux-x64-gnu@14.2.3": + version "14.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz#19e3bcc137c3b582a1ab867106817e5c90a20593" + integrity sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w== + +"@next/swc-linux-x64-musl@14.2.3": + version "14.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz#794a539b98e064169cf0ff7741b2a4fb16adec7d" + integrity sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ== + +"@next/swc-win32-arm64-msvc@14.2.3": + version "14.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz#eda9fa0fbf1ff9113e87ac2668ee67ce9e5add5a" + integrity sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A== + +"@next/swc-win32-ia32-msvc@14.2.3": + version "14.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz#7c1190e3f640ab16580c6bdbd7d0e766b9920457" + integrity sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw== + +"@next/swc-win32-x64-msvc@14.2.3": + version "14.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz#2be4e39ee25bfbd85be78eea17c0e7751dc4323c" + integrity sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA== + +"@next/third-parties@^14.2.3": + version "14.2.3" + resolved "https://registry.yarnpkg.com/@next/third-parties/-/third-parties-14.2.3.tgz#2e72d9fa456c1155700082ab40ac68018e9ec5d2" + integrity sha512-j4E2xBSsEZq4VX2pVm3LpGltSwCxETic6glJWfHyYQvpoMdplCAYrQKpF+E9Gg3jfsrfmRAIdTE11m+biBCx1Q== + dependencies: + third-party-capital "1.0.20" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@radix-ui/react-compose-refs@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989" + integrity sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-slot@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab" + integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + +"@rollbar/react@^0.12.0-beta": + version "0.12.0-beta" + resolved "https://registry.yarnpkg.com/@rollbar/react/-/react-0.12.0-beta.tgz#b80dfab1535ece358a0e98a4f26aadee8b54edda" + integrity sha512-8udBX0lJwdBBq+O/jqDXpg/giHt8bo/Us1IlTkHEdCBO18Cjj7sxWJ80OPFxiPRNwZgZnhf2HbxQxvLN+4FeJA== + dependencies: + tiny-invariant "^1.1.0" + +"@rrweb/types@^2.0.0-alpha.13": + version "2.0.0-alpha.14" + resolved "https://registry.yarnpkg.com/@rrweb/types/-/types-2.0.0-alpha.14.tgz#f7bb6429a13402b54ebd948c2e7275ffbe881280" + integrity sha512-H0qKW75SdsZM4/4116fQDDC3QkUxbP7A9AY5PK2nyUV56KReAQ1sH8ZHu9tomvn0kFJUXhtvjv2H6G6xxSJNqA== + dependencies: + rrweb-snapshot "^2.0.0-alpha.14" + +"@rushstack/eslint-patch@^1.3.3": + version "1.10.3" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz#391d528054f758f81e53210f1a1eebcf1a8b1d20" + integrity sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg== + +"@swc/counter@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" + integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== + +"@swc/helpers@0.5.5": + version "0.5.5" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.5.tgz#12689df71bfc9b21c4f4ca00ae55f2f16c8b77c0" + integrity sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A== + dependencies: + "@swc/counter" "^0.1.3" + tslib "^2.4.0" + +"@table-library/react-table-library@^4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@table-library/react-table-library/-/react-table-library-4.1.7.tgz#90b71f959228f420d2deffeecc9eace879cb4c2e" + integrity sha512-KKFjdACvEUeD9yhgXBjZUJSdE9pxUbJdou4Pp71FW8dxQWc7LcMcSxpveSfGXw8KlcWY0hThJOwyjQb+UKOmnw== + dependencies: + clsx "1.1.1" + react-virtualized-auto-sizer "1.0.7" + react-window "1.8.7" + +"@tanstack/query-core@5.40.0": + version "5.40.0" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.40.0.tgz#c74ae8303752ed4b5a0ab848ec71a0e6e8179f83" + integrity sha512-eD8K8jsOIq0Z5u/QbvOmfvKKE/XC39jA7yv4hgpl/1SRiU+J8QCIwgM/mEHuunQsL87dcvnHqSVLmf9pD4CiaA== + +"@tanstack/query-devtools@5.37.1": + version "5.37.1" + resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.37.1.tgz#8dcfa1488b4f2e353be7eede6691b0ad9197183b" + integrity sha512-XcG4IIHIv0YQKrexTqo2zogQWR1Sz672tX2KsfE9kzB+9zhx44vRKH5si4WDILE1PIWQpStFs/NnrDQrBAUQpg== + +"@tanstack/react-query-devtools@^5.39.0": + version "5.40.0" + resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.40.0.tgz#76999ddd4c4ef8a9e17c35912459e9a291307409" + integrity sha512-JoQOQj/LKaHoHVMAh73R0pc4lIAHiZMV8L4DGHsTSvHcKi22LZmSC9aYBY9oMkqGpFtKmbspwrUIn55+czNSbA== + dependencies: + "@tanstack/query-devtools" "5.37.1" + +"@tanstack/react-query@^5.39.0": + version "5.40.0" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.40.0.tgz#654afa2d9ab328c22be7e1f025ec9b6267c6baa9" + integrity sha512-iv/W0Axc4aXhFzkrByToE1JQqayxTPNotCoSCnarR/A1vDIHaoKpg7FTIfP3Ev2mbKn1yrxq0ZKYUdLEJxs6Tg== + dependencies: + "@tanstack/query-core" "5.40.0" + +"@types/css-font-loading-module@0.0.7": + version "0.0.7" + resolved "https://registry.yarnpkg.com/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz#2f98ede46acc0975de85c0b7b0ebe06041d24601" + integrity sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/mixpanel-browser@^2.49.0": + version "2.49.0" + resolved "https://registry.yarnpkg.com/@types/mixpanel-browser/-/mixpanel-browser-2.49.0.tgz#ad92ecc36fad63b9c0aed80b6283d86dbf52e49e" + integrity sha512-StmgUnS58d44DmIAEX9Kk8qwisAYCl6E2qulIjYyHXUPuJCPOuyUMTTKBp+aU2F2do+kxAzCxiBtsB4fnBT9Fg== + +"@types/node@^20": + version "20.14.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.0.tgz#49ceec7b34f8621470cff44677fa9d461a477f17" + integrity sha512-5cHBxFGJx6L4s56Bubp4fglrEpmyJypsqI6RgzMfBHWUJQGWAAi8cWcgetEbZXHYXo9C2Fa4EEds/uSyS4cxmA== + dependencies: + undici-types "~5.26.4" + +"@types/prop-types@*": + version "15.7.12" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" + integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== + +"@types/react-dom@^18": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" + integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@^18": + version "18.3.3" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.3.tgz#9679020895318b0915d7a3ab004d92d33375c45f" + integrity sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + +"@typescript-eslint/eslint-plugin@^7.11.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.12.0.tgz#f87a32e8972b8a60024f2f8f12205e7c8108bc41" + integrity sha512-7F91fcbuDf/d3S8o21+r3ZncGIke/+eWk0EpO21LXhDfLahriZF9CGj4fbAetEjlaBdjdSm9a6VeXbpbT6Z40Q== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "7.12.0" + "@typescript-eslint/type-utils" "7.12.0" + "@typescript-eslint/utils" "7.12.0" + "@typescript-eslint/visitor-keys" "7.12.0" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/parser@^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.2.0.tgz#44356312aea8852a3a82deebdacd52ba614ec07a" + integrity sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg== + dependencies: + "@typescript-eslint/scope-manager" "7.2.0" + "@typescript-eslint/types" "7.2.0" + "@typescript-eslint/typescript-estree" "7.2.0" + "@typescript-eslint/visitor-keys" "7.2.0" + debug "^4.3.4" + +"@typescript-eslint/parser@^7.11.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.12.0.tgz#8761df3345528b35049353db80010b385719b1c3" + integrity sha512-dm/J2UDY3oV3TKius2OUZIFHsomQmpHtsV0FTh1WO8EKgHLQ1QCADUqscPgTpU+ih1e21FQSRjXckHn3txn6kQ== + dependencies: + "@typescript-eslint/scope-manager" "7.12.0" + "@typescript-eslint/types" "7.12.0" + "@typescript-eslint/typescript-estree" "7.12.0" + "@typescript-eslint/visitor-keys" "7.12.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.12.0.tgz#259c014362de72dd34f995efe6bd8dda486adf58" + integrity sha512-itF1pTnN6F3unPak+kutH9raIkL3lhH1YRPGgt7QQOh43DQKVJXmWkpb+vpc/TiDHs6RSd9CTbDsc/Y+Ygq7kg== + dependencies: + "@typescript-eslint/types" "7.12.0" + "@typescript-eslint/visitor-keys" "7.12.0" + +"@typescript-eslint/scope-manager@7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz#cfb437b09a84f95a0930a76b066e89e35d94e3da" + integrity sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg== + dependencies: + "@typescript-eslint/types" "7.2.0" + "@typescript-eslint/visitor-keys" "7.2.0" + +"@typescript-eslint/type-utils@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.12.0.tgz#9dfaaa1972952f395ec5be4f5bbfc4d3cdc63908" + integrity sha512-lib96tyRtMhLxwauDWUp/uW3FMhLA6D0rJ8T7HmH7x23Gk1Gwwu8UZ94NMXBvOELn6flSPiBrCKlehkiXyaqwA== + dependencies: + "@typescript-eslint/typescript-estree" "7.12.0" + "@typescript-eslint/utils" "7.12.0" + debug "^4.3.4" + ts-api-utils "^1.3.0" + +"@typescript-eslint/types@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.12.0.tgz#bf208f971a8da1e7524a5d9ae2b5f15192a37981" + integrity sha512-o+0Te6eWp2ppKY3mLCU+YA9pVJxhUJE15FV7kxuD9jgwIAa+w/ycGJBMrYDTpVGUM/tgpa9SeMOugSabWFq7bg== + +"@typescript-eslint/types@7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.2.0.tgz#0feb685f16de320e8520f13cca30779c8b7c403f" + integrity sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA== + +"@typescript-eslint/typescript-estree@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.12.0.tgz#e6c1074f248b3db6573ab6a7c47a39c4cd498ff9" + integrity sha512-5bwqLsWBULv1h6pn7cMW5dXX/Y2amRqLaKqsASVwbBHMZSnHqE/HN4vT4fE0aFsiwxYvr98kqOWh1a8ZKXalCQ== + dependencies: + "@typescript-eslint/types" "7.12.0" + "@typescript-eslint/visitor-keys" "7.12.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/typescript-estree@7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz#5beda2876c4137f8440c5a84b4f0370828682556" + integrity sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA== + dependencies: + "@typescript-eslint/types" "7.2.0" + "@typescript-eslint/visitor-keys" "7.2.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "9.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/utils@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.12.0.tgz#c6e58fd7f724cdccc848f71e388ad80cbdb95dd0" + integrity sha512-Y6hhwxwDx41HNpjuYswYp6gDbkiZ8Hin9Bf5aJQn1bpTs3afYY4GX+MPYxma8jtoIV2GRwTM/UJm/2uGCVv+DQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "7.12.0" + "@typescript-eslint/types" "7.12.0" + "@typescript-eslint/typescript-estree" "7.12.0" + +"@typescript-eslint/visitor-keys@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.12.0.tgz#c053b55a996679528beeedd8e565710ce1ae1ad3" + integrity sha512-uZk7DevrQLL3vSnfFl5bj4sL75qC9D6EdjemIdbtkuUmIheWpuiiylSY01JxJE7+zGrOWDZrp1WxOuDntvKrHQ== + dependencies: + "@typescript-eslint/types" "7.12.0" + eslint-visitor-keys "^3.4.3" + +"@typescript-eslint/visitor-keys@7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz#5035f177752538a5750cca1af6044b633610bf9e" + integrity sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A== + dependencies: + "@typescript-eslint/types" "7.2.0" + eslint-visitor-keys "^3.4.1" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@xstate/fsm@^1.4.0": + version "1.6.5" + resolved "https://registry.yarnpkg.com/@xstate/fsm/-/fsm-1.6.5.tgz#f599e301997ad7e3c572a0b1ff0696898081bea5" + integrity sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw== + +ace-builds@^1.32.8: + version "1.34.2" + resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.34.2.tgz#c17c6fd7c661c7ba33f57533a42004a7b3f8dcbd" + integrity sha512-wiOZYuxyOSYfZzDasQTe+ZWmRlYxXSJM/kMKZ/bSqO1VgrBl+PaaTz/Sc+y7hXCKAUj3syUdpwxQyvwv9vQe6w== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.9.0: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +aria-query@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + +array-includes@^3.1.6, array-includes@^3.1.7, array-includes@^3.1.8: + version "3.1.8" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" + integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.findlast@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" + integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + +array.prototype.findlastindex@^1.2.3: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" + integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + +array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.toreversed@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz#b989a6bf35c4c5051e1dc0325151bf8088954eba" + integrity sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" + integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.3" + es-errors "^1.3.0" + es-shim-unscopables "^1.0.2" + +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + +ast-types-flow@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" + integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== + +async@~3.2.3: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +axe-core@=4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf" + integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ== + +axios@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621" + integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +axobject-query@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" + integrity sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg== + dependencies: + dequal "^2.0.3" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-arraybuffer@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +busboy@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +caniuse-lite@^1.0.30001579: + version "1.0.30001627" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001627.tgz#8071c42d468e06ed2fb2c545efe79a663fd326ab" + integrity sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chart.js@^4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.3.tgz#3b2e11e7010fefa99b07d0349236f5098e5226ad" + integrity sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw== + dependencies: + "@kurkle/color" "^0.3.0" + +chartjs-adapter-date-fns@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz#c25f63c7f317c1f96f9a7c44bd45eeedb8a478e5" + integrity sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg== + +chokidar@^3.5.3: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +class-variance-authority@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522" + integrity sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A== + dependencies: + clsx "2.0.0" + +classnames@^2.3.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + +client-only@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" + integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== + +clsx@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" + integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== + +clsx@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b" + integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q== + +clsx@^2.1.0, clsx@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +console-polyfill@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/console-polyfill/-/console-polyfill-0.3.0.tgz#84900902a18c47a5eba932be75fa44d23e8af861" + integrity sha512-w+JSDZS7XML43Xnwo2x5O5vxB0ID7T5BdqDtyqT6uiCAX2kZAgcWxNaGqT97tZfSHzfOcvrfsDAodKcJ3UvnXQ== + +cross-spawn@^7.0.0, cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +damerau-levenshtein@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" + integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== + +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +date-fns@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf" + integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww== + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + +decache@^3.0.5: + version "3.1.0" + resolved "https://registry.yarnpkg.com/decache/-/decache-3.1.0.tgz#4f5036fbd6581fcc97237ac3954a244b9536c2da" + integrity sha512-p7D6wJ5EJFFq1CcF2lu1XeqKFLBob8jRQGNAvFLTsV3CbSKBl3VtliAVlUIGz2i9H6kEFnI2Amaft5ZopIG2Fw== + dependencies: + find "^0.2.4" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + +diff-match-patch@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" + integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +enhanced-resolve@^5.12.0: + version "5.16.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz#e8bc63d51b826d6f1cbc0a150ecb5a8b0c62e567" + integrity sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +error-stack-parser@^2.0.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + +es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-iterator-helpers@^1.0.15, es-iterator-helpers@^1.0.19: + version "1.0.19" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz#117003d0e5fec237b4b5c08aded722e0c6d50ca8" + integrity sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.3" + es-errors "^1.3.0" + es-set-tostringtag "^2.0.3" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + globalthis "^1.0.3" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + iterator.prototype "^1.1.2" + safe-array-concat "^1.1.2" + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + +es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-next@14.2.3: + version "14.2.3" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-14.2.3.tgz#2fb0f7c4eccda530a4b5054438162b2303786d4f" + integrity sha512-ZkNztm3Q7hjqvB1rRlOX8P9E/cXRL9ajRcs8jufEtwMfTVYRqnmtnaSu57QqHyBlovMuiB8LEzfLBkh5RYV6Fg== + dependencies: + "@next/eslint-plugin-next" "14.2.3" + "@rushstack/eslint-patch" "^1.3.3" + "@typescript-eslint/parser" "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0" + eslint-import-resolver-node "^0.3.6" + eslint-import-resolver-typescript "^3.5.2" + eslint-plugin-import "^2.28.1" + eslint-plugin-jsx-a11y "^6.7.1" + eslint-plugin-react "^7.33.2" + eslint-plugin-react-hooks "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" + +eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-import-resolver-typescript@^3.5.2: + version "3.6.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz#7b983680edd3f1c5bce1a5829ae0bc2d57fe9efa" + integrity sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg== + dependencies: + debug "^4.3.4" + enhanced-resolve "^5.12.0" + eslint-module-utils "^2.7.4" + fast-glob "^3.3.1" + get-tsconfig "^4.5.0" + is-core-module "^2.11.0" + is-glob "^4.0.3" + +eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz#52f2404300c3bd33deece9d7372fb337cc1d7c34" + integrity sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q== + dependencies: + debug "^3.2.7" + +eslint-plugin-import@^2.28.1: + version "2.29.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" + integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== + dependencies: + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.8.0" + hasown "^2.0.0" + is-core-module "^2.13.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" + semver "^6.3.1" + tsconfig-paths "^3.15.0" + +eslint-plugin-jsx-a11y@^6.7.1: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz#2fa9c701d44fcd722b7c771ec322432857fcbad2" + integrity sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA== + dependencies: + "@babel/runtime" "^7.23.2" + aria-query "^5.3.0" + array-includes "^3.1.7" + array.prototype.flatmap "^1.3.2" + ast-types-flow "^0.0.8" + axe-core "=4.7.0" + axobject-query "^3.2.1" + damerau-levenshtein "^1.0.8" + emoji-regex "^9.2.2" + es-iterator-helpers "^1.0.15" + hasown "^2.0.0" + jsx-ast-utils "^3.3.5" + language-tags "^1.0.9" + minimatch "^3.1.2" + object.entries "^1.1.7" + object.fromentries "^2.0.7" + +"eslint-plugin-react-hooks@^4.5.0 || 5.0.0-canary-7118f5dd7-20230705": + version "4.6.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596" + integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== + +eslint-plugin-react@^7.33.2: + version "7.34.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.34.2.tgz#2780a1a35a51aca379d86d29b9a72adc6bfe6b66" + integrity sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw== + dependencies: + array-includes "^3.1.8" + array.prototype.findlast "^1.2.5" + array.prototype.flatmap "^1.3.2" + array.prototype.toreversed "^1.1.2" + array.prototype.tosorted "^1.1.3" + doctrine "^2.1.0" + es-iterator-helpers "^1.0.19" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.8" + object.fromentries "^2.0.8" + object.hasown "^1.1.4" + object.values "^1.2.0" + prop-types "^15.8.1" + resolve "^2.0.0-next.5" + semver "^6.3.1" + string.prototype.matchall "^4.0.11" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8: + version "8.57.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +fflate@^0.4.4: + version "0.4.8" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae" + integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA== + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find@^0.2.4: + version "0.2.9" + resolved "https://registry.yarnpkg.com/find/-/find-0.2.9.tgz#4b73f1ff9e56ad91b76e716407fe5ffe6554bb8c" + integrity sha512-7a4/LCiInB9xYMnAUEjLilL9FKclwbwK7VlXw+h5jMvT2TDFeYFCHM24O1XdnC/on/hx8mxVO3FTQkyHZnOghQ== + dependencies: + traverse-chain "~0.1.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + +get-tsconfig@^4.5.0: + version "4.7.5" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" + integrity sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw== + dependencies: + resolve-pkg-maps "^1.0.0" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@10.3.10: + version "10.3.10" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" + integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.5" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" + +glob@^10.3.10: + version "10.4.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.1.tgz#0cfb01ab6a6b438177bfe6a58e2576f6efe909c2" + integrity sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + path-scurry "^1.11.1" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.2.11, graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +ignore@^5.2.0, ignore@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + +is-date-object@^1.0.1, is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finalizationregistry@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" + integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== + dependencies: + call-bind "^1.0.2" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-function@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-weakset@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007" + integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +iterator.prototype@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" + integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== + dependencies: + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" + +jackspeak@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jackspeak@^3.1.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.2.3.tgz#33e8c44f7858d199fc5684f4ab62d1fd873eb10d" + integrity sha512-htOzIMPbpLid/Gq9/zaz9SfExABxqRe1sSCdxntlO/aMD6u0issZQiY25n2GKQUtJ02j7z5sfptlAOMpWWOmvw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jiti@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" + integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json-stringify-safe@~5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: + version "3.3.5" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +language-subtag-registry@^0.3.20: + version "0.3.23" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" + integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== + +language-tags@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.9.tgz#1ffdcd0ec0fafb4b1be7f8b11f306ad0f9c08777" + integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== + dependencies: + language-subtag-registry "^0.3.20" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lilconfig@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lilconfig@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.1.tgz#9d8a246fa753106cfc205fd2d77042faca56e5e3" + integrity sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^10.2.0: + version "10.2.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878" + integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== + +lru-cache@~2.2.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d" + integrity sha512-Q5pAgXs+WEAfoEdw2qKQhNFFhMoFMTYqRVKKUMnzuiR7oKFHS7fWo848cPcTKw+4j/IdN17NyzdhVKgabFV0EA== + +"memoize-one@>=3.1.1 <6": + version "5.2.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +minimatch@9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.1, minimatch@^9.0.4: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +mitt@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + +mixpanel-browser@^2.50.0: + version "2.51.0" + resolved "https://registry.yarnpkg.com/mixpanel-browser/-/mixpanel-browser-2.51.0.tgz#2df372bde0a415704beb382a244244faca9296bf" + integrity sha512-cNjvvhlRkSlqW1w4nxRpK5y4R3QcfhY7H/ZfvZ4jLeiBUNeeuwyQTgOAk/mYiBDOonWEdeN7h3EkL2BdqKd2Sw== + dependencies: + rrweb "2.0.0-alpha.13" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multiselect-react-dropdown@^2.0.25: + version "2.0.25" + resolved "https://registry.yarnpkg.com/multiselect-react-dropdown/-/multiselect-react-dropdown-2.0.25.tgz#0c8d16f20d78023d5be2f3af4f15a4a164b6b427" + integrity sha512-z8kUSyBNOuV7vn4Dk35snzXWtIfTdSEEXhgDdLMvOmR+xJFx35vc1voUlSuOvrk3khb+hXC219Qs9szOvNm25Q== + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoid@^3.3.6, nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +next@14.2.3: + version "14.2.3" + resolved "https://registry.yarnpkg.com/next/-/next-14.2.3.tgz#f117dd5d5f20c307e7b8e4f9c1c97d961008925d" + integrity sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A== + dependencies: + "@next/env" "14.2.3" + "@swc/helpers" "0.5.5" + busboy "1.6.0" + caniuse-lite "^1.0.30001579" + graceful-fs "^4.2.11" + postcss "8.4.31" + styled-jsx "5.1.1" + optionalDependencies: + "@next/swc-darwin-arm64" "14.2.3" + "@next/swc-darwin-x64" "14.2.3" + "@next/swc-linux-arm64-gnu" "14.2.3" + "@next/swc-linux-arm64-musl" "14.2.3" + "@next/swc-linux-x64-gnu" "14.2.3" + "@next/swc-linux-x64-musl" "14.2.3" + "@next/swc-win32-arm64-msvc" "14.2.3" + "@next/swc-win32-ia32-msvc" "14.2.3" + "@next/swc-win32-x64-msvc" "14.2.3" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-assign@^4.0.1, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4, object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.7, object.entries@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" + integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +object.fromentries@^2.0.7, object.fromentries@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.groupby@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" + integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + +object.hasown@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.4.tgz#e270ae377e4c120cdcb7656ce66884a6218283dc" + integrity sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg== + dependencies: + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.values@^1.1.6, object.values@^1.1.7, object.values@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" + integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.10.1, path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pirates@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" + integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== + dependencies: + camelcase-css "^2.0.1" + +postcss-load-config@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" + integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== + dependencies: + lilconfig "^3.0.0" + yaml "^2.3.4" + +postcss-nested@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.1.tgz#f83dc9846ca16d2f4fa864f16e9d9f7d0961662c" + integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== + dependencies: + postcss-selector-parser "^6.0.11" + +postcss-selector-parser@^6.0.11: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz#49694cb4e7c649299fea510a29fa6577104bcf53" + integrity sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@8.4.31: + version "8.4.31" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +postcss@^8, postcss@^8.4.23: + version "8.4.38" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" + integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.2.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +react-ace@^11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-11.0.1.tgz#5e901629a01113849a3860f47b20d52a9689d55c" + integrity sha512-ulk2851Fx2j59AAahZHTe7rmQ5bITW1xytskAt11F8dv3rPLtdwBXCyT2qSbRnJvOq8UpuAhWO4/JhKGqQBEDA== + dependencies: + ace-builds "^1.32.8" + diff-match-patch "^1.0.5" + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + prop-types "^15.8.1" + +react-chartjs-2@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz#43c1e3549071c00a1a083ecbd26c1ad34d385f5d" + integrity sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA== + +react-dom@^18: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" + integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.2" + +react-icons@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.2.1.tgz#28c2040917b2a2eda639b0f797bff1888e018e4a" + integrity sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw== + +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-loading-skeleton@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/react-loading-skeleton/-/react-loading-skeleton-3.4.0.tgz#c71a3a17259d08e4064974aa0b07f150a09dfd57" + integrity sha512-1oJEBc9+wn7BbkQQk7YodlYEIjgeR+GrRjD+QXkVjwZN7LGIcAFHrx4NhT7UHGBxNY1+zax3c+Fo6XQM4R7CgA== + +react-modern-drawer@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/react-modern-drawer/-/react-modern-drawer-1.3.1.tgz#07db41bf5a45c7177b61e49c26e3950061c227ab" + integrity sha512-bT4KLgrnu98dL9NUpiHbg6dpp4zRH7M+VlS1zK6zCFgn6OLxjGEBRTf2ZYfVcibLGTkoRJxfD5l7zTqWtDLp+w== + +react-toastify@^10.0.5: + version "10.0.5" + resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-10.0.5.tgz#6b8f8386060c5c856239f3036d1e76874ce3bd1e" + integrity sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw== + dependencies: + clsx "^2.1.0" + +react-tooltip@^5.26.4: + version "5.26.4" + resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-5.26.4.tgz#9e350f2a1c859201ccdc3b15d17e0a01e88c0a0f" + integrity sha512-5WyDrsfw1+6qNVSr3IjqElqJ+cCwE8+44b+HpJ8qRLv7v0a3mcKf8wvv+NfgALFS6QpksGFqTLV2JQ60c+okZQ== + dependencies: + "@floating-ui/dom" "^1.6.1" + classnames "^2.3.0" + +react-virtualized-auto-sizer@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.7.tgz#bfb8414698ad1597912473de3e2e5f82180c1195" + integrity sha512-Mxi6lwOmjwIjC1X4gABXMJcKHsOo0xWl3E3ugOgufB8GJU+MqrtY35aBuvCYv/razQ1Vbp7h1gWJjGjoNN5pmA== + +react-window@1.8.7: + version "1.8.7" + resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.7.tgz#5e9fd0d23f48f432d7022cdb327219353a15f0d4" + integrity sha512-JHEZbPXBpKMmoNO1bNhoXOOLg/ujhL/BU4IqVU9r8eQPcy5KQnGHIHDRkJ0ns9IM5+Aq5LNwt3j8t3tIrePQzA== + dependencies: + "@babel/runtime" "^7.0.0" + memoize-one ">=3.1.1 <6" + +react@^18: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" + integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== + dependencies: + loose-envify "^1.1.0" + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +reflect.getprototypeof@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859" + integrity sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.1" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + globalthis "^1.0.3" + which-builtin-type "^1.1.3" + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +request-ip@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/request-ip/-/request-ip-3.3.0.tgz#863451e8fec03847d44f223e30a5d63e369fa611" + integrity sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + +resolve@^1.1.7, resolve@^1.22.2, resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.5: + version "2.0.0-next.5" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rollbar@^2.26.4: + version "2.26.4" + resolved "https://registry.yarnpkg.com/rollbar/-/rollbar-2.26.4.tgz#05e47d3b1f52ab6da9f88710ec66371a76cdc3c9" + integrity sha512-JKmrj6riYm9ZPJisgxljgH4uCsvjMHDHXrinDF7aAFaP+eoF51HomVPtLcDTYLsrJ568aKVNLUhedFajONBwSg== + dependencies: + async "~3.2.3" + console-polyfill "0.3.0" + error-stack-parser "^2.0.4" + json-stringify-safe "~5.0.0" + lru-cache "~2.2.1" + request-ip "~3.3.0" + source-map "^0.5.7" + optionalDependencies: + decache "^3.0.5" + +rrdom@^2.0.0-alpha.13: + version "2.0.0-alpha.14" + resolved "https://registry.yarnpkg.com/rrdom/-/rrdom-2.0.0-alpha.14.tgz#692e9f7c7abfc79cd8b7939c1cc6eb9977e970ec" + integrity sha512-aEDi8MNfKWRnWHM1Mhfx535EtHHYHg6L17PC3rvMUJMgHLcyMQsmI+OMeWt5RzXw1J2fdwhMV0grpp5VDTqRaA== + dependencies: + rrweb-snapshot "^2.0.0-alpha.14" + +rrweb-snapshot@^2.0.0-alpha.13, rrweb-snapshot@^2.0.0-alpha.14: + version "2.0.0-alpha.14" + resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-2.0.0-alpha.14.tgz#114e1be41bfbe783f7b9a088f97d0a292ff7c566" + integrity sha512-HuJd7iZauzhf7XI5FFtCGeUXkHk1mgc8yvH9Km9zB09Yk2cr0bW4eKx9fWQhRFiry9yXf/vOMkUy403xTLPIrQ== + +rrweb@2.0.0-alpha.13: + version "2.0.0-alpha.13" + resolved "https://registry.yarnpkg.com/rrweb/-/rrweb-2.0.0-alpha.13.tgz#37798404acd985212f72544c8823af275fdad514" + integrity sha512-a8GXOCnzWHNaVZPa7hsrLZtNZ3CGjiL+YrkpLo0TfmxGLhjNZbWY2r7pE06p+FcjFNlgUVTmFrSJbK3kO7yxvw== + dependencies: + "@rrweb/types" "^2.0.0-alpha.13" + "@types/css-font-loading-module" "0.0.7" + "@xstate/fsm" "^1.4.0" + base64-arraybuffer "^1.0.1" + fflate "^0.4.4" + mitt "^3.0.0" + rrdom "^2.0.0-alpha.13" + rrweb-snapshot "^2.0.0-alpha.13" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + +scheduler@^0.23.2: + version "0.23.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" + integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== + dependencies: + loose-envify "^1.1.0" + +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.5.4, semver@^7.6.0: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.1, set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4, side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map-js@^1.0.2, source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + +source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: + name string-width-cjs + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.matchall@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" + integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + regexp.prototype.flags "^1.5.2" + set-function-name "^2.0.2" + side-channel "^1.0.6" + +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +styled-jsx@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" + integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw== + dependencies: + client-only "0.0.1" + +sucrase@^3.32.0: + version "3.35.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" + integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "^10.3.10" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tailwind-merge@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.3.0.tgz#27d2134fd00a1f77eca22bcaafdd67055917d286" + integrity sha512-vkYrLpIP+lgR0tQCG6AP7zZXCTLc1Lnv/CCRT3BqJ9CZ3ui2++GPaGb1x/ILsINIMSYqqvrpqjUFsMNLlW99EA== + dependencies: + "@babel/runtime" "^7.24.1" + +tailwindcss-animate@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4" + integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA== + +tailwindcss@^3.4.1: + version "3.4.3" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.3.tgz#be48f5283df77dfced705451319a5dffb8621519" + integrity sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A== + dependencies: + "@alloc/quick-lru" "^5.2.0" + arg "^5.0.2" + chokidar "^3.5.3" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.3.0" + glob-parent "^6.0.2" + is-glob "^4.0.3" + jiti "^1.21.0" + lilconfig "^2.1.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.23" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.1" + postcss-nested "^6.0.1" + postcss-selector-parser "^6.0.11" + resolve "^1.22.2" + sucrase "^3.32.0" + +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +third-party-capital@1.0.20: + version "1.0.20" + resolved "https://registry.yarnpkg.com/third-party-capital/-/third-party-capital-1.0.20.tgz#e218a929a35bf4d2245da9addb8ab978d2f41685" + integrity sha512-oB7yIimd8SuGptespDAZnNkzIz+NWaJCu2RMsbs4Wmp9zSDUM8Nhi3s2OOcqYuv3mN4hitXc8DVx+LyUmbUDiA== + +tiny-invariant@^1.1.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" + integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +traverse-chain@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" + integrity sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg== + +ts-api-utils@^1.0.1, ts-api-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^2.4.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + +typescript@^5: + version "5.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +use-sync-external-store@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + +util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-builtin-type@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" + integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + dependencies: + function.prototype.name "^1.1.5" + has-tostringtag "^1.0.0" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.0.2" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +which-collection@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + +which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yaml@^2.3.4: + version "2.4.3" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.3.tgz#0777516b8c7880bcaa0f426a5410e8d6b0be1f3d" + integrity sha512-sntgmxj8o7DE7g/Qi60cqpLBA3HG3STcDA0kO+WfB05jEKhZMbY7umNm2rBpQvsmZ16/lPXCJGW2672dgOUkrg== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zustand@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.2.tgz#fddbe7cac1e71d45413b3682cdb47b48034c3848" + integrity sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g== + dependencies: + use-sync-external-store "1.2.0" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..0b7760e78 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,49 @@ +services: + postgresql: + image: postgres:14.2-alpine + environment: + POSTGRES_USER: pandasai + POSTGRES_PASSWORD: password123 + POSTGRES_DB: pandasai-db + ports: + - "5430:5432" + volumes: + - ./pgdata:/var/lib/postgresql/data + networks: + - pandabi-network + + server: + container_name: pandabi-backend + build: + context: ./server + dockerfile: Dockerfile + ports: + - "8000:8000" + restart: always + env_file: + - ./server/.env + depends_on: + - postgresql + networks: + - pandabi-network + command: "/bin/bash startup.sh" + + client: + container_name: pandabi-frontend + build: + context: ./client + dockerfile: Dockerfile + ports: + - "3000:3000" + restart: always + env_file: + - ./client/.env + environment: + - NODE_ENV=development + command: npm run start + networks: + - pandabi-network + +networks: + pandabi-network: + driver: bridge diff --git a/docs/contributing.mdx b/docs/contributing.mdx index 66e8e2b46..11f23c0e0 100644 --- a/docs/contributing.mdx +++ b/docs/contributing.mdx @@ -1 +1,74 @@ -{!CONTRIBUTING.md!} +# 🐼 Contributing to PandasAI + +Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. + +## 🤝 How to submit a contribution + +To make a contribution, follow the following steps: + +1. Fork and clone this repository +2. Do the changes on your fork +3. If you modified the code (new feature or bug-fix), please add tests for it +4. Check the linting [see below](https://github.com/gventuri/pandas-ai/blob/main/CONTRIBUTING.md#-linting) +5. Ensure that all tests pass [see below](https://github.com/gventuri/pandas-ai/blob/main/CONTRIBUTING.md#-testing) +6. Submit a pull request + +For more details about pull requests, please read [GitHub's guides](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). + +### 📦 Package manager + +We use `poetry` as our package manager. You can install poetry by following the instructions [here](https://python-poetry.org/docs/#installation). + +Please DO NOT use pip or conda to install the dependencies. Instead, use poetry: + +```bash +poetry install --all-extras --with dev +``` + +### 📌 Pre-commit + +To ensure our standards, make sure to install pre-commit before starting to contribute. + +```bash +pre-commit install +``` + +### 🧹 Linting + +We use `ruff` to lint our code. You can run the linter by running the following command: + +```bash +make format_diff +``` + +Make sure that the linter does not report any errors or warnings before submitting a pull request. + +### Code Format with `ruff-format` + +We use `ruff` to reformat the code by running the following command: + +```bash +make format +``` + +### Spell check + +We usee `codespell` to check the spelling of our code. You can run codespell by running the following command: + +```bash +make spell_fix +``` + +### 🧪 Testing + +We use `pytest` to test our code. You can run the tests by running the following command: + +```bash +make tests +``` + +Make sure that all tests pass before submitting a pull request. + +## 🚀 Release Process + +At the moment, the release process is manual. We try to make frequent releases. Usually, we release a new version when we have a new feature or bugfix. A developer with admin rights to the repository will create a new release on GitHub, and then publish the new version to PyPI. diff --git a/docs/examples.mdx b/docs/examples.mdx index 1f888a40a..5b7751cb9 100644 --- a/docs/examples.mdx +++ b/docs/examples.mdx @@ -209,7 +209,7 @@ response = sdf.chat( "Plot the histogram of countries showing for each the gpd, using different colors for each bar", ) print(response) -# Output: check out images/histogram-chart.png +# Output: check out assets/histogram-chart.png ``` ## Saving Plots with User Defined Path diff --git a/docs/intro.mdx b/docs/intro.mdx index 7bf7ffdaf..07ae23b08 100644 --- a/docs/intro.mdx +++ b/docs/intro.mdx @@ -1,9 +1,9 @@ --- -title: "PandasAI" +title: "Introduction to PandasAI" description: "PandasAI is a Python library that makes it easy to ask questions to your data in natural language." --- -# ![PandasAI](https://github.com/Sinaptik-AI/pandas-ai/blob/main/images/logo.png?raw=true) +# ![PandasAI](https://github.com/Sinaptik-AI/pandas-ai/blob/main/assets/logo.png?raw=true) Beyond querying, PandasAI offers functionalities to visualize data through graphs, cleanse datasets by addressing missing values, and enhance data quality through feature generation, making it a comprehensive tool for data scientists and analysts. @@ -25,17 +25,29 @@ PandasAI is designed for data scientists, analysts, and engineers who want to in ## How to get started with PandasAI? -To get started with PandasAI, you first need to install it. You can do this by running the following command: +PandasAI is available as a Python library and a web-based platform. You can install the library using pip or poetry and use it in your Python code. You can also use the web-based platform to interact with your data in a more visual way. -```bash -# Using poetry (recommended) -poetry add pandasai +### ☁️ Using the platform -# Using pip -pip install pandasai -``` +The PandasAI platform provides a web-based interface for interacting with your data in a more visual way. You can ask questions to your data in natural language, generate graphs and charts to visualize your data, and cleanse datasets by addressing missing values. It uses FastAPI as the backend and NextJS as the frontend. + + + +If you want to learn more how to start the platform on your local machine, you can check out the [platform documentation](platform.mdx). + +### 📚 Using the library -Once you have installed PandasAI, you can start using it by importing the `SmartDataframe` class and instantiating it with your data. You can then use the `chat` method to ask questions to your data in natural language. +The PandasAI library provides a Python interface for interacting with your data in natural language. You can use it to ask questions to your data, generate graphs and charts, cleanse datasets, and enhance data quality through feature generation. It uses LLMs to understand and interpret natural language queries and translate them into python code and SQL queries. + +Once you have installed PandasAI, you can start using it by importing the `Agent` class and instantiating it with your data. You can then use the `chat` method to ask questions to your data in natural language. ```python import os @@ -58,11 +70,7 @@ agent.chat('Which are the top 5 countries by sales?') # China, United States, Japan, Germany, Australia ``` -## Demo - -Try out PandasAI yourself in your browser: - -[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1ZnO-njhL7TBOYPZaqvMvGtsjckZKrv2E?usp=sharing) +If you want to learn more about how to use the library, you can check out the [library documentation](library.mdx). ## Support diff --git a/docs/getting-started.mdx b/docs/library.mdx similarity index 98% rename from docs/getting-started.mdx rename to docs/library.mdx index be699fb72..e15217021 100644 --- a/docs/getting-started.mdx +++ b/docs/library.mdx @@ -210,8 +210,8 @@ To customize PandasAI's `SmartDataframe`, you can either pass a `config` object Settings: -- `llm`: the LLM to use. You can pass an instance of an LLM or the name of an LLM. You can use one of the LLMs supported. You can find more information about LLMs [here](./LLMs/llms.md). -- `llm_options`: the options to use for the LLM (for example the api token, etc). You can find more information about the settings [here](./LLMs/llms.md). +- `llm`: the LLM to use. You can pass an instance of an LLM or the name of an LLM. You can use one of the LLMs supported. You can find more information about LLMs [here](llms.mdx) +- `llm_options`: the options to use for the LLM (for example the api token, etc). You can find more information about the settings [here](llms.mdx). - `save_logs`: whether to save the logs of the LLM. Defaults to `True`. You will find the logs in the `pandasai.log` file in the root of your project. - `verbose`: whether to print the logs in the console as PandasAI is executed. Defaults to `False`. - `enforce_privacy`: whether to enforce privacy. Defaults to `False`. If set to `True`, PandasAI will not send any data to the LLM, but only the metadata. By default, PandasAI will send 5 samples that are anonymized to improve the accuracy of the results. @@ -221,7 +221,7 @@ Settings: - `enable_cache`: whether to enable caching. Defaults to `True`. If set to `True`, PandasAI will cache the results of the LLM to improve the response time. If set to `False`, PandasAI will always call the LLM. - `use_error_correction_framework`: whether to use the error correction framework. Defaults to `True`. If set to `True`, PandasAI will try to correct the errors in the code generated by the LLM with further calls to the LLM. If set to `False`, PandasAI will not try to correct the errors in the code generated by the LLM. - `max_retries`: the maximum number of retries to use when using the error correction framework. Defaults to `3`. You can use this setting to override the default number of retries. -- `custom_whitelisted_dependencies`: the custom whitelisted dependencies to use. Defaults to `{}`. You can use this setting to override the default custom whitelisted dependencies. You can find more information about custom whitelisted dependencies [here](custom-whitelisted-dependencies.md). +- `custom_whitelisted_dependencies`: the custom whitelisted dependencies to use. Defaults to `{}`. You can use this setting to override the default custom whitelisted dependencies. You can find more information about custom whitelisted dependencies [here](custom-whitelisted-dependencies.mdx). ## Demo in Google Colab @@ -231,4 +231,4 @@ Try out PandasAI in your browser: ## Other Examples -You can find all the other examples [here](examples.md). +You can find all the other examples [here](examples.mdx). diff --git a/docs/license.mdx b/docs/license.mdx index 84f33f7c6..46dd3ae6b 100644 --- a/docs/license.mdx +++ b/docs/license.mdx @@ -1 +1,25 @@ -{!LICENSE!} +Copyright (c) 2023 Sinaptik GmbH + +Portions of this software are licensed as follows: + +- All content that resides under any "pandasai/ee/" directory of this repository, if such directories exists, are licensed under the license defined in "pandasai/ee/LICENSE". +- All third party components incorporated into the PandasAI Software are licensed under the original license provided by the owner of the applicable component. +- Content outside of the above mentioned directories or restrictions above is available under the "MIT Expat" license as defined below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docs/mint.json b/docs/mint.json index e49a158ab..0cb659455 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -17,8 +17,19 @@ "pages": ["intro", "getting-started"] }, { - "group": "Essentials", - "pages": ["connectors", "llms", "pipelines"] + "group": "Platform", + "pages": ["platform"] + }, + { + "group": "Library", + "pages": [ + "library", + "connectors", + "llms", + "pipelines", + "advanced-usage", + "examples" + ] }, { "group": "Advanced agents", @@ -37,10 +48,6 @@ "determinism" ] }, - { - "group": "Examples", - "pages": ["examples"] - }, { "group": "About", "pages": ["contributing", "license"] diff --git a/docs/platform.mdx b/docs/platform.mdx new file mode 100644 index 000000000..97a5c5c85 --- /dev/null +++ b/docs/platform.mdx @@ -0,0 +1,91 @@ +--- +title: "Getting started" +description: "A comprehensive guide on configuring, and using the PandasAI dockerized UI platform." +--- + +# Using the Dockerized Platform + +PandasAI provides a dockerized client-server architecture for easy deployment and local usage that adds a simple UI for conversational data analysis. This guide will walk you through the steps to set up and run the PandasAI platform on your local machine. + + + +## Prerequisites + +Before you begin, ensure you have the following installed on your system: + +- Docker +- Docker Compose + +**Note**: By default the platform will interact with the csv files located in the `server/data` directory. You can add your own csv files to this directory before running the platform and the platform will automatically detect them and make them available for querying. Make sure you replace the existing files with your own files if you want to use your own data. + +## Step-by-Step Installation Instructions + +1. Clone the PandasAI repository: + + ```bash + git clone https://github.com/sinaptik-ai/pandas-ai/ + cd pandas-ai + ``` + +2. Copy thee `.env.example` file to `.env` in the client and server directories: + + ```bash + cp client/.env.example client/.env + cp server/.env.example server/.env + ``` + +3. Edit the `.env` files and update the `PANDASAI_API_KEY` with your API key: + + ```bash + # Declare the API key + API_KEY="YOUR_PANDASAI_API_KEY" + + # Update the server/.env file + sed -i "" "s/^PANDASAI_API_KEY=.*/PANDASAI_API_KEY=${API_KEY}/" server/.env + ``` + + Replace `YOUR_PANDASAI_API_KEY` with your PandasAI API key. You can get your free API key by signing up at [PandasAI](https://pandabi.ai). + +4. Build the Docker images: + + ```bash + docker-compose build + ``` + +## Running the Platform + +Once you have built the platform, you can run it with: + +```bash +docker-compose up +``` + +### Accessing the Client and Server + +After deployment, the client can be accessed at `http://localhost:3000`, and the server will be available at `http://localhost:8000`. + +## Troubleshooting Tips + +- If you encounter any issues during the deployment process, ensure Docker and Docker Compose are correctly installed and up to date. +- Check the Docker container logs for any error messages: + ```bash + docker-compose logs + ``` + +## Understanding the `docker-compose.yml` File + +The `docker-compose.yml` file outlines the services required for the dockerized platform, including the client and server. Here's a brief overview of the service configurations: + +- `postgresql`: Configures the PostgreSQL database used by the server. +- `server`: Builds and runs the PandasAI server. +- `client`: Builds and runs the PandasAI client interface. + +For detailed information on each service configuration, refer to the comments within the `docker-compose.yml` file. diff --git a/examples/show_chart.py b/examples/show_chart.py index a29afc9b7..992246c89 100644 --- a/examples/show_chart.py +++ b/examples/show_chart.py @@ -18,4 +18,4 @@ "Plot the histogram of countries showing for each the gpd," " using different colors for each bar", ) -# Output: check out images/histogram-chart.png +# Output: check out assets/histogram-chart.png diff --git a/ignore-words.txt b/ignore-words.txt new file mode 100644 index 000000000..877e68a84 --- /dev/null +++ b/ignore-words.txt @@ -0,0 +1,2 @@ +# ignore-words.txt +selectin \ No newline at end of file diff --git a/server/.env.example b/server/.env.example new file mode 100644 index 000000000..488b20309 --- /dev/null +++ b/server/.env.example @@ -0,0 +1,10 @@ +# Database +POSTGRES_URL=postgresql+asyncpg://pandasai:password123@postgresql:5432/pandasai-db +TEST_POSTGRES_URL=postgresql+asyncpg://pandasai:password123@postgresql:5432/pandasai-db +PANDASAI_API_KEY= +# OPENAI_API_KEY= use only in case you want to use OpenAI else use PANDASAI_API_KEY + +# Environment +ENVIRONMENT=development +DEBUG=1 +SHOW_SQL_ALCHEMY_QUERIES=0 diff --git a/server/.gitattributes b/server/.gitattributes new file mode 100644 index 000000000..dfe077042 --- /dev/null +++ b/server/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 000000000..9c1f606e3 --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,166 @@ +# Custom +.vscode/ +pgdata/ +postgresql-test/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ +.DS_Store diff --git a/server/.pylintrc b/server/.pylintrc new file mode 100644 index 000000000..fd37b1eb3 --- /dev/null +++ b/server/.pylintrc @@ -0,0 +1,19 @@ +[BASIC] +extension-pkg-whitelist=pydantic +good-names=i, j, k, q, db +max-args=10 + +[FORMAT] +max-line-length = 79 + +[MESSAGE CONTROL] +disable = missing-module-docstring, + missing-class-docstring, + missing-function-docstring, + fixme, + too-few-public-methods, + line-too-long, + unused-argument, + c-extension-no-member, + not-callable, + invalid-name, diff --git a/server/.tool-versions b/server/.tool-versions new file mode 100644 index 000000000..bc91fdd0d --- /dev/null +++ b/server/.tool-versions @@ -0,0 +1 @@ +python 3.11.1 diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 000000000..ff4fc8665 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,46 @@ +# Use an official Python runtime as a parent image +FROM python:3.11-slim + +# Set the working directory in the container +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + curl \ + make \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Install Poetry +RUN curl -sSL https://install.python-poetry.org | python3 - + +RUN apt-get update && apt-get install netcat-openbsd -y + +# Add Poetry to PATH +ENV PATH="/root/.local/bin:$PATH" + +# Copy the current directory contents into the container at /app +COPY . /app + +RUN poetry lock --no-update + +# Install Python dependencies +RUN poetry install --no-root + +# Add wait-for-it.sh script to the container +COPY wait-for-it.sh /wait-for-it.sh + +RUN chmod +x /wait-for-it.sh + +# Make port 8000 available to the world outside this container +EXPOSE 8000 + +# Copy entrypoint script to the container +COPY startup.sh /startup.sh +RUN chmod +x /startup.sh + +# RUN dos2unix startup.sh + +# Run the entrypoint script +CMD ["/startup.sh"] \ No newline at end of file diff --git a/server/Makefile b/server/Makefile new file mode 100644 index 000000000..1ac292df1 --- /dev/null +++ b/server/Makefile @@ -0,0 +1,97 @@ +# Build configuration +# ------------------- + +APP_NAME := $(shell sed -n 's/^ *name.*=.*"\([^"]*\)".*/\1/p' pyproject.toml) +APP_VERSION := $(shell sed -n 's/^ *version.*=.*"\([^"]*\)".*/\1/p' pyproject.toml) +GIT_REVISION = $(shell git rev-parse HEAD) + +# Introspection targets +# --------------------- + +.PHONY: help +help: header targets + +.PHONY: header +header: + @echo "\033[34mEnvironment\033[0m" + @echo "\033[34m---------------------------------------------------------------\033[0m" + @printf "\033[33m%-23s\033[0m" "APP_NAME" + @printf "\033[35m%s\033[0m" $(APP_NAME) + @echo "" + @printf "\033[33m%-23s\033[0m" "APP_VERSION" + @printf "\033[35m%s\033[0m" $(APP_VERSION) + @echo "" + @printf "\033[33m%-23s\033[0m" "GIT_REVISION" + @printf "\033[35m%s\033[0m" $(GIT_REVISION) + @echo "\n" + +.PHONY: targets +targets: + @echo "\033[34mDevelopment Targets\033[0m" + @echo "\033[34m---------------------------------------------------------------\033[0m" + @perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-22s\033[0m %s\n", $$1, $$2}' + +# Development targets +# ------------- + +.PHONY: install +install: ## Install dependencies + poetry install + +.PHONY: run +run: start + +.PHONY: start +start: ## Starts the server + @eval "$(shell sed 's/=.*//' .env | xargs echo export)" + poetry run python main.py + +.PHONY: migrate +migrate: ## Run the migrations + @eval "$(shell sed 's/=.*//' .env | xargs echo export)" + poetry run alembic upgrade head + +.PHONY: rollback +rollback: ## Rollback migrations one level + @eval "$(shell sed 's/=.*//' .env | xargs echo export)" + poetry run alembic downgrade -1 + +.PHONY: reset-database +reset-database: ## Rollback all migrations + @eval "$(shell sed 's/=.*//' .env | xargs echo export)" + poetry run alembic downgrade base + +.PHONY: generate-migration +generate-migration: ## Generate a new migration + @eval "$(shell sed 's/=.*//' .env | xargs echo export)" + @read -p "Enter migration message: " message; \ + poetry run alembic revision --autogenerate -m "$$message" + +# Check, lint and format targets +# ------------------------------ + +.PHONY: check +check: check-format lint + +.PHONY: check-format +check-format: ## Dry-run code formatter + poetry run black ./ --check + poetry run isort ./ --profile black --check + +.PHONY: lint +lint: ## Run linter + poetry run pylint ./api ./app ./core + +.PHONY: format +format: ## Run code formatter + poetry run black ./ + poetry run isort ./ --profile black + +.PHONY: check-lockfile +check-lockfile: ## Compares lock file with pyproject.toml + poetry lock --check + +.PHONY: test +test: ## Run the test suite + @eval "$(shell sed 's/=.*//' .env | xargs echo export)" + poetry run pytest -vv -rs --cache-clear ./ diff --git a/server/README.md b/server/README.md new file mode 100644 index 000000000..b44d631d6 --- /dev/null +++ b/server/README.md @@ -0,0 +1,33 @@ +## PandasAI Server + +## Run + +### Prerequisites + +- Make sure postgres is already installed +- Create an env file use .env.example + +### Install dependency + +```shell +> poetry shell +> make install +``` + +### Apply database migration + +```shell +> make migrate +``` + +### Create new database migration after schema changes + +```shell +> make generate-migration +``` + +### Start Server + +```shell +> make start +``` diff --git a/server/alembic.ini b/server/alembic.ini new file mode 100644 index 000000000..6fabec4fc --- /dev/null +++ b/server/alembic.ini @@ -0,0 +1,85 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = migrations + +# template used to generate migration files +file_template = %%(year)d%%(month).2d%%(day).2d%%(hour).2d%%(minute).2d%%(second).2d_%%(slug)s + +# timezone to use when rendering the date +# within the migration file as well as the filename. +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; this defaults +# to migrations/versions. When using multiple version +# directories, initial revisions must be specified with --version-path +# version_locations = %(here)s/bar %(here)s/bat migrations/versions + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +# sqlalchemy.url = + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks=black +# black.type=console_scripts +# black.entrypoint=black +# black.options=-l 79 + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/server/api/__init__.py b/server/api/__init__.py new file mode 100644 index 000000000..df6cd3bc4 --- /dev/null +++ b/server/api/__init__.py @@ -0,0 +1,9 @@ +from fastapi import APIRouter + +from .v1 import v1_router + +router = APIRouter() +router.include_router(v1_router, prefix="/v1") + + +__all__ = ["router"] diff --git a/server/api/v1/__init__.py b/server/api/v1/__init__.py new file mode 100644 index 000000000..8fb85bfd0 --- /dev/null +++ b/server/api/v1/__init__.py @@ -0,0 +1,20 @@ +from fastapi import APIRouter + +from .chat import chat_router +from .monitoring import monitoring_router +from .users import users_router +from .datasets import datasets_router +from .conversations import conversation_router +from .workspace import workspaces_router + +v1_router = APIRouter() +v1_router.include_router(monitoring_router, prefix="/monitoring") +v1_router.include_router(users_router, prefix="/users") +v1_router.include_router(chat_router, prefix="/chat", tags=["Chat"]) +v1_router.include_router(datasets_router, prefix="/dataset", tags=["Dataset"]) +v1_router.include_router( + conversation_router, prefix="/conversations", tags=["Conversations"] +) +v1_router.include_router( + workspaces_router, prefix="/workspace", tags=["Workspace"] +) diff --git a/server/api/v1/chat/__init__.py b/server/api/v1/chat/__init__.py new file mode 100644 index 000000000..4b60d7449 --- /dev/null +++ b/server/api/v1/chat/__init__.py @@ -0,0 +1,8 @@ +from fastapi import APIRouter + +from .chat import chat_router + +users_router = APIRouter() +users_router.include_router(chat_router, tags=["Chat"]) + +__all__ = ["chat_router"] diff --git a/server/api/v1/chat/chat.py b/server/api/v1/chat/chat.py new file mode 100644 index 000000000..90fa6eb22 --- /dev/null +++ b/server/api/v1/chat/chat.py @@ -0,0 +1,21 @@ +from fastapi import APIRouter, Depends + +from app.controllers.chat import ChatController +from app.schemas.requests.chat import ChatRequest +from app.schemas.responses import APIResponse +from app.schemas.responses.chat import ChatResponse +from app.schemas.responses.users import UserInfo +from core.factory import Factory +from core.fastapi.dependencies.current_user import get_current_user + +chat_router = APIRouter() + + +@chat_router.post("/") +async def chat( + chat_request: ChatRequest, + chat_controller: ChatController = Depends(Factory().get_chat_controller), + user: UserInfo = Depends(get_current_user), +) -> APIResponse[ChatResponse]: + response = await chat_controller.chat(user, chat_request) + return APIResponse(data=response, message="Chat response returned successfully!") diff --git a/server/api/v1/conversations/__init__.py b/server/api/v1/conversations/__init__.py new file mode 100644 index 000000000..c3249dfa1 --- /dev/null +++ b/server/api/v1/conversations/__init__.py @@ -0,0 +1,8 @@ +from fastapi import APIRouter + +from .conversations import conversation_router + +users_router = APIRouter() +users_router.include_router(conversation_router, tags=["Conversation"]) + +__all__ = ["conversation_router"] diff --git a/server/api/v1/conversations/conversations.py b/server/api/v1/conversations/conversations.py new file mode 100644 index 000000000..a055d5641 --- /dev/null +++ b/server/api/v1/conversations/conversations.py @@ -0,0 +1,63 @@ +from typing import Optional +from fastapi import APIRouter, Depends, Path, Query + +from app.controllers.conversation import ConversationController +from app.schemas.responses import APIResponse + +from app.schemas.responses.conversation import ConversationList, ConversationMessageList +from app.schemas.responses.users import UserInfo +from core.factory import Factory +from core.fastapi.dependencies.current_user import get_current_user +from uuid import UUID + +conversation_router = APIRouter() + + +@conversation_router.get("/") +async def conversations( + conversation_controller: ConversationController = Depends( + Factory().get_conversation_controller + ), + user: UserInfo = Depends(get_current_user), + skip: Optional[int] = Query(0, description="Number of items to skip"), + limit: Optional[int] = Query(10, description="Number of items to retrieve"), +) -> APIResponse[ConversationList]: + response = await conversation_controller.get_workspace_conversations( + user.id, user.space.id, skip, limit + ) + return APIResponse( + data=response, message="User conversations returned successfully!" + ) + + +@conversation_router.get("/{conv_id}/messages") +async def conversation_messages( + conv_id: UUID = Path(..., description="ID of the conversation"), + conversation_controller: ConversationController = Depends( + Factory().get_conversation_controller + ), + skip: Optional[int] = Query(0, description="Number of items to skip"), + limit: Optional[int] = Query(10, description="Number of items to retrieve"), +) -> APIResponse[ConversationMessageList]: + response = await conversation_controller.get_conversation_messages( + conv_id, skip, limit + ) + return APIResponse( + data=response, message="User conversation messages returned successfully!" + ) + +@conversation_router.delete("/{conv_id}") +async def delete_conversation( + conv_id: UUID = Path(..., description="ID of the conversation"), + conversation_controller: ConversationController = Depends( + Factory().get_conversation_controller + ), + user: UserInfo = Depends(get_current_user), + +): + response = await conversation_controller.archive_conversation( + conv_id, user.id + ) + return APIResponse( + data=response, message="Conversation archived successfully" + ) \ No newline at end of file diff --git a/server/api/v1/datasets/__init__.py b/server/api/v1/datasets/__init__.py new file mode 100644 index 000000000..246c6f974 --- /dev/null +++ b/server/api/v1/datasets/__init__.py @@ -0,0 +1,8 @@ +from fastapi import APIRouter + +from .datasets import dataset_router + +datasets_router = APIRouter() +datasets_router.include_router(dataset_router, tags=["Dataset"]) + +__all__ = ["datasets_router"] diff --git a/server/api/v1/datasets/datasets.py b/server/api/v1/datasets/datasets.py new file mode 100644 index 000000000..9b7bbaa78 --- /dev/null +++ b/server/api/v1/datasets/datasets.py @@ -0,0 +1,10 @@ +from fastapi import APIRouter, Depends, Path +from app.controllers.datasets import DatasetController +from app.schemas.responses.datasets import WorkspaceDatasetsResponseModel +from core.factory import Factory + +dataset_router = APIRouter() + +@dataset_router.get("/", response_model=WorkspaceDatasetsResponseModel) +async def get_datasets(datasets_controller: DatasetController = Depends(Factory().get_datasets_controller)): + return await datasets_controller.get_all_datasets() diff --git a/server/api/v1/monitoring/__init__.py b/server/api/v1/monitoring/__init__.py new file mode 100644 index 000000000..0ecfc6067 --- /dev/null +++ b/server/api/v1/monitoring/__init__.py @@ -0,0 +1,8 @@ +from fastapi import APIRouter + +from .health import health_router + +monitoring_router = APIRouter() +monitoring_router.include_router(health_router, prefix="/health", tags=["Health"]) + +__all__ = ["monitoring_router"] diff --git a/server/api/v1/monitoring/health.py b/server/api/v1/monitoring/health.py new file mode 100644 index 000000000..0e6fbfc10 --- /dev/null +++ b/server/api/v1/monitoring/health.py @@ -0,0 +1,12 @@ +from fastapi import APIRouter, Depends + +from app.schemas.extras.health import Health +from core.config import config +from core.fastapi.dependencies.authentication import AuthenticationRequired + +health_router = APIRouter() + + +@health_router.get("/", dependencies=[Depends(AuthenticationRequired)]) +async def health() -> Health: + return Health(version=config.RELEASE_VERSION, status="Healthy") diff --git a/server/api/v1/users/__init__.py b/server/api/v1/users/__init__.py new file mode 100644 index 000000000..fc48aed5e --- /dev/null +++ b/server/api/v1/users/__init__.py @@ -0,0 +1,8 @@ +from fastapi import APIRouter + +from .users import user_router + +users_router = APIRouter() +users_router.include_router(user_router, tags=["Users"]) + +__all__ = ["users_router"] diff --git a/server/api/v1/users/users.py b/server/api/v1/users/users.py new file mode 100644 index 000000000..2c3abc4d9 --- /dev/null +++ b/server/api/v1/users/users.py @@ -0,0 +1,27 @@ +from fastapi import APIRouter, Depends + +from app.controllers import AuthController +from app.controllers.user import UserController +from app.schemas.extras.token import Token +from app.schemas.requests.users import LoginUserRequest +from app.schemas.responses.users import UserInfo +from core.factory import Factory + +user_router = APIRouter() + + +@user_router.post("/login") +async def login_user( + login_user_request: LoginUserRequest, + auth_controller: AuthController = Depends(Factory().get_auth_controller), +) -> Token: + return await auth_controller.login( + email=login_user_request.email, password=login_user_request.password + ) + + +@user_router.get("/me") +async def get_user( + user_controller: UserController = Depends(Factory().get_user_controller), +) -> UserInfo: + return await user_controller.me() diff --git a/server/api/v1/workspace/__init__.py b/server/api/v1/workspace/__init__.py new file mode 100644 index 000000000..0222bb617 --- /dev/null +++ b/server/api/v1/workspace/__init__.py @@ -0,0 +1,8 @@ +from fastapi import APIRouter + +from .workspace import workspace_router + +workspaces_router = APIRouter() +workspaces_router.include_router(workspace_router, tags=["Workspace"]) + +__all__ = ["workspace_router"] diff --git a/server/api/v1/workspace/workspace.py b/server/api/v1/workspace/workspace.py new file mode 100644 index 000000000..b7b82ab86 --- /dev/null +++ b/server/api/v1/workspace/workspace.py @@ -0,0 +1,28 @@ +from fastapi import APIRouter, Depends, Path +from uuid import UUID + +from app.controllers.workspace import WorkspaceController +from core.factory import Factory +from app.schemas.responses.users import UserInfo +from core.fastapi.dependencies.current_user import get_current_user +from app.schemas.responses.users import WorkspaceUsersResponse +from app.schemas.responses.datasets import WorkspaceDatasetsResponseModel + +workspace_router = APIRouter() + + +@workspace_router.get("/{workspace_id}/users", response_model=WorkspaceUsersResponse) +async def get_workspace_users( + workspace_id: UUID = Path(..., description="ID of the workspace"), + workspace_controller: WorkspaceController = Depends( + Factory().get_space_controller + ), + user: UserInfo = Depends(get_current_user)): + return await workspace_controller.get_workspace_users(workspace_id) + +@workspace_router.get("/{workspace_id}/datasets", response_model=WorkspaceDatasetsResponseModel) +async def get_workspace_datasets( + workspace_id: UUID = Path(..., description="ID of the workspace"), + workspace_controller: WorkspaceController = Depends(Factory().get_space_controller) + ): + return await workspace_controller.get_workspace_datasets(workspace_id) \ No newline at end of file diff --git a/server/app/__init__.py b/server/app/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/app/controllers/__init__.py b/server/app/controllers/__init__.py new file mode 100644 index 000000000..c0fb36f31 --- /dev/null +++ b/server/app/controllers/__init__.py @@ -0,0 +1,15 @@ +from .auth import AuthController +from .workspace import WorkspaceController +from .user import UserController +from .conversation import ConversationController +from .datasets import DatasetController +from .chat import ChatController + +__all__ = [ + "AuthController", + "UserController", + "WorkspaceController", + "ConversationController", + "DatasetController", + "ChatController", +] diff --git a/server/app/controllers/auth.py b/server/app/controllers/auth.py new file mode 100644 index 000000000..c2d916890 --- /dev/null +++ b/server/app/controllers/auth.py @@ -0,0 +1,64 @@ +from pydantic import EmailStr + +from app.models import User +from app.repositories import UserRepository +from app.schemas.extras.token import Token +from core.controller import BaseController +from core.database import Propagation, Transactional +from core.exceptions import BadRequestException, UnauthorizedException +from core.security import JWTHandler, PasswordHandler + + +class AuthController(BaseController[User]): + def __init__(self, user_repository: UserRepository): + super().__init__(model=User, repository=user_repository) + self.user_repository = user_repository + + @Transactional(propagation=Propagation.REQUIRED) + async def register(self, email: EmailStr, password: str, username: str) -> User: + # Check if user exists with email + user = await self.user_repository.get_by_email(email) + + if user: + raise BadRequestException("A user with this email already exists") + + # Check if user exists with username + user = await self.user_repository.get_by_username(username) + + if user: + raise BadRequestException("A user with this username already exists") + + password = PasswordHandler.hash(password) + + return await self.user_repository.create( + { + "email": email, + "password": password, + "username": username, + } + ) + + async def login(self, email: EmailStr, password: str) -> Token: + user = await self.user_repository.get_by_email(email) + + if not user: + raise BadRequestException("Invalid credentials") + + if not PasswordHandler.verify(user.password, password): + raise BadRequestException("Invalid credentials") + + return Token( + access_token=JWTHandler.encode(payload={"user_id": user.id}), + refresh_token=JWTHandler.encode(payload={"sub": "refresh_token"}), + ) + + async def refresh_token(self, access_token: str, refresh_token: str) -> Token: + token = JWTHandler.decode(access_token) + refresh_token = JWTHandler.decode(refresh_token) + if refresh_token.get("sub") != "refresh_token": + raise UnauthorizedException("Invalid refresh token") + + return Token( + access_token=JWTHandler.encode(payload={"user_id": token.get("user_id")}), + refresh_token=JWTHandler.encode(payload={"sub": "refresh_token"}), + ) diff --git a/server/app/controllers/chat.py b/server/app/controllers/chat.py new file mode 100644 index 000000000..3c6cca071 --- /dev/null +++ b/server/app/controllers/chat.py @@ -0,0 +1,129 @@ +import os +import shutil +from typing import List + +import pandas as pd +from pandasai import Agent +from pandasai.connectors.pandas import PandasConnector +from pandasai.helpers.path import find_project_root +from pandasai.llm.openai import OpenAI + +from app.models import Dataset, User +from app.repositories import UserRepository +from app.repositories.conversation import ConversationRepository +from app.repositories.workspace import WorkspaceRepository +from app.schemas.requests.chat import ChatRequest +from app.schemas.responses.chat import ChatResponse +from app.schemas.responses.users import UserInfo +from app.utils.memory import prepare_conv_memory +from core.constants import CHAT_FALLBACK_MESSAGE +from core.controller import BaseController +from core.database.transactional import Propagation, Transactional +from core.utils.dataframe import load_df +from core.utils.json_encoder import jsonable_encoder +from core.utils.response_parser import JsonResponseParser +from core.config import config as env_config + + +class ChatController(BaseController[User]): + def __init__( + self, + user_repository: UserRepository, + space_repository: WorkspaceRepository, + conversation_repository: ConversationRepository, + ): + super().__init__(model=User, repository=user_repository) + self.user_repository = user_repository + self.space_repository = space_repository + self.conversation_repository = conversation_repository + + @Transactional(propagation=Propagation.REQUIRED) + async def start_new_conversation(self, user: UserInfo, chat_request: ChatRequest): + return await self.conversation_repository.create( + { + "workspace_id": chat_request.workspace_id, + "user_id": user.id, + } + ) + + @Transactional(propagation=Propagation.REQUIRED) + async def chat(self, user: UserInfo, chat_request: ChatRequest) -> ChatResponse: + datasets: List[Dataset] = await self.space_repository.get_space_datasets( + chat_request.workspace_id + ) + conversation_id = chat_request.conversation_id + conversation_messages = [] + memory = None + + if not chat_request.conversation_id: + user_conversation = await self.start_new_conversation(user, chat_request) + conversation_id = user_conversation.id + + else: + conversation_messages = ( + await self.conversation_repository.get_conversation_messages( + conversation_id + ) + ) + memory = prepare_conv_memory(conversation_messages) + + connectors = [] + for dataset in datasets: + config = dataset.connector.config + df = pd.read_csv(config["file_path"]) + connector = PandasConnector( + {"original_df": df}, + name=dataset.name, + description=dataset.description, + custom_head=(load_df(dataset.head) if dataset.head else None), + field_descriptions=dataset.field_descriptions, + ) + connectors.append(connector) + + path_plot_directory = find_project_root() + "/exports/" + str(conversation_id) + + config = { + "enable_cache": False, + "response_parser": JsonResponseParser, + "save_charts": True, + "save_charts_path": path_plot_directory, + } + + if env_config.OPENAI_API_KEY: + llm = OpenAI(env_config.OPENAI_API_KEY) + config["llm"] = llm + + agent = Agent(connectors, config=config) + if memory: + agent.context.memory = memory + + response = agent.chat(chat_request.query) + + if os.path.exists(path_plot_directory): + shutil.rmtree(path_plot_directory) + + if isinstance(response, str) and ( + response.startswith("Unfortunately, I was not able to") + ): + return [ + { + "type": "string", + "message": CHAT_FALLBACK_MESSAGE, + "value": CHAT_FALLBACK_MESSAGE, + } + ] + + response = jsonable_encoder([response]) + conversation_message = await self.conversation_repository.add_conversation_message( + conversation_id=conversation_id, + query=chat_request.query, + response=response, + code_generated=agent.last_code_executed, + ) + + return ChatResponse( + response=response, + conversation_id=str(conversation_id), + message_id = str(conversation_message.id), + query = str(conversation_message.query) + ) diff --git a/server/app/controllers/conversation.py b/server/app/controllers/conversation.py new file mode 100644 index 000000000..bc671b633 --- /dev/null +++ b/server/app/controllers/conversation.py @@ -0,0 +1,56 @@ +from fastapi import status, HTTPException +from app.models import User, UserConversation +from app.repositories import UserRepository +from app.repositories.conversation import ConversationRepository + +from app.schemas.responses.conversation import ConversationList, ConversationMessageList +from core.controller import BaseController + + +class ConversationController(BaseController[UserConversation]): + def __init__( + self, + user_repository: UserRepository, + conversation_repository: ConversationRepository, + ): + super().__init__(model=User, repository=user_repository) + self.user_repository = user_repository + self.conversation_repository = conversation_repository + + async def get_workspace_conversations( + self, user_id: str, workspace_id: str, skip: int = 0, limit: int = 100 + ) -> ConversationList: + conversations = await self.conversation_repository.get_conversations( + user_id, workspace_id, skip, limit + ) + + count = await self.conversation_repository.get_count(user_id, workspace_id) + return ConversationList(count=count, conversations=conversations) + + async def get_conversation_messages( + self, conversation_id: str, skip: int = 0, limit: int = 100 + ): + conversation_messages = ( + await self.conversation_repository.get_conversation_messages( + conversation_id, skip, limit, "desc" + ) + ) + + count = await self.conversation_repository.get_messages_count(conversation_id) + + return ConversationMessageList(count=count, messages=list(reversed(conversation_messages))) + + async def archive_conversation( + self, conversation_id: str, user_id: str + ): + user_conversation = ( + await self.conversation_repository.get_by_id(conversation_id) + ) + if not user_conversation: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + message=f"conversation with id: {conversation_id} was not found") + if user_conversation.user_id != user_id: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, + message="Unauthorized to update conversation for this user") + user_conversation.valid = False + await self.conversation_repository.session.commit() \ No newline at end of file diff --git a/server/app/controllers/datasets.py b/server/app/controllers/datasets.py new file mode 100644 index 000000000..71bc56ccc --- /dev/null +++ b/server/app/controllers/datasets.py @@ -0,0 +1,26 @@ +from fastapi import status, HTTPException +from app.models import Dataset +from app.repositories import DatasetRepository, WorkspaceRepository +from core.controller import BaseController +from core.exceptions.base import NotFoundException +from app.schemas.responses.datasets import WorkspaceDatasetsResponseModel +from typing import List +class DatasetController(BaseController[Dataset]): + def __init__( + self, + dataset_repository: DatasetRepository, + space_repository: WorkspaceRepository + ): + super().__init__(model=Dataset, repository=dataset_repository) + self.dataset_repository = dataset_repository + self.space_repository = space_repository + + async def get_all_datasets(self) -> WorkspaceDatasetsResponseModel: + datasets = await self.get_all() + + if not datasets: + raise NotFoundException( + "No dataset found. Please restart the server and try again" + ) + + return WorkspaceDatasetsResponseModel(datasets=datasets) diff --git a/server/app/controllers/user.py b/server/app/controllers/user.py new file mode 100644 index 000000000..dd5df06fc --- /dev/null +++ b/server/app/controllers/user.py @@ -0,0 +1,56 @@ +from app.models import User +from app.repositories import UserRepository +from app.repositories.workspace import WorkspaceRepository +from app.schemas.responses.users import UserInfo, OrganizationBase, SpaceBase +from core.controller import BaseController +from core.database.transactional import Propagation, Transactional +from core.exceptions.base import NotFoundException + + +class UserController(BaseController[User]): + def __init__( + self, user_repository: UserRepository, space_repository: WorkspaceRepository + ): + super().__init__(model=User, repository=user_repository) + self.user_repository = user_repository + self.space_repository = space_repository + + @Transactional(propagation=Propagation.REQUIRED_NEW) + async def create_default_user(self) -> User: + users = await self.get_all(limit=1, join_={"memberships"}) + if not users: + await self.user_repository.create_and_init_dummy_user() + + async def get_by_email(self, email: str) -> User: + return await self.user_repository.get_by_email(email) + + async def me(self) -> UserInfo: + users = await self.get_all(limit=1) + if not users: + raise NotFoundException( + "No user found. Please restart the server and try again" + ) + + user = users[0] + + organizations = [ + OrganizationBase( + id=membership.organization.id, name=membership.organization.name + ) + for membership in user.memberships + ] + + space = await self.space_repository.get_by( + "organization_id", organizations[0].id + ) + space = space[0] + + space_base = SpaceBase(id=space.id, name=space.name, slug=space.slug) + + return UserInfo( + email=user.email, + first_name=user.first_name, + id=user.id, + organizations=organizations, + space=space_base, + ) diff --git a/server/app/controllers/workspace.py b/server/app/controllers/workspace.py new file mode 100644 index 000000000..57b6ee235 --- /dev/null +++ b/server/app/controllers/workspace.py @@ -0,0 +1,72 @@ +from fastapi import status, HTTPException +from typing import List +from app.models import ConnectorType, Workspace, User +from app.repositories.dataset import DatasetRepository +from app.repositories.workspace import WorkspaceRepository +from core.controller import BaseController +from core.database.transactional import Propagation, Transactional +from app.schemas.responses.users import WorkspaceUsersResponse +from app.schemas.responses.datasets import WorkspaceDatasetsResponseModel + +class WorkspaceController(BaseController[Workspace]): + def __init__( + self, + space_repository: WorkspaceRepository, + dataset_repository: DatasetRepository, + ): + super().__init__(model=User, repository=space_repository) + self.space_repository = space_repository + self.dataset_repository = dataset_repository + + @Transactional(propagation=Propagation.REQUIRED_NEW) + async def reset_space_datasets(self, workspace_id: str) -> bool: + await self.space_repository.delete_space_datasets(workspace_id) + return True + + @Transactional(propagation=Propagation.REQUIRED_NEW) + async def add_csv_datasets( + self, datasets: List[dict], user: User, workspace_id: str + ): + for dataset in datasets: + dataset = await self.dataset_repository.create_dataset( + user_id=user.id, + organization_id=user.memberships[0].organization_id, + name=dataset["file_name"], + connector_type=ConnectorType.CSV, + config={ + "file_path": dataset["file_path"], + "file_name": dataset["file_name"], + }, + head=dataset["head"], + ) + await self.space_repository.add_dataset_to_space( + dataset_id=dataset.id, workspace_id=workspace_id + ) + + + async def get_workspace_by_id(self, workspace_id: str): + workspace = await self.space_repository.get_by_id(id=workspace_id) + if not workspace: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Workspace with id: {workspace_id} was not found" + ) + return workspace + + async def get_workspace_users( + self, workspace_id: str, + ): + await self.get_workspace_by_id(workspace_id) + users = await self.space_repository.get_users_by_workspace_id(workspace_id) + return WorkspaceUsersResponse(users=users) + + + async def get_workspace_datasets(self, workspace_id) -> WorkspaceDatasetsResponseModel: + await self.get_workspace_by_id(workspace_id) + datasets = await self.dataset_repository.get_all_by_workspace_id(workspace_id) + if not datasets: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="No dataset found. Please restart the server and try again" + ) + return WorkspaceDatasetsResponseModel(datasets=datasets) \ No newline at end of file diff --git a/server/app/models/__init__.py b/server/app/models/__init__.py new file mode 100644 index 000000000..51b82c1b4 --- /dev/null +++ b/server/app/models/__init__.py @@ -0,0 +1,191 @@ +import datetime +import uuid +from enum import Enum + +from sqlalchemy import ( + JSON, + Boolean, + Column, + DateTime, + ForeignKey, + String, + UniqueConstraint, +) +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import relationship + +from core.database import Base + + +class OrganizationRole: + MEMBER = "MEMBER" + ADMIN = "ADMIN" + OWNER = "OWNER" + + +class DataframeLoadStatus(Enum): + DONE = "DONE" + IN_PROGRESS = "IN_PROGRESS" + FAILED = "FAILED" + + +class ConnectorType(Enum): + CSV = "CSV" + + +class User(Base): + __tablename__ = "user" + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True) + email = Column(String(255), index=True, unique=True) + first_name = Column(String(255), nullable=True) + created_at = Column(DateTime, default=datetime.datetime.now) + password = Column(String(255)) + verified = Column(Boolean, default=False) + last_name = Column(String(255), nullable=True) + + datasets = relationship("Dataset", back_populates="user") + connectors = relationship("Connector", back_populates="user") + memberships = relationship( + "OrganizationMembership", back_populates="user", lazy="selectin" + ) + spaces = relationship("Workspace", back_populates="user") + user_spaces = relationship("UserSpace", back_populates="user") + + +class Organization(Base): + __tablename__ = "organization" + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True) + name = Column(String, index=True) + url = Column(String, nullable=True) + is_default = Column(Boolean, default=False) + settings = Column(JSON, nullable=True) + + api_keys = relationship("APIKeys", back_populates="organization") + datasets = relationship("Dataset", back_populates="organization") + members = relationship( + "OrganizationMembership", back_populates="organization", lazy="selectin" + ) + workspaces = relationship("Workspace", back_populates="organization") + + +class APIKeys(Base): + __tablename__ = "api_keys" + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True) + organization_id = Column(UUID(as_uuid=True), ForeignKey("organization.id")) + api_key = Column(String(255)) + + organization = relationship("Organization", back_populates="api_keys") + + +class OrganizationMembership(Base): + __tablename__ = "organization_membership" + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True) + user_id = Column(UUID(as_uuid=True), ForeignKey("user.id")) + organization_id = Column(UUID(as_uuid=True), ForeignKey("organization.id")) + role = Column(String, default=OrganizationRole.MEMBER) + verified = Column(Boolean, default=True) + + organization = relationship("Organization", back_populates="members", lazy="joined") + user = relationship("User", back_populates="memberships", lazy="joined") + + +class Dataset(Base): + __tablename__ = "dataset" + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True) + name = Column(String) + table_name = Column(String) + description = Column(String, nullable=True) + created_at = Column(DateTime, default=datetime.datetime.now) + head = Column(JSON, nullable=True) + user_id = Column(UUID(as_uuid=True), ForeignKey("user.id")) + organization_id = Column(UUID(as_uuid=True), ForeignKey("organization.id")) + connector_id = Column(UUID(as_uuid=True), ForeignKey("connector.id")) + field_descriptions = Column(JSON, nullable=True) + filterable_columns = Column(JSON, nullable=True) + + user = relationship("User", back_populates="datasets") + organization = relationship("Organization", back_populates="datasets") + connector = relationship("Connector", back_populates="datasets", lazy="joined") + dataset_spaces = relationship("DatasetSpace", back_populates="dataset") + + +class Connector(Base): + __tablename__ = "connector" + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True) + type = Column(String, nullable=False) + config = Column(JSON, nullable=True) + created_at = Column(DateTime, default=datetime.datetime.now) + user_id = Column(UUID(as_uuid=True), ForeignKey("user.id")) + + user = relationship("User", back_populates="connectors") + datasets = relationship("Dataset", back_populates="connector") + + __table_args__ = (UniqueConstraint("id", name="uq_connector_id"),) + + +class Workspace(Base): + __tablename__ = "workspace" + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True) + name = Column(String) + user_id = Column(UUID(as_uuid=True), ForeignKey("user.id")) + organization_id = Column(UUID(as_uuid=True), ForeignKey("organization.id")) + slug = Column(String, nullable=True) + created_at = Column(DateTime, default=datetime.datetime.now) + + organization = relationship("Organization", back_populates="workspaces") + user = relationship("User", back_populates="spaces") + dataset_spaces = relationship("DatasetSpace", back_populates="workspace") + user_spaces = relationship("UserSpace", back_populates="workspace") + + +class UserSpace(Base): + __tablename__ = "user_space" + workspace_id = Column( + UUID(as_uuid=True), ForeignKey("workspace.id"), primary_key=True + ) + user_id = Column(UUID(as_uuid=True), ForeignKey("user.id"), primary_key=True) + + workspace = relationship("Workspace", back_populates="user_spaces", lazy="joined") + user = relationship("User", back_populates="user_spaces", lazy="joined") + + +class UserConversation(Base): + __tablename__ = "user_conversation" + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True) + workspace_id = Column(UUID(as_uuid=True), ForeignKey("workspace.id")) + user_id = Column(UUID(as_uuid=True), ForeignKey("user.id")) + created_at = Column(DateTime, default=datetime.datetime.now) + valid = Column(Boolean, default=True) + + workspace = relationship("Workspace") + user = relationship("User") + messages = relationship( + "ConversationMessage", + back_populates="user_conversation", + cascade="all, delete-orphan", + ) + + +class ConversationMessage(Base): + __tablename__ = "conversation_message" + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True) + conversation_id = Column(UUID(as_uuid=True), ForeignKey("user_conversation.id")) + created_at = Column(DateTime, default=datetime.datetime.now) + query = Column(String) + response = Column(JSON, nullable=True) + code_generated = Column(String, nullable=True) + label = Column(String, nullable=True) + log_id = Column(UUID(as_uuid=True), nullable=True) + settings = Column(JSON, nullable=True) + + user_conversation = relationship("UserConversation", back_populates="messages") + + +class DatasetSpace(Base): + __tablename__ = "dataset_space" + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True) + dataset_id = Column(UUID(as_uuid=True), ForeignKey("dataset.id")) + workspace_id = Column(UUID(as_uuid=True), ForeignKey("workspace.id")) + + dataset = relationship("Dataset", back_populates="dataset_spaces") + workspace = relationship("Workspace", back_populates="dataset_spaces") diff --git a/server/app/repositories/__init__.py b/server/app/repositories/__init__.py new file mode 100644 index 000000000..ce4145f21 --- /dev/null +++ b/server/app/repositories/__init__.py @@ -0,0 +1,18 @@ +from .api_key import APIKeyRepository +from .dataset import DatasetRepository +from .organization import OrganizationRepository +from .organization_membership import OrganizationMembershipRepository +from .workspace import WorkspaceRepository +from .user import UserRepository +from .conversation import ConversationRepository + +__all__ = [ + "UserRepository", + "APIKeyRepository", + "DatasetRepository", + "OrganizationMembership", + "OrganizationMembershipRepository", + "OrganizationRepository", + "WorkspaceRepository", + "ConversationRepository", +] diff --git a/server/app/repositories/api_key.py b/server/app/repositories/api_key.py new file mode 100644 index 000000000..dd4bcdd8e --- /dev/null +++ b/server/app/repositories/api_key.py @@ -0,0 +1,8 @@ +from app.models import APIKeys +from core.repository import BaseRepository + + +class APIKeyRepository(BaseRepository[APIKeys]): + """ + APIKeys repository provides all the database operations for the APIKeys model. + """ diff --git a/server/app/repositories/conversation.py b/server/app/repositories/conversation.py new file mode 100644 index 000000000..a7d7ab126 --- /dev/null +++ b/server/app/repositories/conversation.py @@ -0,0 +1,107 @@ +from typing import Any, Dict, List + +from sqlalchemy import and_, asc, desc, func +from app.models import ConversationMessage, UserConversation +from core.repository import BaseRepository +from sqlalchemy.sql.expression import select +from sqlalchemy.orm import selectinload +from core.database.transactional import Propagation, Transactional + + +class ConversationRepository(BaseRepository[UserConversation]): + """ + UserConversation repository provides all the database operations for the UserConversation model. + """ + @Transactional(propagation=Propagation.REQUIRED) + async def add_conversation_message( + self, + conversation_id: str, + query: str, + response: List[Dict], + code_generated: str, + **attributes: Any, + ): + conversation_message = ConversationMessage( + conversation_id=conversation_id, + query=query, + response=response, + code_generated=code_generated, + **attributes, + ) + + self.session.add(conversation_message) + + return conversation_message + + async def get_conversation_messages( + self, conversation_id: str, skip: int = 0, limit: int = 100, order: str = "asc" + ): + order_by_clause = ( + desc(ConversationMessage.created_at) + if order == "desc" + else asc(ConversationMessage.created_at) + ) + + query = ( + select(ConversationMessage) + .where(ConversationMessage.conversation_id == conversation_id) + .order_by(order_by_clause) + .offset(skip) + .limit(limit) + ) + result = await self.session.execute(query) + return result.scalars().all() + + async def get_conversations( + self, user_id: str, workspace_id: str, skip: int = 0, limit: int = 100 + ) -> List[UserConversation]: + query = ( + select(UserConversation) + .where( + and_( + UserConversation.user_id == user_id, + UserConversation.valid == True, + UserConversation.workspace_id == workspace_id, + ) + ) + .order_by(desc(UserConversation.created_at)) + .options( + selectinload(UserConversation.messages).load_only( + ConversationMessage.id, + ConversationMessage.query, + ConversationMessage.created_at, + ), + ) + .offset(skip) + .limit(limit) + ) + + result = await self.session.execute(query) + conversations = result.scalars().all() + + for conversation in conversations: + if conversation.messages: + conversation.messages.sort(key=lambda msg: msg.created_at) + conversation.messages = conversation.messages[:1] + + return conversations + + async def get_count(self, user_id: str, workspace_id: str) -> int: + query = select(func.count(UserConversation.id)).where( + and_( + UserConversation.user_id == user_id, + UserConversation.workspace_id == workspace_id, + UserConversation.valid == True, + ) + ) + + result = await self.session.execute(query) + return result.scalar_one() + + async def get_messages_count(self, conversation_id: str) -> int: + query = select(func.count(ConversationMessage.id)).where( + ConversationMessage.conversation_id == conversation_id + ) + + result = await self.session.execute(query) + return result.scalar_one() diff --git a/server/app/repositories/dataset.py b/server/app/repositories/dataset.py new file mode 100644 index 000000000..1d33fe371 --- /dev/null +++ b/server/app/repositories/dataset.py @@ -0,0 +1,44 @@ +from app.models import Connector, ConnectorType, Dataset, DatasetSpace +from core.repository import BaseRepository +from uuid import UUID +from sqlalchemy import select + + +class DatasetRepository(BaseRepository[Dataset]): + """ + Dataset repository provides all the database operations for the Dataset model. + """ + + async def create_dataset( + self, + user_id: str, + organization_id: str, + name: str, + connector_type: ConnectorType, + config, + head: dict, + description: str = "", + ): + connector = Connector(type=connector_type.value, config=config, user_id=user_id) + self.session.add(connector) + await self.session.flush() + + dataset = Dataset( + name=name, + table_name=name, + head=head, + user_id=user_id, + organization_id=organization_id, + connector_id=connector.id, + ) + + self.session.add(dataset) + await self.session.flush() + return dataset + + async def get_all_by_workspace_id(self, workspace_id: UUID): + result = await self.session.execute( + select(Dataset).join(DatasetSpace).where(DatasetSpace.workspace_id == workspace_id) + ) + datasets = result.scalars().all() + return datasets \ No newline at end of file diff --git a/server/app/repositories/organization.py b/server/app/repositories/organization.py new file mode 100644 index 000000000..d62e751b4 --- /dev/null +++ b/server/app/repositories/organization.py @@ -0,0 +1,8 @@ +from app.models import Organization +from core.repository import BaseRepository + + +class OrganizationRepository(BaseRepository[Organization]): + """ + Organization repository provides all the database operations for the Organization model. + """ diff --git a/server/app/repositories/organization_membership.py b/server/app/repositories/organization_membership.py new file mode 100644 index 000000000..a3de270ff --- /dev/null +++ b/server/app/repositories/organization_membership.py @@ -0,0 +1,8 @@ +from app.models import OrganizationMembership +from core.repository import BaseRepository + + +class OrganizationMembershipRepository(BaseRepository[OrganizationMembership]): + """ + Organization repository provides all the database operations for the Organization model. + """ diff --git a/server/app/repositories/user.py b/server/app/repositories/user.py new file mode 100644 index 000000000..688603a95 --- /dev/null +++ b/server/app/repositories/user.py @@ -0,0 +1,90 @@ +import uuid + +from sqlalchemy import Select +from sqlalchemy.orm import joinedload + +from app.models import ( + APIKeys, + Organization, + OrganizationMembership, + OrganizationRole, + User, +) +from core.config import config +from core.repository import BaseRepository +from core.security.password import PasswordHandler + + +class UserRepository(BaseRepository[User]): + """ + User repository provides all the database operations for the User model. + """ + + async def get_by_email( + self, email: str, join_: set[str] | None = None + ) -> User | None: + """ + Get user by email. + + :param email: Email. + :param join_: Join relations. + :return: User. + """ + query = self._query(join_) + query = query.filter(User.email == email) + + if join_ is not None: + return await self._all_unique(query) + + return await self._one_or_none(query) + + def get_user_me_details(self) -> User: + return + + async def create_and_init_dummy_user(self) -> User: + # Create user + password = PasswordHandler.hash(config.PASSWORD) + user = User( + email=config.EMAIL, + password=password, + first_name="pandasai", + verified=True, + ) + self.session.add(user) + + # Create organization + organization = Organization(name="PandasAI", url="", is_default=True) + self.session.add(organization) + + # Flush to ensure IDs are populated + await self.session.flush() + + # Create API key + api_token = PasswordHandler.hash(str(uuid.uuid4())) + api_key = APIKeys(organization_id=organization.id, api_key=api_token) + self.session.add(api_key) + + # Create user-organization membership + user_organization = OrganizationMembership( + user_id=user.id, + organization_id=organization.id, + role=OrganizationRole.MEMBER, + verified=True, + ) + + self.session.add(user_organization) + + user.organization_id = organization.id + + return user + + def _join_memberships(self, query: Select) -> Select: + """ + Join tasks. + + :param query: Query. + :return: Query. + """ + return query.options( + joinedload(User.memberships).joinedload(OrganizationMembership.organization) + ) diff --git a/server/app/repositories/workspace.py b/server/app/repositories/workspace.py new file mode 100644 index 000000000..043c1aa85 --- /dev/null +++ b/server/app/repositories/workspace.py @@ -0,0 +1,109 @@ +from typing import List + +from sqlalchemy import delete, select +from sqlalchemy.orm import joinedload + +from app.models import Connector, Dataset, DatasetSpace, Workspace, UserSpace, User +from app.repositories.dataset import DatasetRepository +from core.config import config +from core.repository import BaseRepository +from app.schemas.responses.users import WorkspaceUserResponse + + +class WorkspaceRepository(BaseRepository[Workspace]): + """ + Space repository provides all the database operations for the Space model. + """ + + async def create_default_space_in_org( + self, organization_id: str, user_id: str + ) -> Workspace: + space = await self.get_all(limit=1) + if space: + return space[0] + + space = Workspace( + name=config.DEFAULT_SPACE, + user_id=user_id, + organization_id=organization_id, + slug=config.DEFAULT_SPACE, + ) + self.session.add(space) + await self.session.flush() + + user_space = UserSpace(user_id=user_id, workspace_id=space.id) + + self.session.add(user_space) + + return space + + async def delete_space_datasets(self, workspace_id: str) -> None: + # Select the DatasetSpace entries for the given workspace_id + query = select(DatasetSpace).where(DatasetSpace.workspace_id == workspace_id) + result = await self.session.execute(query) + dataset_spaces = result.scalars().all() + + if not dataset_spaces: + return + + dataset_ids = [ds.dataset_id for ds in dataset_spaces] + + # Select the datasets to be deleted with their connectors + datasets_query = ( + select(Dataset) + .options(joinedload(Dataset.connector)) + .where(Dataset.id.in_(dataset_ids)) + ) + datasets_result = await self.session.execute(datasets_query) + datasets = datasets_result.scalars().all() + + connector_ids = [ + dataset.connector.id for dataset in datasets if dataset.connector + ] + + # Delete the DatasetSpace entries + delete_dataset_space_query = delete(DatasetSpace).where( + DatasetSpace.workspace_id == workspace_id + ) + await self.session.execute(delete_dataset_space_query) + + delete_dataset_query = delete(Dataset).where(Dataset.id.in_(dataset_ids)) + await self.session.execute(delete_dataset_query) + + # Delete the Connector entries + if connector_ids: + delete_connector_query = delete(Connector).where( + Connector.id.in_(connector_ids) + ) + await self.session.execute(delete_connector_query) + + await self.session.commit() + + async def create_csv_datasets(self, datasets: List[dict]) -> None: + dataset_repository = DatasetRepository(Dataset, db_session=self.session) + for dataset in datasets: + dataset_repository.create({**dataset}) + + async def add_dataset_to_space(self, workspace_id: str, dataset_id: str): + dataset_space = DatasetSpace(workspace_id=workspace_id, dataset_id=dataset_id) + self.session.add(dataset_space) + + async def get_space_datasets(self, workspace_id: str) -> List[Dataset]: + result = await self.session.execute( + select(Dataset) + .join(DatasetSpace) + .options(joinedload(Dataset.dataset_spaces)) + .filter(DatasetSpace.workspace_id == workspace_id) + ) + return result.unique().scalars().all() + + async def get_users_by_workspace_id(self, workspace_id: str) -> List[WorkspaceUserResponse]: + result = await self.session.execute( + select( + User.id, + User.first_name, + User.last_name, + User.email + ).join(UserSpace).filter(UserSpace.workspace_id == workspace_id) + ) + return result.all() \ No newline at end of file diff --git a/server/app/schemas/__init__.py b/server/app/schemas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/app/schemas/extras/__init__.py b/server/app/schemas/extras/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/app/schemas/extras/current_user.py b/server/app/schemas/extras/current_user.py new file mode 100644 index 000000000..04a0c911a --- /dev/null +++ b/server/app/schemas/extras/current_user.py @@ -0,0 +1,8 @@ +from pydantic import BaseModel, Field + + +class CurrentUser(BaseModel): + id: int = Field(None, description="User ID") + + class Config: + validate_assignment = True diff --git a/server/app/schemas/extras/health.py b/server/app/schemas/extras/health.py new file mode 100644 index 000000000..1a19e0039 --- /dev/null +++ b/server/app/schemas/extras/health.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel, Field + + +class Health(BaseModel): + version: str = Field(..., example="1.0.0") + status: str = Field(..., example="OK") diff --git a/server/app/schemas/extras/token.py b/server/app/schemas/extras/token.py new file mode 100644 index 000000000..19a795d2e --- /dev/null +++ b/server/app/schemas/extras/token.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel + + +class Token(BaseModel): + access_token: str + refresh_token: str diff --git a/server/app/schemas/requests/__init__.py b/server/app/schemas/requests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/app/schemas/requests/chat.py b/server/app/schemas/requests/chat.py new file mode 100644 index 000000000..dfafe94ab --- /dev/null +++ b/server/app/schemas/requests/chat.py @@ -0,0 +1,8 @@ +from typing import Optional +from pydantic import BaseModel + + +class ChatRequest(BaseModel): + workspace_id: str + query: str + conversation_id: Optional[str] = None diff --git a/server/app/schemas/requests/users.py b/server/app/schemas/requests/users.py new file mode 100644 index 000000000..f1255134e --- /dev/null +++ b/server/app/schemas/requests/users.py @@ -0,0 +1,46 @@ +# pylint: disable=all + +import re + +from pydantic import BaseModel, EmailStr, constr, validator + + +class RegisterUserRequest(BaseModel): + email: EmailStr + password: constr(min_length=8, max_length=64) + username: constr(min_length=3, max_length=64) + + @validator("password") + def password_must_contain_special_characters(cls, v): + if not re.search(r"[^a-zA-Z0-9]", v): + raise ValueError("Password must contain special characters") + return v + + @validator("password") + def password_must_contain_numbers(cls, v): + if not re.search(r"[0-9]", v): + raise ValueError("Password must contain numbers") + return v + + @validator("password") + def password_must_contain_uppercase(cls, v): + if not re.search(r"[A-Z]", v): + raise ValueError("Password must contain uppercase characters") + return v + + @validator("password") + def password_must_contain_lowercase(cls, v): + if not re.search(r"[a-z]", v): + raise ValueError("Password must contain lowercase characters") + return v + + @validator("username") + def username_must_not_contain_special_characters(cls, v): + if re.search(r"[^a-zA-Z0-9]", v): + raise ValueError("Username must not contain special characters") + return v + + +class LoginUserRequest(BaseModel): + email: EmailStr + password: str diff --git a/server/app/schemas/responses/__init__.py b/server/app/schemas/responses/__init__.py new file mode 100644 index 000000000..c23e484f1 --- /dev/null +++ b/server/app/schemas/responses/__init__.py @@ -0,0 +1,12 @@ +from typing import Generic, Optional, TypeVar + +from pydantic import BaseModel, Field + +T = TypeVar("T") + + +class APIResponse(BaseModel, Generic[T]): + status_code: int = Field(default=200, example=0) + data: Optional[T] = Field(default=None, example={}) + message: Optional[str] = None + error: Optional[str] = None diff --git a/server/app/schemas/responses/chat.py b/server/app/schemas/responses/chat.py new file mode 100644 index 000000000..9345cc560 --- /dev/null +++ b/server/app/schemas/responses/chat.py @@ -0,0 +1,16 @@ +from typing import Any, List + +from pydantic import BaseModel + + +class ChatResponseBase(BaseModel): + type: str + value: Any + message: str + + +class ChatResponse(BaseModel): + response: List[ChatResponseBase] + conversation_id: str + message_id: str + query: str diff --git a/server/app/schemas/responses/conversation.py b/server/app/schemas/responses/conversation.py new file mode 100644 index 000000000..6f7df0cf9 --- /dev/null +++ b/server/app/schemas/responses/conversation.py @@ -0,0 +1,42 @@ +from typing import Any, List, Optional +from pydantic import UUID4, BaseModel +from datetime import datetime + + +class ConversationMessageBase(BaseModel): + id: UUID4 + query: str + created_at: datetime + + class Config: + orm_mode = True + + +class ConversationMessageDTO(ConversationMessageBase): + response: Optional[Any] = None + code_generated: Optional[str] = None + label: Optional[str] = None + log_id: Optional[UUID4] = None + settings: Optional[Any] = None + + +class UserConversationBase(BaseModel): + id: UUID4 + workspace_id: UUID4 + user_id: UUID4 + created_at: datetime + valid: bool + messages: List[ConversationMessageBase] + + class Config: + orm_mode = True + + +class ConversationList(BaseModel): + count: int + conversations: List[UserConversationBase] + + +class ConversationMessageList(BaseModel): + count: int + messages: List[ConversationMessageDTO] diff --git a/server/app/schemas/responses/datasets.py b/server/app/schemas/responses/datasets.py new file mode 100644 index 000000000..9a1820160 --- /dev/null +++ b/server/app/schemas/responses/datasets.py @@ -0,0 +1,22 @@ +from pydantic import BaseModel, UUID4 +from typing import Optional, List +from datetime import datetime + +class DatasetModel(BaseModel): + id: UUID4 + name: str + table_name: str + description: Optional[str] + created_at: datetime + head: Optional[dict] + user_id: UUID4 + organization_id: UUID4 + connector_id: UUID4 + field_descriptions: Optional[dict] + filterable_columns: Optional[dict] + + class Config: + orm_mode = True + +class WorkspaceDatasetsResponseModel(BaseModel): + datasets: List[DatasetModel] \ No newline at end of file diff --git a/server/app/schemas/responses/organization.py b/server/app/schemas/responses/organization.py new file mode 100644 index 000000000..caf85dd89 --- /dev/null +++ b/server/app/schemas/responses/organization.py @@ -0,0 +1,9 @@ +from pydantic import UUID4, BaseModel, Field + + +class OrganizationBase(BaseModel): + name: str + id: UUID4 = Field(..., example="a3b8f042-1e16-4f0a-a8f0-421e16df0a2f") + + class Config: + orm_mode = True diff --git a/server/app/schemas/responses/space.py b/server/app/schemas/responses/space.py new file mode 100644 index 000000000..45e4021c0 --- /dev/null +++ b/server/app/schemas/responses/space.py @@ -0,0 +1,7 @@ +from pydantic import UUID4, BaseModel + + +class SpaceBase(BaseModel): + name: str + id: UUID4 + slug: str diff --git a/server/app/schemas/responses/users.py b/server/app/schemas/responses/users.py new file mode 100644 index 000000000..aa6144934 --- /dev/null +++ b/server/app/schemas/responses/users.py @@ -0,0 +1,46 @@ +from typing import List, Optional + +from pydantic import UUID4, BaseModel, Field + +from app.schemas.responses.organization import OrganizationBase +from app.schemas.responses.space import SpaceBase + + +class UserResponse(BaseModel): + email: str = Field(..., example="john.doe@example.com") + username: str = Field(..., example="john.doe") + uuid: UUID4 = Field(..., example="a3b8f042-1e16-4f0a-a8f0-421e16df0a2f") + + class Config: + orm_mode = True + + +class MembershipBase(BaseModel): + organization: OrganizationBase + + class Config: + orm_mode = True + + +class UserInfo(BaseModel): + email: str = Field(..., example="john.doe@example.com") + first_name: str = Field(..., example="john.doe") + id: UUID4 = Field(..., example="a3b8f042-1e16-4f0a-a8f0-421e16df0a2f") + organizations: List[OrganizationBase] = Field(...) + space: SpaceBase = Field(...) + + class Config: + orm_mode = True + + +class WorkspaceUserResponse(BaseModel): + id: UUID4 + first_name: Optional[str] + last_name: Optional[str] + email: Optional[str] + + class Config: + orm_mode = True + +class WorkspaceUsersResponse(BaseModel): + users: List[WorkspaceUserResponse] \ No newline at end of file diff --git a/server/app/utils/memory.py b/server/app/utils/memory.py new file mode 100644 index 000000000..d0ee8f546 --- /dev/null +++ b/server/app/utils/memory.py @@ -0,0 +1,20 @@ +from typing import List + +from pandasai.helpers.memory import Memory +from app.models import ConversationMessage + + +def prepare_conv_memory(messages: List[ConversationMessage]) -> Memory: + memory = Memory() + for message in messages: + if message.response: + memory.add(message.query, is_user=True) + if message.code_generated: + memory.add(f"```python\n{message.code_generated}\n```", is_user=False) + else: + memory.add( + "\n".join([str(item["message"]) for item in message.response]), + is_user=False, + ) + + return memory diff --git a/server/core/__init__.py b/server/core/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/core/config.py b/server/core/config.py new file mode 100644 index 000000000..30818c7af --- /dev/null +++ b/server/core/config.py @@ -0,0 +1,36 @@ +from enum import Enum + +from pydantic import BaseSettings, PostgresDsn + + +class EnvironmentType(str, Enum): + DEVELOPMENT = "development" + PRODUCTION = "production" + TEST = "test" + + +class BaseConfig(BaseSettings): + class Config: + case_sensitive = True + + +class Config(BaseConfig): + DEBUG: int = 0 + DEFAULT_LOCALE: str = "en_US" + ENVIRONMENT: str = EnvironmentType.DEVELOPMENT + POSTGRES_URL: PostgresDsn = ( + "postgresql+asyncpg://user:password@127.0.0.1:5432/db-name" + ) + OPENAI_API_KEY: str = None + RELEASE_VERSION: str = "0.1.0" + SHOW_SQL_ALCHEMY_QUERIES: int = 0 + SECRET_KEY: str = "super-secret-key" + JWT_ALGORITHM: str = "HS256" + JWT_EXPIRE_MINUTES: int = 60 * 24 + EMAIL = "test@pandabi.ai" + PASSWORD = "12345" + DEFAULT_ORGANIZATION = "PandaBI" + DEFAULT_SPACE = "pandasai" + + +config: Config = Config() diff --git a/server/core/constants.py b/server/core/constants.py new file mode 100644 index 000000000..7263de137 --- /dev/null +++ b/server/core/constants.py @@ -0,0 +1 @@ +CHAT_FALLBACK_MESSAGE = "I'm sorry, I wasn't able to fully understand your question. Could you please rephrase it or provide more details?" diff --git a/server/core/controller/__init__.py b/server/core/controller/__init__.py new file mode 100644 index 000000000..163c242aa --- /dev/null +++ b/server/core/controller/__init__.py @@ -0,0 +1,3 @@ +from .base import BaseController + +__all__ = ["BaseController"] diff --git a/server/core/controller/base.py b/server/core/controller/base.py new file mode 100644 index 000000000..8b1ee975d --- /dev/null +++ b/server/core/controller/base.py @@ -0,0 +1,106 @@ +from typing import Any, Generic, Type, TypeVar +from uuid import UUID + +from pydantic import BaseModel + +from core.database import Base, Propagation, Transactional +from core.exceptions import NotFoundException +from core.repository import BaseRepository + +ModelType = TypeVar("ModelType", bound=Base) + + +class BaseController(Generic[ModelType]): + """Base class for data controllers.""" + + def __init__(self, model: Type[ModelType], repository: BaseRepository): + self.model_class = model + self.repository = repository + + async def get_by_id(self, id_: int, join_: set[str] | None = None) -> ModelType: + """ + Returns the model instance matching the id. + + :param id_: The id to match. + :param join_: The joins to make. + :return: The model instance. + """ + + db_obj = await self.repository.get_by( + field="id", value=id_, join_=join_, unique=True + ) + if not db_obj: + raise NotFoundException( + f"{self.model_class.__tablename__.title()} with id: {id_} does not exist" + ) + + return db_obj + + async def get_by_uuid(self, uuid: UUID, join_: set[str] | None = None) -> ModelType: + """ + Returns the model instance matching the uuid. + + :param uuid: The uuid to match. + :param join_: The joins to make. + :return: The model instance. + """ + + db_obj = await self.repository.get_by( + field="uuid", value=uuid, join_=join_, unique=True + ) + if not db_obj: + raise NotFoundException( + f"{self.model_class.__tablename__.title()} with id: {uuid} does not exist" + ) + return db_obj + + async def get_all( + self, skip: int = 0, limit: int = 100, join_: set[str] | None = None + ) -> list[ModelType]: + """ + Returns a list of records based on pagination params. + + :param skip: The number of records to skip. + :param limit: The number of records to return. + :param join_: The joins to make. + :return: A list of records. + """ + + response = await self.repository.get_all(skip, limit, join_) + return response + + @Transactional(propagation=Propagation.REQUIRED) + async def create(self, attributes: dict[str, Any]) -> ModelType: + """ + Creates a new Object in the DB. + + :param attributes: The attributes to create the object with. + :return: The created object. + """ + create = await self.repository.create(attributes) + return create + + @Transactional(propagation=Propagation.REQUIRED) + async def delete(self, model: ModelType) -> bool: + """ + Deletes the Object from the DB. + + :param model: The model to delete. + :return: True if the object was deleted, False otherwise. + """ + delete = await self.repository.delete(model) + return delete + + @staticmethod + def extract_attributes_from_schema( + schema: BaseModel, excludes: set = None + ) -> dict[str, Any]: + """ + Extracts the attributes from the schema. + + :param schema: The schema to extract the attributes from. + :param excludes: The attributes to exclude. + :return: The attributes. + """ + + return schema.dict(exclude=excludes, exclude_unset=True) diff --git a/server/core/database/__init__.py b/server/core/database/__init__.py new file mode 100644 index 000000000..310fd81de --- /dev/null +++ b/server/core/database/__init__.py @@ -0,0 +1,20 @@ +from .session import ( + Base, + get_session, + reset_session_context, + session, + set_session_context, +) +from .standalone_session import standalone_session +from .transactional import Propagation, Transactional + +__all__ = [ + "Base", + "session", + "get_session", + "set_session_context", + "reset_session_context", + "standalone_session", + "Transactional", + "Propagation", +] diff --git a/server/core/database/mixins/__init__.py b/server/core/database/mixins/__init__.py new file mode 100644 index 000000000..3151c1d22 --- /dev/null +++ b/server/core/database/mixins/__init__.py @@ -0,0 +1,3 @@ +from .timestamp import TimestampMixin + +__all__ = ["TimestampMixin"] diff --git a/server/core/database/mixins/timestamp.py b/server/core/database/mixins/timestamp.py new file mode 100644 index 000000000..db91f0344 --- /dev/null +++ b/server/core/database/mixins/timestamp.py @@ -0,0 +1,19 @@ +# pylint: skip-file + +from sqlalchemy import Column, DateTime, func +from sqlalchemy.ext.declarative import declared_attr + + +class TimestampMixin: + @declared_attr + def created_at(cls): + return Column(DateTime, default=func.now(), nullable=False) + + @declared_attr + def updated_at(cls): + return Column( + DateTime, + default=func.now(), + onupdate=func.now(), + nullable=False, + ) diff --git a/server/core/database/session.py b/server/core/database/session.py new file mode 100644 index 000000000..eea175c35 --- /dev/null +++ b/server/core/database/session.py @@ -0,0 +1,67 @@ +from contextvars import ContextVar, Token +from typing import Union + +from sqlalchemy.ext.asyncio import ( + AsyncSession, + async_scoped_session, + create_async_engine, +) +from sqlalchemy.orm import Session, declarative_base, sessionmaker +from sqlalchemy.sql.expression import Delete, Insert, Update + +from core.config import config + +session_context: ContextVar[str] = ContextVar("session_context") + + +def get_session_context() -> str: + return session_context.get() + + +def set_session_context(session_id: str) -> Token: + return session_context.set(session_id) + + +def reset_session_context(context: Token) -> None: + session_context.reset(context) + + +engines = { + "writer": create_async_engine(config.POSTGRES_URL, pool_recycle=3600), + "reader": create_async_engine(config.POSTGRES_URL, pool_recycle=3600, echo=True), +} + + +class RoutingSession(Session): + def get_bind(self, mapper=None, clause=None, **kwargs): + if self._flushing or isinstance(clause, (Update, Delete, Insert)): + return engines["writer"].sync_engine + return engines["reader"].sync_engine + + +async_session_factory = sessionmaker( + class_=AsyncSession, + sync_session_class=RoutingSession, + expire_on_commit=False, +) + +session: Union[AsyncSession, async_scoped_session] = async_scoped_session( + session_factory=async_session_factory, + scopefunc=get_session_context, +) + + +async def get_session(): + """ + Get the database session. + This can be used for dependency injection. + + :return: The database session. + """ + try: + yield session + finally: + await session.close() + + +Base = declarative_base() diff --git a/server/core/database/standalone_session.py b/server/core/database/standalone_session.py new file mode 100644 index 000000000..c1468ed0a --- /dev/null +++ b/server/core/database/standalone_session.py @@ -0,0 +1,20 @@ +from uuid import uuid4 + +from .session import reset_session_context, session, set_session_context + + +def standalone_session(func): + async def _standalone_session(*args, **kwargs): + session_id = str(uuid4()) + context = set_session_context(session_id=session_id) + + try: + await func(*args, **kwargs) + except Exception as exception: + await session.rollback() + raise exception + finally: + await session.remove() + reset_session_context(context=context) + + return _standalone_session diff --git a/server/core/database/transactional.py b/server/core/database/transactional.py new file mode 100644 index 000000000..9ab778993 --- /dev/null +++ b/server/core/database/transactional.py @@ -0,0 +1,55 @@ +from enum import Enum +from functools import wraps + +from core.database import session + + +class Propagation(Enum): + REQUIRED = "required" + REQUIRED_NEW = "required_new" + + +class Transactional: + def __init__(self, propagation: Propagation = Propagation.REQUIRED): + self.propagation = propagation + + def __call__(self, function): + @wraps(function) + async def decorator(*args, **kwargs): + try: + if self.propagation == Propagation.REQUIRED: + result = await self._run_required( + function=function, + args=args, + kwargs=kwargs, + ) + elif self.propagation == Propagation.REQUIRED_NEW: + result = await self._run_required_new( + function=function, + args=args, + kwargs=kwargs, + ) + else: + result = await self._run_required( + function=function, + args=args, + kwargs=kwargs, + ) + except Exception as exception: + await session.rollback() + raise exception + + return result + + return decorator + + async def _run_required(self, function, args, kwargs) -> None: + result = await function(*args, **kwargs) + await session.commit() + return result + + async def _run_required_new(self, function, args, kwargs) -> None: + session.begin() + result = await function(*args, **kwargs) + await session.commit() + return result diff --git a/server/core/exceptions/__init__.py b/server/core/exceptions/__init__.py new file mode 100644 index 000000000..c91c61f6d --- /dev/null +++ b/server/core/exceptions/__init__.py @@ -0,0 +1,19 @@ +from .base import ( + BadRequestException, + CustomException, + DuplicateValueException, + ForbiddenException, + NotFoundException, + UnauthorizedException, + UnprocessableEntity, +) + +__all__ = [ + "CustomException", + "BadRequestException", + "NotFoundException", + "ForbiddenException", + "UnauthorizedException", + "UnprocessableEntity", + "DuplicateValueException", +] diff --git a/server/core/exceptions/base.py b/server/core/exceptions/base.py new file mode 100644 index 000000000..e5e777475 --- /dev/null +++ b/server/core/exceptions/base.py @@ -0,0 +1,47 @@ +from http import HTTPStatus + + +class CustomException(Exception): + code = HTTPStatus.BAD_GATEWAY + error_code = HTTPStatus.BAD_GATEWAY + message = HTTPStatus.BAD_GATEWAY.description + + def __init__(self, message=None): + if message: + self.message = message + + +class BadRequestException(CustomException): + code = HTTPStatus.BAD_REQUEST + error_code = HTTPStatus.BAD_REQUEST + message = HTTPStatus.BAD_REQUEST.description + + +class NotFoundException(CustomException): + code = HTTPStatus.NOT_FOUND + error_code = HTTPStatus.NOT_FOUND + message = HTTPStatus.NOT_FOUND.description + + +class ForbiddenException(CustomException): + code = HTTPStatus.FORBIDDEN + error_code = HTTPStatus.FORBIDDEN + message = HTTPStatus.FORBIDDEN.description + + +class UnauthorizedException(CustomException): + code = HTTPStatus.UNAUTHORIZED + error_code = HTTPStatus.UNAUTHORIZED + message = HTTPStatus.UNAUTHORIZED.description + + +class UnprocessableEntity(CustomException): + code = HTTPStatus.UNPROCESSABLE_ENTITY + error_code = HTTPStatus.UNPROCESSABLE_ENTITY + message = HTTPStatus.UNPROCESSABLE_ENTITY.description + + +class DuplicateValueException(CustomException): + code = HTTPStatus.UNPROCESSABLE_ENTITY + error_code = HTTPStatus.UNPROCESSABLE_ENTITY + message = HTTPStatus.UNPROCESSABLE_ENTITY.description diff --git a/server/core/factory/__init__.py b/server/core/factory/__init__.py new file mode 100644 index 000000000..fc0d146f3 --- /dev/null +++ b/server/core/factory/__init__.py @@ -0,0 +1,3 @@ +from .factory import Factory + +__all__ = ["Factory"] diff --git a/server/core/factory/factory.py b/server/core/factory/factory.py new file mode 100644 index 000000000..f4593f829 --- /dev/null +++ b/server/core/factory/factory.py @@ -0,0 +1,77 @@ +from functools import partial + +from fastapi import Depends + +from app.controllers import AuthController, UserController +from app.controllers.chat import ChatController +from app.controllers.conversation import ConversationController +from app.controllers.workspace import WorkspaceController +from app.controllers.datasets import DatasetController +from app.models import ( + Dataset, + Organization, + OrganizationMembership, + Workspace, + User, + UserConversation, +) +from app.repositories import UserRepository +from app.repositories.api_key import APIKeyRepository +from app.repositories.conversation import ConversationRepository +from app.repositories.dataset import DatasetRepository +from app.repositories.organization import OrganizationRepository +from app.repositories.workspace import WorkspaceRepository +from core.database import get_session + + +class Factory: + """ + This is the factory container that will instantiate all the controllers and + repositories which can be accessed by the rest of the application. + """ + + # Repositories + user_repository = partial(UserRepository, User) + organization_repository = partial(OrganizationRepository, Organization) + api_key_repository = partial(APIKeyRepository, Organization) + org_membership_repository = partial(OrganizationMembership, Organization) + space_repository = partial(WorkspaceRepository, Workspace) + dataset_repository = partial(DatasetRepository, Dataset) + conversation_repository = partial(ConversationRepository, UserConversation) + datasets_repository = partial(ConversationRepository, UserConversation) + + def get_user_controller(self, db_session=Depends(get_session)): + return UserController( + user_repository=self.user_repository(db_session=db_session), + space_repository=self.space_repository(db_session=db_session), + ) + + def get_space_controller(self, db_session=Depends(get_session)): + return WorkspaceController( + space_repository=self.space_repository(db_session=db_session), + dataset_repository=self.dataset_repository(db_session=db_session), + ) + + def get_auth_controller(self, db_session=Depends(get_session)): + return AuthController( + user_repository=self.user_repository(db_session=db_session), + ) + + def get_chat_controller(self, db_session=Depends(get_session)): + return ChatController( + user_repository=self.user_repository(db_session=db_session), + space_repository=self.space_repository(db_session=db_session), + conversation_repository=self.conversation_repository(db_session=db_session), + ) + + def get_datasets_controller(self, db_session=Depends(get_session)): + return DatasetController( + dataset_repository=self.dataset_repository(db_session=db_session), + space_repository=self.space_repository(db_session=db_session) + ) + + def get_conversation_controller(self, db_session=Depends(get_session)): + return ConversationController( + user_repository=self.user_repository(db_session=db_session), + conversation_repository=self.conversation_repository(db_session=db_session), + ) diff --git a/server/core/fastapi/__init__.py b/server/core/fastapi/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/core/fastapi/dependencies/__init__.py b/server/core/fastapi/dependencies/__init__.py new file mode 100644 index 000000000..d9c809089 --- /dev/null +++ b/server/core/fastapi/dependencies/__init__.py @@ -0,0 +1,4 @@ +from .authentication import AuthenticationRequired +from .logging import Logging + +__all__ = ["Logging"] diff --git a/server/core/fastapi/dependencies/authentication.py b/server/core/fastapi/dependencies/authentication.py new file mode 100644 index 000000000..168e69dde --- /dev/null +++ b/server/core/fastapi/dependencies/authentication.py @@ -0,0 +1,19 @@ +from fastapi import Depends, status +from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer + +from core.exceptions.base import CustomException + + +class AuthenticationRequiredException(CustomException): + code = status.HTTP_401_UNAUTHORIZED + error_code = status.HTTP_401_UNAUTHORIZED + message = "Authentication required" + + +class AuthenticationRequired: + def __init__( + self, + token: HTTPAuthorizationCredentials = Depends(HTTPBearer(auto_error=False)), + ): + if not token: + raise AuthenticationRequiredException() diff --git a/server/core/fastapi/dependencies/current_user.py b/server/core/fastapi/dependencies/current_user.py new file mode 100644 index 000000000..eade41538 --- /dev/null +++ b/server/core/fastapi/dependencies/current_user.py @@ -0,0 +1,12 @@ +from fastapi import Depends, Request + +from app.controllers.user import UserController +from app.schemas.responses.users import UserInfo +from core.factory import Factory + + +async def get_current_user( + request: Request, + user_controller: UserController = Depends(Factory().get_user_controller), +) -> UserInfo: + return await user_controller.me() diff --git a/server/core/fastapi/dependencies/logging.py b/server/core/fastapi/dependencies/logging.py new file mode 100644 index 000000000..f940aa2d8 --- /dev/null +++ b/server/core/fastapi/dependencies/logging.py @@ -0,0 +1,9 @@ +from fastapi import BackgroundTasks + + +class Logging: + def __init__(self, background_task: BackgroundTasks): + background_task.add_task(self._send_log) + + async def _send_log(self): + pass diff --git a/server/core/fastapi/middlewares/__init__.py b/server/core/fastapi/middlewares/__init__.py new file mode 100644 index 000000000..f235afc85 --- /dev/null +++ b/server/core/fastapi/middlewares/__init__.py @@ -0,0 +1,10 @@ +from .authentication import AuthBackend, AuthenticationMiddleware +from .response_logger import ResponseLoggerMiddleware +from .sqlalchemy import SQLAlchemyMiddleware + +__all__ = [ + "SQLAlchemyMiddleware", + "ResponseLoggerMiddleware", + "AuthenticationMiddleware", + "AuthBackend", +] diff --git a/server/core/fastapi/middlewares/authentication.py b/server/core/fastapi/middlewares/authentication.py new file mode 100644 index 000000000..4ee4d3683 --- /dev/null +++ b/server/core/fastapi/middlewares/authentication.py @@ -0,0 +1,48 @@ +from typing import Optional, Tuple + +from jose import JWTError, jwt +from starlette.authentication import AuthenticationBackend +from starlette.middleware.authentication import ( + AuthenticationMiddleware as BaseAuthenticationMiddleware, +) +from starlette.requests import HTTPConnection + +from app.schemas.extras.current_user import CurrentUser +from core.config import config + + +class AuthBackend(AuthenticationBackend): + async def authenticate( + self, conn: HTTPConnection + ) -> Tuple[bool, Optional[CurrentUser]]: + current_user = CurrentUser() + authorization: str = conn.headers.get("Authorization") + if not authorization: + return False, current_user + + try: + scheme, token = authorization.split(" ") + if scheme.lower() != "bearer": + return False, current_user + except ValueError: + return False, current_user + + if not token: + return False, current_user + + try: + payload = jwt.decode( + token, + config.SECRET_KEY, + algorithms=[config.JWT_ALGORITHM], + ) + user_id = payload.get("user_id") + except JWTError: + return False, current_user + + current_user.id = user_id + return True, current_user + + +class AuthenticationMiddleware(BaseAuthenticationMiddleware): + pass diff --git a/server/core/fastapi/middlewares/response_logger.py b/server/core/fastapi/middlewares/response_logger.py new file mode 100644 index 000000000..a636ac912 --- /dev/null +++ b/server/core/fastapi/middlewares/response_logger.py @@ -0,0 +1,37 @@ +from typing import Optional + +from pydantic import BaseModel, Field +from starlette.datastructures import Headers +from starlette.types import ASGIApp, Message, Receive, Scope, Send + + +class ResponseInfo(BaseModel): + headers: Optional[Headers] = Field(default=None, title="Response header") + body: str = Field(default="", title="Body") + status_code: Optional[int] = Field(default=None, title="Status code") + + class Config: + arbitrary_types_allowed = True + + +class ResponseLoggerMiddleware: + def __init__(self, app: ASGIApp) -> None: + self.app = app + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] != "http": + return await self.app(scope, receive, send) + + response_info = ResponseInfo() + + async def _logging_send(message: Message) -> None: + if message.get("type") == "http.response.start": + response_info.headers = Headers(raw=message.get("headers")) + response_info.status_code = message.get("status") + elif message.get("type") == "http.response.body": + if body := message.get("body"): + response_info.body += body.decode("utf8") + + await send(message) + + await self.app(scope, receive, _logging_send) diff --git a/server/core/fastapi/middlewares/sqlalchemy.py b/server/core/fastapi/middlewares/sqlalchemy.py new file mode 100644 index 000000000..254722405 --- /dev/null +++ b/server/core/fastapi/middlewares/sqlalchemy.py @@ -0,0 +1,22 @@ +from uuid import uuid4 + +from starlette.types import ASGIApp, Receive, Scope, Send + +from core.database.session import reset_session_context, session, set_session_context + + +class SQLAlchemyMiddleware: + def __init__(self, app: ASGIApp) -> None: + self.app = app + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + session_id = str(uuid4()) + context = set_session_context(session_id=session_id) + + try: + await self.app(scope, receive, send) + except Exception as exception: + raise exception + finally: + await session.remove() + reset_session_context(context=context) diff --git a/server/core/repository/__init__.py b/server/core/repository/__init__.py new file mode 100644 index 000000000..40a534795 --- /dev/null +++ b/server/core/repository/__init__.py @@ -0,0 +1,3 @@ +from .base import BaseRepository + +__all__ = ["BaseRepository"] diff --git a/server/core/repository/base.py b/server/core/repository/base.py new file mode 100644 index 000000000..a2f156b38 --- /dev/null +++ b/server/core/repository/base.py @@ -0,0 +1,247 @@ +from functools import reduce +from typing import Any, Generic, Type, TypeVar + +from sqlalchemy import Select, func +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.sql.expression import select + +from core.database import Base + +ModelType = TypeVar("ModelType", bound=Base) + + +class BaseRepository(Generic[ModelType]): + """Base class for data repositories.""" + + def __init__(self, model: Type[ModelType], db_session: AsyncSession): + self.session = db_session + self.model_class: Type[ModelType] = model + + async def create(self, attributes: dict[str, Any] = None) -> ModelType: + """ + Creates the model instance. + + :param attributes: The attributes to create the model with. + :return: The created model instance. + """ + if attributes is None: + attributes = {} + model = self.model_class(**attributes) + self.session.add(model) + return model + + async def get_all( + self, skip: int = 0, limit: int = 100, join_: set[str] | None = None + ) -> list[ModelType]: + """ + Returns a list of model instances. + + :param skip: The number of records to skip. + :param limit: The number of record to return. + :param join_: The joins to make. + :return: A list of model instances. + """ + query = self._query(join_) + query = query.offset(skip).limit(limit) + + if join_ is not None: + return await self._all_unique(query) + + return await self._all(query) + + async def get_by( + self, + field: str, + value: Any, + join_: set[str] | None = None, + unique: bool = False, + ) -> ModelType: + """ + Returns the model instance matching the field and value. + + :param field: The field to match. + :param value: The value to match. + :param join_: The joins to make. + :return: The model instance. + """ + query = self._query(join_) + query = await self._get_by(query, field, value) + + if join_ is not None: + return await self._all_unique(query) + if unique: + return await self._one(query) + + return await self._all(query) + + async def get_by_id(self, id: Any) -> ModelType | None: + """ + generic method to get by id. + + """ + query = select(self.model_class).filter_by(id=id) + result = await self.session.execute(query) + return result.scalars().first() + + async def delete(self, model: ModelType) -> None: + """ + Deletes the model. + + :param model: The model to delete. + :return: None + """ + self.session.delete(model) + + def _query( + self, + join_: set[str] | None = None, + order_: dict | None = None, + ) -> Select: + """ + Returns a callable that can be used to query the model. + + :param join_: The joins to make. + :param order_: The order of the results. (e.g desc, asc) + :return: A callable that can be used to query the model. + """ + query = select(self.model_class) + query = self._maybe_join(query, join_) + query = self._maybe_ordered(query, order_) + + return query + + async def _all(self, query: Select) -> list[ModelType]: + """ + Returns all results from the query. + + :param query: The query to execute. + :return: A list of model instances. + """ + query = await self.session.scalars(query) + return query.all() + + async def _all_unique(self, query: Select) -> list[ModelType]: + result = await self.session.execute(query) + return result.unique().scalars().all() + + async def _first(self, query: Select) -> ModelType | None: + """ + Returns the first result from the query. + + :param query: The query to execute. + :return: The first model instance. + """ + query = await self.session.scalars(query) + return query.first() + + async def _one_or_none(self, query: Select) -> ModelType | None: + """Returns the first result from the query or None.""" + query = await self.session.scalars(query) + return query.one_or_none() + + async def _one(self, query: Select) -> ModelType: + """ + Returns the first result from the query or raises NoResultFound. + + :param query: The query to execute. + :return: The first model instance. + """ + query = await self.session.scalars(query) + return query.one() + + async def _count(self, query: Select) -> int: + """ + Returns the count of the records. + + :param query: The query to execute. + """ + query = query.subquery() + query = await self.session.scalars(select(func.count()).select_from(query)) + return query.one() + + async def _sort_by( + self, + query: Select, + sort_by: str, + order: str | None = "asc", + model: Type[ModelType] | None = None, + case_insensitive: bool = False, + ) -> Select: + """ + Returns the query sorted by the given column. + + :param query: The query to sort. + :param sort_by: The column to sort by. + :param order: The order to sort by. + :param model: The model to sort. + :param case_insensitive: Whether to sort case insensitively. + :return: The sorted query. + """ + model = model or self.model_class + + order_column = None + + if case_insensitive: + order_column = func.lower(getattr(model, sort_by)) + else: + order_column = getattr(model, sort_by) + + if order == "desc": + return query.order_by(order_column.desc()) + + return query.order_by(order_column.asc()) + + async def _get_by(self, query: Select, field: str, value: Any) -> Select: + """ + Returns the query filtered by the given column. + + :param query: The query to filter. + :param field: The column to filter by. + :param value: The value to filter by. + :return: The filtered query. + """ + return query.where(getattr(self.model_class, field) == value) + + def _maybe_join(self, query: Select, join_: set[str] | None = None) -> Select: + """ + Returns the query with the given joins. + + :param query: The query to join. + :param join_: The joins to make. + :return: The query with the given joins. + """ + if not join_: + return query + + if not isinstance(join_, set): + raise TypeError("join_ must be a set") + + return reduce(self._add_join_to_query, join_, query) + + def _maybe_ordered(self, query: Select, order_: dict | None = None) -> Select: + """ + Returns the query ordered by the given column. + + :param query: The query to order. + :param order_: The order to make. + :return: The query ordered by the given column. + """ + if order_: + if order_["asc"]: + for order in order_["asc"]: + query = query.order_by(getattr(self.model_class, order).asc()) + else: + for order in order_["desc"]: + query = query.order_by(getattr(self.model_class, order).desc()) + + return query + + def _add_join_to_query(self, query: Select, join_: set[str]) -> Select: + """ + Returns the query with the given join. + + :param query: The query to join. + :param join_: The join to make. + :return: The query with the given join. + """ + return getattr(self, "_join_" + join_)(query) diff --git a/server/core/security/__init__.py b/server/core/security/__init__.py new file mode 100644 index 000000000..fe532bf42 --- /dev/null +++ b/server/core/security/__init__.py @@ -0,0 +1,7 @@ +from .jwt import JWTHandler +from .password import PasswordHandler + +__all__ = [ + "JWTHandler", + "PasswordHandler", +] diff --git a/server/core/security/jwt.py b/server/core/security/jwt.py new file mode 100644 index 000000000..eebe5ef20 --- /dev/null +++ b/server/core/security/jwt.py @@ -0,0 +1,53 @@ +from datetime import datetime, timedelta + +from jose import ExpiredSignatureError, JWTError, jwt + +from core.config import config +from core.exceptions import CustomException + + +class JWTDecodeError(CustomException): + code = 401 + message = "Invalid token" + + +class JWTExpiredError(CustomException): + code = 401 + message = "Token expired" + + +class JWTHandler: + secret_key = config.SECRET_KEY + algorithm = config.JWT_ALGORITHM + expire_minutes = config.JWT_EXPIRE_MINUTES + + @staticmethod + def encode(payload: dict) -> str: + expire = datetime.utcnow() + timedelta(minutes=JWTHandler.expire_minutes) + payload.update({"exp": expire}) + return jwt.encode( + payload, JWTHandler.secret_key, algorithm=JWTHandler.algorithm + ) + + @staticmethod + def decode(token: str) -> dict: + try: + return jwt.decode( + token, JWTHandler.secret_key, algorithms=[JWTHandler.algorithm] + ) + except ExpiredSignatureError as exception: + raise JWTExpiredError() from exception + except JWTError as exception: + raise JWTDecodeError() from exception + + @staticmethod + def decode_expired(token: str) -> dict: + try: + return jwt.decode( + token, + JWTHandler.secret_key, + algorithms=[JWTHandler.algorithm], + options={"verify_exp": False}, + ) + except JWTError as exception: + raise JWTDecodeError() from exception diff --git a/server/core/security/password.py b/server/core/security/password.py new file mode 100644 index 000000000..e95faa4ae --- /dev/null +++ b/server/core/security/password.py @@ -0,0 +1,16 @@ +from passlib.context import CryptContext + + +class PasswordHandler: + pwd_context = CryptContext( + schemes=["bcrypt"], + deprecated="auto", + ) + + @staticmethod + def hash(password: str): + return PasswordHandler.pwd_context.hash(password) + + @staticmethod + def verify(hashed_password, plain_password): + return PasswordHandler.pwd_context.verify(plain_password, hashed_password) diff --git a/server/core/server.py b/server/core/server.py new file mode 100644 index 000000000..2de4c7b01 --- /dev/null +++ b/server/core/server.py @@ -0,0 +1,148 @@ +import os +from typing import List + +import pandas as pd +from fastapi import Depends, FastAPI, Request +from fastapi.middleware import Middleware +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import JSONResponse + +from api import router +from app.controllers.workspace import WorkspaceController +from app.controllers.user import UserController +from app.models import Dataset, Workspace, User +from app.repositories.dataset import DatasetRepository +from app.repositories.workspace import WorkspaceRepository +from app.repositories.user import UserRepository +from core.config import config +from core.database.session import session +from core.exceptions import CustomException +from core.fastapi.dependencies import Logging +from core.fastapi.middlewares import ( + AuthBackend, + AuthenticationMiddleware, + SQLAlchemyMiddleware, +) +from core.utils.dataframe import convert_dataframe_to_dict + + +def on_auth_error(request: Request, exc: Exception): + status_code, error_code, message = 401, None, str(exc) + if isinstance(exc, CustomException): + status_code = int(exc.code) + error_code = exc.error_code + message = exc.message + + return JSONResponse( + status_code=status_code, + content={"error_code": error_code, "message": message}, + ) + + +def init_routers(app_: FastAPI) -> None: + app_.include_router(router) + + +def init_listeners(app_: FastAPI) -> None: + @app_.exception_handler(CustomException) + async def custom_exception_handler(request: Request, exc: CustomException): + return JSONResponse( + status_code=exc.code, + content={"error_code": exc.error_code, "message": exc.message}, + ) + + +def make_middleware() -> List[Middleware]: + middleware = [ + Middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ), + Middleware( + AuthenticationMiddleware, + backend=AuthBackend(), + on_error=on_auth_error, + ), + Middleware(SQLAlchemyMiddleware), + # Middleware(ResponseLoggerMiddleware), + ] + return middleware + + +async def init_user(): + user_repository = UserRepository(User, db_session=session) + space_repository = WorkspaceRepository(Workspace, db_session=session) + controller = UserController(user_repository, space_repository) + await controller.create_default_user() + users = await controller.get_all(limit=1, join_={"memberships"}) + return users[0] + + +def read_csv_files_from_directory(directory_path): + """ + Reads all CSV files from the specified directory and loads them into pandas DataFrames. + + :param directory_path: The path to the directory containing CSV files. + :return: A dictionary where keys are filenames and values are pandas DataFrames. + """ + dataframes = [] + + for root, _, files in os.walk(directory_path): + for file in files: + if file.endswith(".csv"): + file_path = os.path.join(root, file) + df = pd.read_csv(file_path) + + dataframes.append( + { + "head": convert_dataframe_to_dict(df.head()), + "file_name": file, + "file_path": file_path, + } + ) + + return dataframes + + +async def init_database(): + user = await init_user() + directory_path = os.path.join(os.path.dirname(__file__), "..", "data") + datasets = read_csv_files_from_directory(directory_path) + space_repository = WorkspaceRepository(Workspace, db_session=session) + + space = await space_repository.create_default_space_in_org( + organization_id=user.memberships[0].organization_id, user_id=user.id + ) + dataset_repository = DatasetRepository(Dataset, db_session=session) + space_controller = WorkspaceController( + space_repository=space_repository, dataset_repository=dataset_repository + ) + + await space_controller.reset_space_datasets(space.id) + await space_controller.add_csv_datasets(datasets, user, space.id) + + +def create_app() -> FastAPI: + app_ = FastAPI( + title="PandasAI Server", + description="PandasAI Backend server", + version="1.0.0", + docs_url=None if config.ENVIRONMENT == "production" else "/docs", + redoc_url=None if config.ENVIRONMENT == "production" else "/redoc", + dependencies=[Depends(Logging)], + middleware=make_middleware(), + ) + init_routers(app_=app_) + init_listeners(app_=app_) + + @app_.on_event("startup") + async def on_startup(): + await init_database() + + return app_ + + +app = create_app() diff --git a/server/core/utils/__init__.py b/server/core/utils/__init__.py new file mode 100644 index 000000000..8a9ebf745 --- /dev/null +++ b/server/core/utils/__init__.py @@ -0,0 +1 @@ +from .datetime import * diff --git a/server/core/utils/dataframe.py b/server/core/utils/dataframe.py new file mode 100644 index 000000000..fbd4147e0 --- /dev/null +++ b/server/core/utils/dataframe.py @@ -0,0 +1,19 @@ +import json + +import pandas as pd + + +def convert_dataframe_to_dict(df: pd.DataFrame): + json_data = json.loads( + df.to_json( + orient="split", + date_format="iso", + default_handler=str, + force_ascii=False, + ) + ) + return {"headers": json_data["columns"], "rows": json_data["data"]} + + +def load_df(data: dict): + return pd.DataFrame(data["rows"], columns=data["headers"]) diff --git a/server/core/utils/datetime.py b/server/core/utils/datetime.py new file mode 100644 index 000000000..23a854156 --- /dev/null +++ b/server/core/utils/datetime.py @@ -0,0 +1,9 @@ +import datetime + + +def utcnow() -> datetime: + """ + Returns the current time in UTC but with tzinfo set, as opposed + to datetime.utcnow which does not. + """ + return datetime.datetime.now(datetime.timezone.utc) diff --git a/server/core/utils/json_encoder.py b/server/core/utils/json_encoder.py new file mode 100644 index 000000000..04bd6d5a8 --- /dev/null +++ b/server/core/utils/json_encoder.py @@ -0,0 +1,27 @@ +import datetime +import json +from json import JSONEncoder + +import numpy as np +import pandas as pd + + +class CustomEncoder(JSONEncoder): + def default(self, obj): + if isinstance(obj, (np.integer, np.int64)): + return int(obj) + + if isinstance(obj, (np.floating, np.float32, np.float64)): + return float(obj) + + if isinstance(obj, (np.ndarray,)): + return obj.tolist() + + if isinstance(obj, (pd.Timestamp, datetime.datetime, datetime.date)): + return obj.isoformat() + + return JSONEncoder.default(self, obj) + + +def jsonable_encoder(data: dict) -> dict: + return json.loads(json.dumps(data, cls=CustomEncoder)) diff --git a/server/core/utils/response_parser.py b/server/core/utils/response_parser.py new file mode 100644 index 000000000..676a251b0 --- /dev/null +++ b/server/core/utils/response_parser.py @@ -0,0 +1,84 @@ +import base64 +import json +from typing import Any + +from pandasai.responses.response_parser import IResponseParser + + +class JsonResponseParser(IResponseParser): + _context = None + + def __init__(self, context) -> None: + """ + Initialize the ResponseParser with Context from Agent + Args: + context (Context): context contains the config and logger + """ + self._context = context + + def parse(self, result: dict) -> Any: + """ + Parses result from the chat input + Args: + result (dict): result contains type and value + Raises: + ValueError: if result is not a dictionary with valid key + + Returns: + Any: Returns depending on the user input + """ + if not isinstance(result, dict) or any( + key not in result for key in ["type", "value"] + ): + raise ValueError("Unsupported result format") + + if result["type"] == "plot": + return self.format_plot(result) + elif result["type"] == "dataframe": + json_data = json.loads( + result["value"].to_json( + orient="split", + date_format="iso", + default_handler=str, + force_ascii=False, + ) + ) + return { + "type": "dataframe", + "message": "Dataframe created: ", + "value": { + "headers": json_data["columns"], + "rows": json_data["data"], + }, + } + + result["message"] = result["value"] + + return result + + def format_plot(self, result: dict) -> Any: + """ + Display matplotlib plot against a user query. + + If `open_charts` option set to `False`, the chart won't be displayed. + + Args: + result (dict): result contains type and value + Returns: + Any: Returns depending on the user input + """ + if "data:image/png;base64" not in result["value"]: + with open(result["value"], "rb") as image_file: + image_data = image_file.read() + # Encode the image data to Base64 + base64_image = ( + f"data:image/png;base64,{base64.b64encode(image_data).decode()}" + ) + result = { + "type": result["type"], + "value": base64_image, + } + + result["message"] = "Plot generated: " + + return result diff --git a/server/data/Loan payments data.csv b/server/data/Loan payments data.csv new file mode 100644 index 000000000..ab2bccc4a --- /dev/null +++ b/server/data/Loan payments data.csv @@ -0,0 +1,501 @@ +Loan_ID,loan_status,Principal,terms,effective_date,due_date,paid_off_time,past_due_days,age,education,Gender +xqd20166231,PAIDOFF,1000,30,9/8/2016,10/7/2016,9/14/2016 19:31,,45,High School or Below,male +xqd20168902,PAIDOFF,1000,30,9/8/2016,10/7/2016,10/7/2016 9:00,,50,Bechalor,female +xqd20160003,PAIDOFF,1000,30,9/8/2016,10/7/2016,9/25/2016 16:58,,33,Bechalor,female +xqd20160004,PAIDOFF,1000,15,9/8/2016,9/22/2016,9/22/2016 20:00,,27,college,male +xqd20160005,PAIDOFF,1000,30,9/9/2016,10/8/2016,9/23/2016 21:36,,28,college,female +xqd20160706,PAIDOFF,300,7,9/9/2016,9/15/2016,9/9/2016 13:45,,35,Master or Above,male +xqd20160007,PAIDOFF,1000,30,9/9/2016,10/8/2016,10/7/2016 23:07,,29,college,male +xqd20160008,PAIDOFF,1000,30,9/9/2016,10/8/2016,10/5/2016 20:33,,36,college,male +xqd20160909,PAIDOFF,1000,30,9/9/2016,10/8/2016,10/8/2016 16:00,,28,college,male +xqd20160010,PAIDOFF,800,15,9/10/2016,9/24/2016,9/24/2016 13:00,,26,college,male +xqd20160011,PAIDOFF,300,7,9/10/2016,9/16/2016,9/11/2016 19:11,,29,college,male +xqd20160012,PAIDOFF,1000,15,9/10/2016,10/9/2016,10/9/2016 16:00,,39,High School or Below,male +xqd20160013,PAIDOFF,1000,30,9/10/2016,10/9/2016,10/7/2016 23:32,,26,college,male +xqd20160014,PAIDOFF,900,7,9/10/2016,9/16/2016,9/13/2016 21:57,,26,college,female +xqd20160015,PAIDOFF,1000,7,9/10/2016,9/16/2016,9/15/2016 14:27,,27,High School or Below,male +xqd20160016,PAIDOFF,800,15,9/10/2016,9/24/2016,9/24/2016 16:00,,26,college,male +xqd20160017,PAIDOFF,1000,30,9/10/2016,10/9/2016,9/27/2016 14:21,,40,High School or Below,male +xqd20160018,PAIDOFF,1000,15,9/10/2016,9/24/2016,9/23/2016 18:49,,32,High School or Below,male +xqd20160019,PAIDOFF,1000,30,9/10/2016,10/9/2016,10/5/2016 22:05,,32,High School or Below,male +xqd20160020,PAIDOFF,800,30,9/10/2016,10/9/2016,9/23/2016 7:42,,26,college,male +xqd20160021,PAIDOFF,1000,30,9/10/2016,10/9/2016,10/9/2016 9:00,,26,college,male +xqd20160022,PAIDOFF,1000,30,9/10/2016,10/9/2016,10/8/2016 17:09,,43,High School or Below,female +xqd20160023,PAIDOFF,1000,30,9/10/2016,10/9/2016,10/9/2016 23:00,,25,High School or Below,male +xqd20160024,PAIDOFF,1000,15,9/10/2016,9/24/2016,9/24/2016 13:00,,26,college,male +xqd20160025,PAIDOFF,1000,30,9/10/2016,10/9/2016,10/3/2016 12:50,,26,college,male +xqd20160026,PAIDOFF,1000,30,9/10/2016,10/9/2016,9/29/2016 12:18,,29,High School or Below,male +xqd20160027,PAIDOFF,800,15,9/10/2016,9/24/2016,9/21/2016 20:16,,39,Bechalor,male +xqd20170088,PAIDOFF,1000,15,9/10/2016,9/24/2016,9/23/2016 8:21,,34,Bechalor,male +xqd20160029,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/22/2016 19:17,,31,college,male +xqd20160030,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/9/2016 17:33,,33,college,male +xqd88160031,PAIDOFF,800,15,9/11/2016,9/25/2016,9/24/2016 14:41,,33,High School or Below,male +xqd20160032,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/7/2016 21:48,,37,college,male +xqd20160033,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/9/2016 17:44,,27,college,male +xqd22169034,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/9/2016 7:24,,37,college,male +xqd20160035,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 21:49,,33,college,male +xqd20160036,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 9:00,,29,Bechalor,male +xqd20160037,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 16:00,,27,High School or Below,male +xqd20160038,PAIDOFF,700,15,9/11/2016,9/25/2016,9/25/2016 13:00,,33,High School or Below,male +xqd20160039,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/25/2016 9:00,,24,college,male +xqd20160040,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 11:33,,21,Bechalor,male +xqd20160041,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/25/2016 9:00,,32,college,female +xqd20160042,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 14:36,,30,college,male +xqd20160043,PAIDOFF,1000,7,9/11/2016,9/24/2016,9/24/2016 9:00,,31,Bechalor,male +xqd20160044,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/20/2016 15:00,,30,college,male +xqd20160045,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/21/2016 22:29,,24,Bechalor,female +xqd20160046,PAIDOFF,800,7,9/11/2016,9/17/2016,9/12/2016 22:17,,35,High School or Below,male +xqd20160047,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/8/2016 14:14,,22,High School or Below,male +xqd20160048,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/9/2016 8:53,,32,college,male +xqd20160049,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 9:00,,32,Bechalor,male +xqd20160050,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 19:21,,50,High School or Below,male +xqd20160051,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 13:00,,27,college,female +xqd20160052,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 9:00,,35,Bechalor,female +xqd20160053,PAIDOFF,800,15,9/11/2016,9/25/2016,9/13/2016 4:34,,35,Bechalor,female +xqd20160054,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 9:00,,34,High School or Below,male +xqd20160055,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 16:00,,21,High School or Below,male +xqd20160056,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/25/2016 16:00,,25,college,male +xqd20160057,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 9:00,,27,High School or Below,male +xqd20160058,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/7/2016 2:33,,26,Bechalor,male +xqd20160059,PAIDOFF,800,15,9/11/2016,9/25/2016,9/24/2016 11:40,,44,High School or Below,female +xqd20160060,PAIDOFF,800,15,9/11/2016,9/25/2016,9/22/2016 6:38,,39,Master or Above,male +xqd20160061,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/30/2016 21:12,,34,Bechalor,male +xqd20160062,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/24/2016 13:42,,37,college,male +xqd20160063,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/8/2016 7:25,,34,High School or Below,male +xqd20160064,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/12/2016 11:40,,45,college,male +xqd20160065,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 14:38,,24,High School or Below,male +xqd20160066,PAIDOFF,900,15,9/11/2016,9/25/2016,9/25/2016 23:00,,28,college,male +xqd20160067,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/8/2016 12:04,,28,Bechalor,male +xqd20160068,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 9:00,,37,High School or Below,male +xqd20160069,PAIDOFF,300,7,9/11/2016,9/17/2016,9/14/2016 22:05,,35,college,male +xqd20160070,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/24/2016 13:27,,43,Bechalor,male +xqd20160071,PAIDOFF,800,15,9/11/2016,9/25/2016,9/22/2016 21:18,,29,college,male +xqd20160072,PAIDOFF,800,15,9/11/2016,9/25/2016,9/24/2016 22:53,,29,High School or Below,male +xqd20160073,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 16:00,,33,Bechalor,female +xqd20160074,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/25/2016 9:00,,34,college,male +xqd20160075,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/25/2016 9:00,,25,college,male +xqd20160076,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/8/2016 13:12,,30,High School or Below,male +xqd20160077,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/9/2016 13:49,,31,Bechalor,male +xqd20160078,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 9:00,,35,college,male +xqd20160079,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/30/2016 14:29,,37,college,female +xqd20160080,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 9:00,,44,High School or Below,female +xqd20160081,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/21/2016 16:18,,28,High School or Below,male +xqd20160082,PAIDOFF,1000,7,9/11/2016,9/17/2016,9/13/2016 14:53,,25,college,male +xqd20160083,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/25/2016 9:00,,29,college,male +xqd20160084,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 13:00,,33,college,male +xqd20160085,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 13:00,,37,High School or Below,female +xqd20160086,PAIDOFF,1000,30,9/11/2016,11/9/2016,11/9/2016 9:00,,33,college,male +xqd20160087,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 9:00,,24,High School or Below,female +xqd20160088,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/17/2016 13:01,,27,college,female +xqd20160089,PAIDOFF,800,15,9/11/2016,9/25/2016,9/21/2016 9:35,,43,Bechalor,male +xqd90160090,PAIDOFF,800,15,9/11/2016,9/25/2016,9/24/2016 20:33,,46,High School or Below,female +xqd91160291,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 9:00,,34,college,female +xqd90160092,PAIDOFF,1000,7,9/11/2016,9/17/2016,9/17/2016 9:00,,32,Bechalor,female +xqd90163093,PAIDOFF,800,15,9/11/2016,9/25/2016,9/24/2016 0:12,,38,High School or Below,male +xqd20160094,PAIDOFF,800,15,9/11/2016,9/25/2016,9/21/2016 12:43,,27,High School or Below,male +xqd20167095,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 13:00,,33,High School or Below,male +xqd20160096,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/8/2016 20:49,,36,college,male +xqd20160097,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/20/2016 5:38,,26,High School or Below,male +xqd20160098,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 9:01,,34,college,male +xqd20160099,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 9:01,,22,High School or Below,male +xqd20160100,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/8/2016 16:55,,31,Bechalor,female +xqd20160101,PAIDOFF,1000,7,9/11/2016,9/17/2016,9/17/2016 9:00,,29,High School or Below,male +xqd20160102,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 9:00,,38,college,male +xqd20160103,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 16:00,,30,college,male +xqd20160104,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/25/2016 23:48,,45,High School or Below,male +xqd20160105,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/22/2016 13:15,,35,college,male +xqd20160106,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/23/2016 13:31,,30,college,male +xqd20160107,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/8/2016 17:19,,31,High School or Below,male +xqd20160108,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 9:01,,31,High School or Below,male +xqd20160109,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/9/2016 13:05,,28,college,male +xqd20160110,PAIDOFF,1000,7,9/11/2016,9/24/2016,9/24/2016 13:00,,29,college,male +xqd20160111,PAIDOFF,800,15,9/11/2016,9/25/2016,9/20/2016 20:47,,29,college,male +xqd20160112,PAIDOFF,1000,30,9/11/2016,11/9/2016,11/9/2016 9:00,,27,college,female +xqd20160113,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 9:01,,27,college,male +xqd20160114,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 13:01,,33,college,male +xqd20160115,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/8/2016 21:39,,28,college,male +xqd20160116,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/25/2016 23:00,,25,High School or Below,male +xqd20160117,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/7/2016 14:23,,40,college,male +xqd20160118,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/6/2016 15:25,,23,High School or Below,male +xqd20160119,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/8/2016 6:56,,35,Bechalor,male +xqd20160120,PAIDOFF,800,15,9/11/2016,9/25/2016,9/16/2016 11:58,,24,college,male +xqd20160121,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 16:01,,34,college,male +xqd20160122,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/27/2016 7:02,,22,High School or Below,male +xqd20160123,PAIDOFF,1000,15,9/11/2016,10/25/2016,10/25/2016 9:00,,20,college,male +xqd20160124,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/24/2016 11:02,,23,college,male +xqd20160125,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/29/2016 18:57,,33,college,male +xqd20160126,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 13:01,,26,college,male +xqd20160127,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/25/2016 9:00,,28,High School or Below,male +xqd20160128,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 12:24,,43,High School or Below,male +xqd78160129,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/25/2016 13:00,,34,Bechalor,male +xqd20160130,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/26/2016 4:41,,38,Bechalor,male +xqd20160131,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/22/2016 15:44,,26,High School or Below,male +xqd20160132,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 16:00,,43,High School or Below,male +xqd20160133,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/10/2016 16:13,,26,High School or Below,male +xqd20160134,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/30/2016 7:12,,33,college,female +xqd20160135,PAIDOFF,800,15,9/11/2016,9/25/2016,9/23/2016 11:26,,24,college,male +xqd20160136,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/12/2016 10:26,,30,High School or Below,male +xqd20160137,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 13:00,,32,High School or Below,female +xqd20160138,PAIDOFF,1000,15,9/11/2016,10/25/2016,10/25/2016 9:00,,22,college,male +xqd20160139,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/22/2016 21:45,,47,High School or Below,male +xqd56160140,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/9/2016 20:28,,20,High School or Below,male +xqd20160141,PAIDOFF,1000,30,9/11/2016,10/10/2016,10/1/2016 16:48,,28,High School or Below,male +xqd20160142,PAIDOFF,800,15,9/11/2016,9/25/2016,9/25/2016 9:01,,35,college,male +xqd20160143,PAIDOFF,1000,7,9/11/2016,9/17/2016,9/15/2016 20:36,,27,High School or Below,male +xqd20160144,PAIDOFF,800,15,9/11/2016,9/25/2016,9/21/2016 15:33,,33,college,female +xqd20160145,PAIDOFF,1000,30,9/11/2016,10/10/2016,9/29/2016 13:36,,30,High School or Below,male +xqd20160146,PAIDOFF,1000,15,9/11/2016,9/25/2016,9/22/2016 20:51,,31,college,male +xqd20160147,PAIDOFF,1000,30,9/11/2016,11/9/2016,11/9/2016 23:00,,26,college,female +xqd20160148,PAIDOFF,300,7,9/12/2016,9/18/2016,9/18/2016 9:00,,37,Master or Above,male +xqd20160149,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 9:00,,26,Bechalor,male +xqd20160150,PAIDOFF,800,15,9/12/2016,9/26/2016,9/24/2016 10:14,,35,Bechalor,male +xqd20160151,PAIDOFF,1000,15,9/12/2016,10/26/2016,10/26/2016 9:00,,29,college,male +xqd34160152,PAIDOFF,800,15,9/12/2016,9/26/2016,9/23/2016 20:30,,23,college,male +xqd20160153,PAIDOFF,500,15,9/12/2016,9/26/2016,9/13/2016 20:17,,23,college,female +xqd20160154,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 9:00,,30,college,male +xqd20160155,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/10/2016 7:01,,34,college,male +xqd20160156,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 13:00,,36,High School or Below,female +xqd20160157,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 9:00,,26,Bechalor,male +xqd20160158,PAIDOFF,800,15,9/12/2016,9/26/2016,9/24/2016 14:55,,29,High School or Below,male +xqd12160159,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 9:00,,28,college,female +xqd20160160,PAIDOFF,1000,30,9/12/2016,10/11/2016,9/25/2016 20:56,,27,High School or Below,male +xqd20160161,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/22/2016 10:49,,24,High School or Below,male +xqd20160162,PAIDOFF,800,15,9/12/2016,9/26/2016,9/25/2016 22:09,,31,Bechalor,male +xqd20160163,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 9:00,,28,High School or Below,male +xqd28160164,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 9:00,,27,college,female +xqd20160165,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 19:33,,25,High School or Below,male +xqd20160166,PAIDOFF,1000,30,9/12/2016,11/10/2016,11/10/2016 16:00,,24,High School or Below,male +xqd20160167,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 16:00,,28,college,male +xqd20160168,PAIDOFF,800,30,9/12/2016,10/11/2016,10/11/2016 16:00,,28,college,male +xqd20160169,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 13:00,,35,High School or Below,male +xqd27160170,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 13:00,,38,college,male +xqd20160171,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 16:00,,38,High School or Below,male +xqd20160172,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 16:00,,29,college,male +xqd20160173,PAIDOFF,800,15,9/12/2016,9/26/2016,9/26/2016 13:00,,35,High School or Below,male +xqd20160174,PAIDOFF,1000,30,9/12/2016,10/11/2016,9/17/2016 7:39,,24,college,male +xqd20160175,PAIDOFF,800,15,9/12/2016,9/26/2016,9/22/2016 10:30,,39,High School or Below,male +xqd20160176,PAIDOFF,800,15,9/12/2016,9/26/2016,9/26/2016 13:00,,25,college,male +xqd20160177,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 16:00,,38,High School or Below,male +xqd20160178,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/10/2016 20:41,,30,college,male +xqd20160179,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 9:00,,21,High School or Below,male +xqd20160180,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/10/2016 8:04,,46,college,male +xqd20160181,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/24/2016 11:00,,31,High School or Below,female +xqd20160182,PAIDOFF,300,7,9/12/2016,9/18/2016,9/17/2016 9:25,,29,High School or Below,male +xqd20160183,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/7/2016 11:53,,35,High School or Below,male +xqd20160184,PAIDOFF,800,15,9/12/2016,9/26/2016,9/25/2016 8:39,,30,High School or Below,male +xqd20160185,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 9:00,,27,High School or Below,male +xqd20160186,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/10/2016 20:28,,31,High School or Below,female +xqd20160187,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/1/2016 10:18,,33,Bechalor,male +xqd20160188,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 16:00,,34,High School or Below,male +xqd20160189,PAIDOFF,800,15,9/12/2016,9/26/2016,9/19/2016 21:07,,28,college,male +xqd20160190,PAIDOFF,800,15,9/12/2016,9/26/2016,9/26/2016 9:00,,42,college,male +xqd20160191,PAIDOFF,1000,30,9/12/2016,10/11/2016,9/30/2016 14:38,,32,college,male +xqd20160192,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 13:00,,30,High School or Below,male +xqd20160193,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/14/2016 20:31,,25,High School or Below,female +xqd20160194,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 9:00,,27,High School or Below,female +xqd20160195,PAIDOFF,800,15,9/12/2016,9/26/2016,9/24/2016 16:15,,21,college,male +xqd20160196,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/10/2016 15:49,,24,college,male +xqd20160197,PAIDOFF,1000,30,9/12/2016,11/10/2016,11/10/2016 13:00,,29,college,male +xqd20160198,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/23/2016 10:32,,40,college,male +xqd20160199,PAIDOFF,1000,30,9/12/2016,10/11/2016,9/30/2016 14:03,,29,High School or Below,male +xqd20160200,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/9/2016 14:17,,29,college,male +xqd20160201,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/20/2016 8:26,,30,college,male +xqd20160202,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 23:00,,26,High School or Below,female +xqd20160203,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/24/2016 20:47,,36,High School or Below,male +xqd20160204,PAIDOFF,800,15,9/12/2016,9/26/2016,9/26/2016 16:00,,27,college,male +xqd20160205,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 9:01,,20,college,male +xqd20160206,PAIDOFF,1000,7,9/12/2016,9/18/2016,9/16/2016 14:52,,26,Bechalor,female +xqd20160207,PAIDOFF,1000,30,9/12/2016,11/10/2016,11/10/2016 13:00,,26,college,male +xqd20160208,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/9/2016 10:00,,27,college,male +xqd20160209,PAIDOFF,300,7,9/12/2016,9/18/2016,9/12/2016 14:40,,23,High School or Below,male +xqd20160210,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 16:00,,39,High School or Below,male +xqd20160211,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/23/2016 21:58,,27,High School or Below,male +xqd20160212,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/8/2016 18:48,,30,High School or Below,male +xqd20160213,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/10/2016 16:41,,33,High School or Below,female +xqd20160214,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 13:01,,27,High School or Below,male +xqd20160215,PAIDOFF,1000,30,9/12/2016,10/11/2016,9/16/2016 2:34,,35,High School or Below,male +xqd20160216,PAIDOFF,1000,30,9/12/2016,11/10/2016,11/10/2016 16:00,,29,college,female +xqd20160217,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/21/2016 8:11,,50,High School or Below,male +xqd20160218,PAIDOFF,800,15,9/12/2016,9/26/2016,9/26/2016 9:00,,31,High School or Below,female +xqd20160219,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 13:00,,31,High School or Below,male +xqd20160220,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 13:01,,29,High School or Below,male +xqd20160221,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 23:00,,35,college,male +xqd20160222,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 9:01,,39,college,male +xqd20160223,PAIDOFF,1000,30,9/12/2016,11/10/2016,11/10/2016 13:00,,29,college,male +xqd20160224,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 23:00,,30,High School or Below,male +xqd20160225,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/9/2016 10:00,,33,Bechalor,male +xqd20160226,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 13:01,,26,High School or Below,male +xqd20160227,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/23/2016 14:01,,25,High School or Below,male +xqd20160228,PAIDOFF,800,15,9/12/2016,9/26/2016,9/25/2016 13:29,,37,Bechalor,male +xqd20160229,PAIDOFF,800,15,9/12/2016,9/26/2016,9/25/2016 14:50,,26,High School or Below,male +xqd20160230,PAIDOFF,800,15,9/12/2016,9/26/2016,9/26/2016 9:00,,26,college,male +xqd20160231,PAIDOFF,1000,15,9/12/2016,10/26/2016,10/26/2016 9:00,,27,college,male +xqd20160232,PAIDOFF,1000,7,9/12/2016,9/25/2016,9/25/2016 9:01,,34,college,female +xqd20160233,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/8/2016 15:35,,37,college,male +xqd20160234,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/11/2016 16:01,,36,High School or Below,male +xqd20160235,PAIDOFF,800,15,9/12/2016,9/26/2016,9/26/2016 19:35,,33,High School or Below,male +xqd20160236,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/9/2016 21:28,,30,High School or Below,male +xqd20160237,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/7/2016 16:45,,30,college,male +xqd20160238,PAIDOFF,800,15,9/12/2016,9/26/2016,9/24/2016 12:13,,36,High School or Below,male +xqd20160239,PAIDOFF,1000,15,9/12/2016,10/11/2016,10/11/2016 9:01,,29,college,male +xqd20160240,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/14/2016 23:02,,36,High School or Below,male +xqd20160241,PAIDOFF,1000,30,9/12/2016,10/11/2016,10/8/2016 11:03,,32,High School or Below,male +xqd20160242,PAIDOFF,1000,15,9/12/2016,9/26/2016,9/26/2016 9:00,,29,High School or Below,female +xqd20160243,PAIDOFF,800,15,9/12/2016,9/26/2016,9/26/2016 23:00,,36,Bechalor,male +xqd20160244,PAIDOFF,800,15,9/12/2016,9/26/2016,9/25/2016 19:31,,30,High School or Below,female +xqd20160245,PAIDOFF,1000,7,9/13/2016,9/19/2016,9/14/2016 19:48,,31,college,male +xqd20160246,PAIDOFF,1000,30,9/13/2016,10/12/2016,10/12/2016 23:00,,19,High School or Below,female +xqd20160247,PAIDOFF,800,15,9/13/2016,9/27/2016,9/25/2016 12:48,,26,college,male +xqd20160248,PAIDOFF,800,15,9/13/2016,9/27/2016,9/26/2016 21:18,,34,High School or Below,male +xqd20160249,PAIDOFF,1000,30,9/13/2016,10/12/2016,10/7/2016 10:22,,35,High School or Below,male +xqd20160250,PAIDOFF,1000,15,9/13/2016,9/27/2016,9/26/2016 6:17,,35,Bechalor,female +xqd20160251,PAIDOFF,800,15,9/13/2016,9/27/2016,9/22/2016 16:57,,38,college,male +xqd20160252,PAIDOFF,1000,30,9/13/2016,10/12/2016,10/9/2016 21:57,,29,college,male +xqd20160253,PAIDOFF,1000,30,9/13/2016,10/12/2016,10/4/2016 12:59,,28,High School or Below,male +xqd20160254,PAIDOFF,500,7,9/13/2016,9/19/2016,9/17/2016 20:51,,22,High School or Below,male +xqd20160255,PAIDOFF,1000,30,9/13/2016,10/12/2016,10/12/2016 23:00,,32,college,male +xqd20160256,PAIDOFF,1000,30,9/13/2016,10/12/2016,10/8/2016 15:51,,31,college,male +xqd20160257,PAIDOFF,800,15,9/13/2016,9/27/2016,9/27/2016 9:00,,28,college,male +xqd20160258,PAIDOFF,1000,15,9/13/2016,9/27/2016,9/27/2016 9:00,,37,college,female +xqd20160259,PAIDOFF,1000,7,9/13/2016,9/19/2016,9/16/2016 15:57,,25,college,male +xqd20160260,PAIDOFF,1000,30,9/13/2016,10/12/2016,10/12/2016 9:00,,19,High School or Below,male +xqd20160261,PAIDOFF,800,15,9/13/2016,9/27/2016,9/26/2016 7:48,,51,college,male +xqd20160262,PAIDOFF,1000,15,9/13/2016,9/27/2016,9/21/2016 16:53,,29,High School or Below,male +xqd20160263,PAIDOFF,800,30,9/13/2016,10/12/2016,10/11/2016 0:29,,23,college,female +xqd20160264,PAIDOFF,1000,15,9/13/2016,9/27/2016,9/25/2016 10:37,,30,High School or Below,male +xqd20160265,PAIDOFF,800,15,9/13/2016,9/27/2016,9/27/2016 13:00,,23,college,male +xqd20160266,PAIDOFF,1000,15,9/13/2016,9/27/2016,9/26/2016 15:10,,34,Bechalor,female +xqd20160267,PAIDOFF,800,15,9/13/2016,9/27/2016,9/24/2016 12:46,,31,Bechalor,female +xqd20160268,PAIDOFF,1000,15,9/14/2016,9/28/2016,9/28/2016 9:00,,24,High School or Below,male +xqd20160269,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/13/2016 9:00,,42,High School or Below,male +xqd20160270,PAIDOFF,800,30,9/14/2016,10/13/2016,10/6/2016 12:09,,40,college,female +xqd20160271,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/14/2016 11:03,,29,High School or Below,male +xqd20160272,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/8/2016 17:12,,32,college,female +xqd20160273,PAIDOFF,1000,30,9/14/2016,11/12/2016,11/12/2016 9:00,,28,college,male +xqd20160274,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/13/2016 9:00,,35,High School or Below,male +xqd20160275,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/13/2016 13:00,,30,Bechalor,male +xqd20160276,PAIDOFF,800,15,9/14/2016,9/28/2016,9/27/2016 15:52,,44,college,male +xqd20160277,PAIDOFF,800,15,9/14/2016,9/28/2016,9/28/2016 13:00,,37,High School or Below,male +xqd20160278,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/13/2016 9:00,,31,college,male +xqd20160279,PAIDOFF,800,15,9/14/2016,9/28/2016,9/15/2016 0:43,,36,college,male +xqd20160280,PAIDOFF,800,30,9/14/2016,10/13/2016,10/10/2016 10:25,,31,college,male +xqd20160281,PAIDOFF,800,15,9/14/2016,9/28/2016,9/27/2016 20:41,,42,High School or Below,male +xqd20160282,PAIDOFF,1000,15,9/14/2016,9/28/2016,9/28/2016 9:00,,28,Bechalor,male +xqd20160283,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/6/2016 6:51,,30,college,male +xqd20160284,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/12/2016 6:25,,30,High School or Below,male +xqd20160285,PAIDOFF,1000,15,9/14/2016,9/28/2016,9/27/2016 22:50,,24,Bechalor,male +xqd20160286,PAIDOFF,1000,30,9/14/2016,11/12/2016,11/12/2016 9:00,,34,Bechalor,male +xqd20160287,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/12/2016 12:30,,29,college,male +xqd20160288,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/12/2016 3:49,,38,High School or Below,female +xqd20160289,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/13/2016 13:00,,34,Bechalor,male +xqd20160290,PAIDOFF,800,15,9/14/2016,9/28/2016,9/27/2016 7:48,,28,High School or Below,male +xqd20160291,PAIDOFF,1000,15,9/14/2016,9/28/2016,9/22/2016 9:28,,30,college,female +xqd20160292,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/11/2016 16:33,,41,High School or Below,male +xqd20160293,PAIDOFF,1000,30,9/14/2016,10/13/2016,9/18/2016 16:56,,29,college,male +xqd20160294,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/13/2016 9:00,,37,High School or Below,male +xqd20160295,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/13/2016 13:00,,36,Bechalor,male +xqd20160296,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/13/2016 13:00,,30,college,female +xqd20160297,PAIDOFF,800,15,9/14/2016,9/28/2016,9/21/2016 4:42,,27,college,male +xqd20160298,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/13/2016 9:00,,29,High School or Below,male +xqd20160299,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/13/2016 9:00,,40,High School or Below,male +xqd20160300,PAIDOFF,1000,30,9/14/2016,10/13/2016,10/13/2016 11:00,,28,college,male +xqd20160301,COLLECTION,1000,15,9/9/2016,9/23/2016,,76,29,college,male +xqd20160302,COLLECTION,1000,30,9/9/2016,10/8/2016,,61,37,High School or Below,male +xqd20160303,COLLECTION,1000,30,9/9/2016,10/8/2016,,61,33,High School or Below,male +xqd20160304,COLLECTION,800,15,9/9/2016,9/23/2016,,76,27,college,male +xqd20160305,COLLECTION,800,15,9/9/2016,9/23/2016,,76,24,Bechalor,male +xqd20160306,COLLECTION,1000,15,9/10/2016,9/24/2016,,75,31,High School or Below,female +xqd20160307,COLLECTION,800,15,9/10/2016,10/9/2016,,60,28,college,male +xqd20160308,COLLECTION,1000,30,9/10/2016,10/9/2016,,60,40,High School or Below,male +xqd20160309,COLLECTION,1000,30,9/10/2016,10/9/2016,,60,33,college,male +xqd20160310,COLLECTION,800,15,9/10/2016,9/24/2016,,75,41,college,male +xqd20160311,COLLECTION,1000,30,9/10/2016,10/9/2016,,60,30,college,male +xqd20160312,COLLECTION,800,15,9/10/2016,9/24/2016,,75,26,High School or Below,female +xqd20160313,COLLECTION,1000,30,9/10/2016,10/9/2016,,60,27,High School or Below,male +xqd20160314,COLLECTION,1000,30,9/10/2016,10/9/2016,,60,20,High School or Below,male +xqd20160315,COLLECTION,1000,30,9/10/2016,10/9/2016,,60,24,college,male +xqd20160316,COLLECTION,1000,15,9/10/2016,10/9/2016,,60,26,High School or Below,male +xqd20160317,COLLECTION,1000,30,9/10/2016,10/9/2016,,60,30,High School or Below,male +xqd20160318,COLLECTION,1000,15,9/10/2016,9/24/2016,,75,29,High School or Below,male +xqd20160319,COLLECTION,1000,30,9/10/2016,10/9/2016,,60,22,Bechalor,male +xqd20160320,COLLECTION,1000,15,9/10/2016,9/24/2016,,75,24,Bechalor,male +xqd20160321,COLLECTION,1000,30,9/10/2016,10/9/2016,,60,25,college,male +xqd20160322,COLLECTION,1000,30,9/10/2016,10/9/2016,,60,28,High School or Below,male +xqd20160323,COLLECTION,1000,30,9/10/2016,10/9/2016,,60,37,college,male +xqd20160324,COLLECTION,800,15,9/10/2016,9/24/2016,,75,32,college,male +xqd20160325,COLLECTION,1000,15,9/10/2016,9/24/2016,,75,34,college,male +xqd20160326,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,28,Bechalor,male +xqd20160327,COLLECTION,800,15,9/11/2016,9/25/2016,,74,35,Bechalor,male +xqd20160328,COLLECTION,1000,30,9/11/2016,11/9/2016,,29,27,college,male +xqd20160329,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,24,High School or Below,female +xqd20160330,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,44,Bechalor,male +xqd20160331,COLLECTION,1000,15,9/11/2016,10/25/2016,,44,31,college,male +xqd20160332,COLLECTION,800,15,9/11/2016,9/25/2016,,74,27,college,male +xqd20160333,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,21,High School or Below,male +xqd20160334,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,30,High School or Below,female +xqd20160335,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,38,college,female +xqd20160336,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,34,High School or Below,male +xqd20160337,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,31,college,male +xqd20160338,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,23,High School or Below,male +xqd20160339,COLLECTION,1000,15,9/11/2016,9/25/2016,,74,27,college,female +xqd20160340,COLLECTION,1000,15,9/11/2016,9/25/2016,,74,39,High School or Below,male +xqd20160341,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,30,High School or Below,female +xqd20160342,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,25,college,male +xqd20160343,COLLECTION,1000,15,9/11/2016,9/25/2016,,74,50,Master or Above,male +xqd20160344,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,23,High School or Below,male +xqd20160345,COLLECTION,800,15,9/11/2016,9/25/2016,,74,38,Bechalor,male +xqd20160346,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,27,High School or Below,male +xqd20160347,COLLECTION,1000,30,9/11/2016,11/9/2016,,29,31,High School or Below,male +xqd20160348,COLLECTION,800,15,9/11/2016,9/25/2016,,74,40,college,male +xqd20160349,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,32,High School or Below,male +xqd20160350,COLLECTION,800,15,9/11/2016,9/25/2016,,74,29,college,male +xqd20160351,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,26,High School or Below,male +xqd20160352,COLLECTION,1000,15,9/11/2016,9/25/2016,,74,25,college,male +xqd20160353,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,35,High School or Below,male +xqd20160354,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,41,High School or Below,male +xqd20160355,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,37,High School or Below,male +xqd20160356,COLLECTION,1000,15,9/11/2016,10/10/2016,,59,34,college,male +xqd20160357,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,45,High School or Below,male +xqd20160358,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,26,Bechalor,male +xqd20160359,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,32,college,male +xqd20160360,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,28,High School or Below,male +xqd20160361,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,34,college,male +xqd20160362,COLLECTION,800,15,9/11/2016,9/25/2016,,74,29,High School or Below,male +xqd20160363,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,26,High School or Below,male +xqd20160364,COLLECTION,1000,15,9/11/2016,9/25/2016,,74,26,college,male +xqd20160365,COLLECTION,800,15,9/11/2016,9/25/2016,,74,22,college,male +xqd20160366,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,27,High School or Below,female +xqd20160367,COLLECTION,800,30,9/11/2016,10/10/2016,,59,33,High School or Below,male +xqd20160368,COLLECTION,800,15,9/11/2016,9/25/2016,,74,28,Bechalor,male +xqd20160369,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,24,college,male +xqd20160370,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,37,High School or Below,male +xqd20160371,COLLECTION,800,15,9/11/2016,9/25/2016,,74,36,High School or Below,male +xqd20160372,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,18,college,male +xqd20160373,COLLECTION,800,15,9/11/2016,9/25/2016,,74,25,High School or Below,male +xqd20160374,COLLECTION,1000,15,9/11/2016,9/25/2016,,74,40,High School or Below,male +xqd20182575,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,29,college,male +xqd20160376,COLLECTION,800,15,9/11/2016,9/25/2016,,74,26,High School or Below,female +xqd20151038,COLLECTION,1000,15,9/11/2016,9/25/2016,,74,30,college,male +xqd20160378,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,33,college,male +xqd20197340,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,30,college,male +xqd20160380,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,32,college,male +xqd20160381,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,25,High School or Below,male +xqd20160382,COLLECTION,800,15,9/11/2016,9/25/2016,,74,35,High School or Below,male +xqd20175721,COLLECTION,1000,15,9/11/2016,9/25/2016,,74,30,Bechalor,male +xqd20160384,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,26,High School or Below,male +xqd20160385,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,29,High School or Below,male +xqd20160386,COLLECTION,1000,30,9/11/2016,11/9/2016,,29,26,High School or Below,male +xqd20160387,COLLECTION,800,15,9/11/2016,9/25/2016,,74,46,High School or Below,male +xqd20160388,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,36,High School or Below,male +xqd20160389,COLLECTION,1000,15,9/11/2016,9/25/2016,,74,38,Bechalor,male +xqd20160390,COLLECTION,1000,15,9/11/2016,10/25/2016,,44,32,High School or Below,male +xqd20160391,COLLECTION,1000,15,9/11/2016,9/25/2016,,74,30,college,male +xqd20125284,COLLECTION,800,15,9/11/2016,9/25/2016,,74,35,High School or Below,male +xqd20160393,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,29,college,female +xqd20160394,COLLECTION,1000,30,9/11/2016,11/9/2016,,29,26,college,male +xqd20160395,COLLECTION,800,15,9/11/2016,9/25/2016,,74,32,High School or Below,male +xqd20160396,COLLECTION,1000,30,9/11/2016,10/10/2016,,59,25,High School or Below,male +xqd20160397,COLLECTION,1000,30,9/12/2016,10/11/2016,,58,33,High School or Below,male +xqd20160398,COLLECTION,800,15,9/12/2016,9/26/2016,,73,39,college,male +xqd20160399,COLLECTION,1000,30,9/12/2016,11/10/2016,,28,28,college,male +xqd20160400,COLLECTION,1000,30,9/12/2016,10/11/2016,,58,26,college,male +xqd20160401,COLLECTION_PAIDOFF,1000,30,9/9/2016,10/8/2016,10/10/2016 11:45,2,26,college,male +xqd20160402,COLLECTION_PAIDOFF,1000,15,9/9/2016,9/23/2016,9/27/2016 17:00,4,28,college,male +xqd20320403,COLLECTION_PAIDOFF,1000,30,9/9/2016,11/7/2016,11/20/2016 14:10,13,39,college,male +xqd20160404,COLLECTION_PAIDOFF,1000,15,9/9/2016,9/23/2016,9/28/2016 15:38,5,29,Bechalor,male +xqd20190405,COLLECTION_PAIDOFF,800,15,9/9/2016,9/23/2016,9/26/2016 17:22,3,33,High School or Below,male +xqd20160406,COLLECTION_PAIDOFF,1000,30,9/10/2016,10/9/2016,10/21/2016 14:00,12,27,college,male +xqd20160407,COLLECTION_PAIDOFF,800,15,9/10/2016,9/24/2016,9/26/2016 11:03,2,34,college,male +xqd20160408,COLLECTION_PAIDOFF,1000,30,9/10/2016,10/9/2016,11/5/2016 15:39,27,26,High School or Below,male +xqd20110409,COLLECTION_PAIDOFF,1000,30,9/10/2016,10/9/2016,11/22/2016 15:53,44,28,High School or Below,male +xqd20160410,COLLECTION_PAIDOFF,1000,15,9/10/2016,9/24/2016,9/29/2016 10:30,5,32,Bechalor,male +xqd20160411,COLLECTION_PAIDOFF,800,15,9/10/2016,10/9/2016,10/10/2016 15:18,1,27,college,female +xqd20160412,COLLECTION_PAIDOFF,1000,30,9/10/2016,10/9/2016,11/5/2016 10:49,27,21,college,male +xqd20160413,COLLECTION_PAIDOFF,800,15,9/11/2016,9/25/2016,9/27/2016 17:10,2,39,college,male +xqd20169083,COLLECTION_PAIDOFF,1000,15,9/11/2016,9/25/2016,9/26/2016 11:35,1,38,college,male +xqd20160415,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/12/2016 9:59,2,36,High School or Below,female +xqd20160416,COLLECTION_PAIDOFF,800,15,9/11/2016,9/25/2016,9/27/2016 17:14,2,33,college,male +xqd20160417,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/11/2016 12:45,1,21,college,female +xqd20160418,COLLECTION_PAIDOFF,800,15,9/11/2016,9/25/2016,9/28/2016 11:38,3,25,High School or Below,male +xqd20160419,COLLECTION_PAIDOFF,800,15,9/11/2016,9/25/2016,10/7/2016 13:21,12,29,college,male +xqd20160420,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,11/4/2016 15:37,25,33,High School or Below,male +xqd20160421,COLLECTION_PAIDOFF,1000,15,9/11/2016,9/25/2016,9/28/2016 17:39,3,47,High School or Below,female +xqd20160422,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/12/2016 9:52,2,33,college,male +xqd20160423,COLLECTION_PAIDOFF,800,15,9/11/2016,9/25/2016,9/29/2016 15:12,4,23,High School or Below,male +xqd20160424,COLLECTION_PAIDOFF,1000,15,9/11/2016,10/10/2016,10/12/2016 11:17,2,24,college,male +xqd20880425,COLLECTION_PAIDOFF,1000,30,9/11/2016,11/9/2016,11/10/2016 22:58,1,27,High School or Below,male +xqd20160426,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,11/3/2016 15:23,24,32,Bechalor,male +xqd20160427,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/11/2016 16:44,1,33,college,male +xqd20160428,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/11/2016 11:02,2,27,college,female +xqd20160429,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/12/2016 13:17,2,35,High School or Below,male +xqd20160430,COLLECTION_PAIDOFF,500,15,9/11/2016,10/10/2016,10/11/2016 17:22,1,37,Bechalor,male +xqd20160431,COLLECTION_PAIDOFF,800,15,9/11/2016,9/25/2016,9/28/2016 14:02,3,28,Bechalor,male +xqd20160432,COLLECTION_PAIDOFF,1000,15,9/11/2016,9/25/2016,9/29/2016 13:42,4,33,college,male +xqd20160433,COLLECTION_PAIDOFF,800,7,9/11/2016,9/17/2016,9/19/2016 15:00,2,34,Bechalor,female +xqd20160434,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/12/2016 14:32,2,29,college,male +xqd20160435,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/11/2016 11:33,1,34,Bechalor,male +xqd20160436,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/11/2016 16:27,1,29,Bechalor,male +xqd20790437,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,11/15/2016 15:27,36,24,High School or Below,male +xqd20160438,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/11/2016 16:13,1,34,High School or Below,male +xqd20160439,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/17/2016 10:06,7,25,college,female +xqd20160440,COLLECTION_PAIDOFF,1000,30,9/11/2016,11/9/2016,11/14/2016 13:15,5,24,college,male +xqd20160441,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/24/2016 16:20,14,30,college,male +xqd20160442,COLLECTION_PAIDOFF,1000,15,9/11/2016,9/25/2016,9/27/2016 16:35,2,28,college,male +xqd20160443,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/11/2016 11:48,1,24,High School or Below,male +xqd20160444,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,11/7/2016 19:21,28,26,college,female +xqd20160445,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/12/2016 16:22,2,24,High School or Below,male +xqd20160446,COLLECTION_PAIDOFF,1000,15,9/11/2016,9/25/2016,9/27/2016 17:24,2,29,college,male +xqd20420447,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,11/4/2016 11:07,25,31,college,male +xqd20160448,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,11/2/2016 9:39,23,26,college,male +xqd20160449,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/13/2016 18:18,3,25,High School or Below,male +xqd20160450,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/11/2016 11:29,1,29,college,male +xqd20160451,COLLECTION_PAIDOFF,1000,30,9/11/2016,10/10/2016,10/13/2016 16:27,3,38,college,male +xqd20160452,COLLECTION_PAIDOFF,800,15,9/11/2016,9/25/2016,9/29/2016 11:19,4,41,college,male +xqd20390453,COLLECTION_PAIDOFF,1000,15,9/11/2016,9/25/2016,9/28/2016 11:17,3,26,High School or Below,male +xqd20160454,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/14/2016 11:04,3,26,High School or Below,male +xqd20160455,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/17/2016 17:40,6,35,High School or Below,male +xqd20160456,COLLECTION_PAIDOFF,1000,15,9/12/2016,9/26/2016,9/28/2016 9:42,2,37,college,male +xqd20160457,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,11/18/2016 15:52,38,25,college,male +xqd20160458,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/30/2016 14:19,19,24,college,male +xqd20160459,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/13/2016 15:10,2,34,college,male +xqd20160460,COLLECTION_PAIDOFF,800,15,9/12/2016,9/26/2016,9/28/2016 13:36,2,33,college,male +xqd20490461,COLLECTION_PAIDOFF,800,15,9/12/2016,9/26/2016,9/28/2016 15:34,2,38,Bechalor,male +xqd20160462,COLLECTION_PAIDOFF,1000,30,9/12/2016,11/10/2016,11/17/2016 11:55,7,38,High School or Below,male +xqd20160463,COLLECTION_PAIDOFF,1000,30,9/12/2016,11/10/2016,11/15/2016 18:51,5,26,college,male +xqd20870464,COLLECTION_PAIDOFF,1000,15,9/12/2016,9/26/2016,9/30/2016 10:23,4,37,Bechalor,male +xqd20160465,COLLECTION_PAIDOFF,1000,30,9/12/2016,11/10/2016,11/11/2016 17:17,1,42,High School or Below,female +xqd20169466,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/12/2016 12:54,1,49,High School or Below,female +xqd20160467,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/15/2016 9:48,4,26,High School or Below,male +xqd20160468,COLLECTION_PAIDOFF,1000,15,9/12/2016,10/26/2016,10/27/2016 11:14,1,41,High School or Below,male +xqd20160469,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/15/2016 14:14,4,38,High School or Below,male +xqd25660470,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,12/2/2016 9:45,52,26,High School or Below,male +xqd20160471,COLLECTION_PAIDOFF,1000,15,9/12/2016,9/26/2016,9/28/2016 15:02,2,32,High School or Below,male +xqd20160472,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,11/4/2016 14:46,24,27,Bechalor,male +xqd20160473,COLLECTION_PAIDOFF,800,15,9/12/2016,9/26/2016,11/16/2016 12:12,51,33,college,male +xqd20160474,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/14/2016 19:02,3,30,High School or Below,male +xqd20160475,COLLECTION_PAIDOFF,800,15,9/12/2016,9/26/2016,9/28/2016 11:34,2,26,High School or Below,female +xqd20160476,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,11/9/2016 18:12,29,35,college,female +xqd20160477,COLLECTION_PAIDOFF,800,15,9/12/2016,10/26/2016,10/31/2016 13:07,5,46,college,female +xqd20160478,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/20/2016 17:38,9,27,college,male +xqd20160479,COLLECTION_PAIDOFF,1000,15,9/12/2016,10/11/2016,11/7/2016 8:55,27,22,High School or Below,male +xqd20160480,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/12/2016 18:26,1,27,Bechalor,male +xqd20160481,COLLECTION_PAIDOFF,1000,15,9/12/2016,9/26/2016,10/25/2016 13:44,29,30,Bechalor,male +xqd20160482,COLLECTION_PAIDOFF,1000,15,9/12/2016,9/26/2016,9/29/2016 15:07,3,27,High School or Below,male +xqd20160483,COLLECTION_PAIDOFF,800,15,9/12/2016,9/26/2016,9/27/2016 11:40,1,47,college,male +xqd20160484,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/18/2016 19:08,7,30,college,male +xqd20160485,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/15/2016 9:23,4,26,college,male +xqd20160486,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/14/2016 10:07,3,38,High School or Below,male +xqd20160487,COLLECTION_PAIDOFF,800,15,9/12/2016,9/26/2016,11/21/2016 11:36,56,46,High School or Below,male +xqd20160488,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/13/2016 12:02,2,35,Bechalor,male +xqd20160489,COLLECTION_PAIDOFF,1000,15,9/12/2016,9/26/2016,10/9/2016 19:30,13,45,college,male +xqd20160490,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/12/2016 18:04,1,36,college,male +xqd20160491,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/17/2016 10:53,6,38,High School or Below,male +xqd20160492,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,11/9/2016 13:41,29,27,college,male +xqd20160493,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/25/2016 17:44,14,27,Bechalor,male +xqd20160494,COLLECTION_PAIDOFF,1000,15,9/12/2016,9/26/2016,9/29/2016 12:45,3,29,college,male +xqd20160495,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/13/2016 14:45,2,30,High School or Below,male +xqd20160496,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/14/2016 19:08,3,28,High School or Below,male +xqd20160497,COLLECTION_PAIDOFF,1000,15,9/12/2016,9/26/2016,10/10/2016 20:02,14,26,High School or Below,male +xqd20160498,COLLECTION_PAIDOFF,800,15,9/12/2016,9/26/2016,9/29/2016 11:49,3,30,college,male +xqd20160499,COLLECTION_PAIDOFF,1000,30,9/12/2016,11/10/2016,11/11/2016 22:40,1,38,college,female +xqd20160500,COLLECTION_PAIDOFF,1000,30,9/12/2016,10/11/2016,10/19/2016 11:58,8,28,High School or Below,male diff --git a/server/main.py b/server/main.py new file mode 100644 index 000000000..f17e0bc70 --- /dev/null +++ b/server/main.py @@ -0,0 +1,16 @@ +import os +import uvicorn +from dotenv import load_dotenv + +load_dotenv() + +from core.config import config + +if __name__ == "__main__": + uvicorn.run( + app="core.server:app", + reload=True if config.ENVIRONMENT != "production" else False, + host=os.environ.get("SERVER_HOST", "0.0.0.0"), + port=os.environ.get("SERVER_PORT", 8000), + workers=1, + ) diff --git a/server/migrations/env.py b/server/migrations/env.py new file mode 100644 index 000000000..b7fd55345 --- /dev/null +++ b/server/migrations/env.py @@ -0,0 +1,91 @@ +import asyncio +import os +import sys +from logging.config import fileConfig + +from alembic import context +from sqlalchemy import pool +from sqlalchemy.ext.asyncio import create_async_engine +from dotenv import load_dotenv + +load_dotenv() + +parent_dir = os.path.abspath(os.path.join(os.getcwd(), "..")) +sys.path.append(parent_dir) + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config +fileConfig(config.config_file_name) +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + +print(parent_dir) + + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata + +from app.models import Base + +# For auto generate schemas +from core.config import config as app_config + +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + Calls to context.execute() here emit the given string to the + script output. + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=app_config.POSTGRES_URL, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection): + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_migrations_online(): + """Run migrations in 'online' mode. + In this scenario we need to create an Engine + and associate a connection with the context. + """ + connectable = create_async_engine(app_config.POSTGRES_URL, poolclass=pool.NullPool) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + asyncio.run(run_migrations_online()) diff --git a/server/migrations/script.py.mako b/server/migrations/script.py.mako new file mode 100644 index 000000000..2c0156303 --- /dev/null +++ b/server/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/server/migrations/versions/20240603142118_initial_migration.py b/server/migrations/versions/20240603142118_initial_migration.py new file mode 100644 index 000000000..f376d132f --- /dev/null +++ b/server/migrations/versions/20240603142118_initial_migration.py @@ -0,0 +1,244 @@ +"""initial_migration + +Revision ID: 51e3880da98b +Revises: +Create Date: 2024-06-03 14:21:18.361386 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "51e3880da98b" +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "organization", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("name", sa.String(), nullable=True), + sa.Column("url", sa.String(), nullable=True), + sa.Column("is_default", sa.Boolean(), nullable=True), + sa.Column("settings", sa.JSON(), nullable=True), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_organization_id"), "organization", ["id"], unique=False) + op.create_index( + op.f("ix_organization_name"), "organization", ["name"], unique=False + ) + op.create_table( + "user", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("email", sa.String(length=255), nullable=True), + sa.Column("first_name", sa.String(length=255), nullable=True), + sa.Column("created_at", sa.DateTime(), nullable=True), + sa.Column("password", sa.String(length=255), nullable=True), + sa.Column("verified", sa.Boolean(), nullable=True), + sa.Column("last_name", sa.String(length=255), nullable=True), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_user_email"), "user", ["email"], unique=True) + op.create_index(op.f("ix_user_id"), "user", ["id"], unique=False) + op.create_table( + "api_keys", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("organization_id", sa.UUID(), nullable=True), + sa.Column("api_key", sa.String(length=255), nullable=True), + sa.ForeignKeyConstraint( + ["organization_id"], + ["organization.id"], + ), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_api_keys_id"), "api_keys", ["id"], unique=False) + op.create_table( + "connector", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("type", sa.String(), nullable=False), + sa.Column("config", sa.JSON(), nullable=True), + sa.Column("created_at", sa.DateTime(), nullable=True), + sa.Column("user_id", sa.UUID(), nullable=True), + sa.ForeignKeyConstraint( + ["user_id"], + ["user.id"], + ), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("id", name="uq_connector_id"), + ) + op.create_index(op.f("ix_connector_id"), "connector", ["id"], unique=False) + op.create_table( + "organization_membership", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("user_id", sa.UUID(), nullable=True), + sa.Column("organization_id", sa.UUID(), nullable=True), + sa.Column("role", sa.String(), nullable=True), + sa.Column("verified", sa.Boolean(), nullable=True), + sa.ForeignKeyConstraint( + ["organization_id"], + ["organization.id"], + ), + sa.ForeignKeyConstraint( + ["user_id"], + ["user.id"], + ), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index( + op.f("ix_organization_membership_id"), + "organization_membership", + ["id"], + unique=False, + ) + op.create_table( + "workspace", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("name", sa.String(), nullable=True), + sa.Column("user_id", sa.UUID(), nullable=True), + sa.Column("organization_id", sa.UUID(), nullable=True), + sa.Column("slug", sa.String(), nullable=True), + sa.Column("created_at", sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint( + ["organization_id"], + ["organization.id"], + ), + sa.ForeignKeyConstraint( + ["user_id"], + ["user.id"], + ), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_workspace_id"), "workspace", ["id"], unique=False) + op.create_table( + "dataset", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("name", sa.String(), nullable=True), + sa.Column("table_name", sa.String(), nullable=True), + sa.Column("description", sa.String(), nullable=True), + sa.Column("created_at", sa.DateTime(), nullable=True), + sa.Column("head", sa.JSON(), nullable=True), + sa.Column("user_id", sa.UUID(), nullable=True), + sa.Column("organization_id", sa.UUID(), nullable=True), + sa.Column("connector_id", sa.UUID(), nullable=True), + sa.Column("field_descriptions", sa.JSON(), nullable=True), + sa.Column("filterable_columns", sa.JSON(), nullable=True), + sa.ForeignKeyConstraint( + ["connector_id"], + ["connector.id"], + ), + sa.ForeignKeyConstraint( + ["organization_id"], + ["organization.id"], + ), + sa.ForeignKeyConstraint( + ["user_id"], + ["user.id"], + ), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_dataset_id"), "dataset", ["id"], unique=False) + op.create_table( + "user_conversation", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("workspace_id", sa.UUID(), nullable=True), + sa.Column("user_id", sa.UUID(), nullable=True), + sa.Column("created_at", sa.DateTime(), nullable=True), + sa.Column("valid", sa.Boolean(), nullable=True), + sa.ForeignKeyConstraint( + ["user_id"], + ["user.id"], + ), + sa.ForeignKeyConstraint( + ["workspace_id"], + ["workspace.id"], + ), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index( + op.f("ix_user_conversation_id"), "user_conversation", ["id"], unique=False + ) + op.create_table( + "user_space", + sa.Column("workspace_id", sa.UUID(), nullable=False), + sa.Column("user_id", sa.UUID(), nullable=False), + sa.ForeignKeyConstraint( + ["user_id"], + ["user.id"], + ), + sa.ForeignKeyConstraint( + ["workspace_id"], + ["workspace.id"], + ), + sa.PrimaryKeyConstraint("workspace_id", "user_id"), + ) + op.create_table( + "conversation_message", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("conversation_id", sa.UUID(), nullable=True), + sa.Column("created_at", sa.DateTime(), nullable=True), + sa.Column("query", sa.String(), nullable=True), + sa.Column("response", sa.JSON(), nullable=True), + sa.Column("code_generated", sa.String(), nullable=True), + sa.Column("label", sa.String(), nullable=True), + sa.Column("log_id", sa.UUID(), nullable=True), + sa.Column("settings", sa.JSON(), nullable=True), + sa.ForeignKeyConstraint( + ["conversation_id"], + ["user_conversation.id"], + ), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index( + op.f("ix_conversation_message_id"), "conversation_message", ["id"], unique=False + ) + op.create_table( + "dataset_space", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("dataset_id", sa.UUID(), nullable=True), + sa.Column("workspace_id", sa.UUID(), nullable=True), + sa.ForeignKeyConstraint( + ["dataset_id"], + ["dataset.id"], + ), + sa.ForeignKeyConstraint( + ["workspace_id"], + ["workspace.id"], + ), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_dataset_space_id"), "dataset_space", ["id"], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f("ix_dataset_space_id"), table_name="dataset_space") + op.drop_table("dataset_space") + op.drop_index(op.f("ix_conversation_message_id"), table_name="conversation_message") + op.drop_table("conversation_message") + op.drop_table("user_space") + op.drop_index(op.f("ix_user_conversation_id"), table_name="user_conversation") + op.drop_table("user_conversation") + op.drop_index(op.f("ix_dataset_id"), table_name="dataset") + op.drop_table("dataset") + op.drop_index(op.f("ix_workspace_id"), table_name="workspace") + op.drop_table("workspace") + op.drop_index( + op.f("ix_organization_membership_id"), table_name="organization_membership" + ) + op.drop_table("organization_membership") + op.drop_index(op.f("ix_connector_id"), table_name="connector") + op.drop_table("connector") + op.drop_index(op.f("ix_api_keys_id"), table_name="api_keys") + op.drop_table("api_keys") + op.drop_index(op.f("ix_user_id"), table_name="user") + op.drop_index(op.f("ix_user_email"), table_name="user") + op.drop_table("user") + op.drop_index(op.f("ix_organization_name"), table_name="organization") + op.drop_index(op.f("ix_organization_id"), table_name="organization") + op.drop_table("organization") + # ### end Alembic commands ### diff --git a/server/poetry.lock b/server/poetry.lock new file mode 100644 index 000000000..4e395643f --- /dev/null +++ b/server/poetry.lock @@ -0,0 +1,2168 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + +[[package]] +name = "anyio" +version = "4.4.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + +[[package]] +name = "astor" +version = "0.8.1" +description = "Read/rewrite/write Python ASTs" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +files = [ + {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, + {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, +] + +[[package]] +name = "astroid" +version = "3.2.2" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, + {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, +] + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "asyncpg" +version = "0.29.0" +description = "An asyncio PostgreSQL driver" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "asyncpg-0.29.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169"}, + {file = "asyncpg-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385"}, + {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e6823a7012be8b68301342ba33b4740e5a166f6bbda0aee32bc01638491a22"}, + {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746e80d83ad5d5464cfbf94315eb6744222ab00aa4e522b704322fb182b83610"}, + {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ff8e8109cd6a46ff852a5e6bab8b0a047d7ea42fcb7ca5ae6eaae97d8eacf397"}, + {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97eb024685b1d7e72b1972863de527c11ff87960837919dac6e34754768098eb"}, + {file = "asyncpg-0.29.0-cp310-cp310-win32.whl", hash = "sha256:5bbb7f2cafd8d1fa3e65431833de2642f4b2124be61a449fa064e1a08d27e449"}, + {file = "asyncpg-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:76c3ac6530904838a4b650b2880f8e7af938ee049e769ec2fba7cd66469d7772"}, + {file = "asyncpg-0.29.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4900ee08e85af01adb207519bb4e14b1cae8fd21e0ccf80fac6aa60b6da37b4"}, + {file = "asyncpg-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a65c1dcd820d5aea7c7d82a3fdcb70e096f8f70d1a8bf93eb458e49bfad036ac"}, + {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b52e46f165585fd6af4863f268566668407c76b2c72d366bb8b522fa66f1870"}, + {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc600ee8ef3dd38b8d67421359779f8ccec30b463e7aec7ed481c8346decf99f"}, + {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:039a261af4f38f949095e1e780bae84a25ffe3e370175193174eb08d3cecab23"}, + {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6feaf2d8f9138d190e5ec4390c1715c3e87b37715cd69b2c3dfca616134efd2b"}, + {file = "asyncpg-0.29.0-cp311-cp311-win32.whl", hash = "sha256:1e186427c88225ef730555f5fdda6c1812daa884064bfe6bc462fd3a71c4b675"}, + {file = "asyncpg-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfe73ffae35f518cfd6e4e5f5abb2618ceb5ef02a2365ce64f132601000587d3"}, + {file = "asyncpg-0.29.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6011b0dc29886ab424dc042bf9eeb507670a3b40aece3439944006aafe023178"}, + {file = "asyncpg-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b544ffc66b039d5ec5a7454667f855f7fec08e0dfaf5a5490dfafbb7abbd2cfb"}, + {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d84156d5fb530b06c493f9e7635aa18f518fa1d1395ef240d211cb563c4e2364"}, + {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54858bc25b49d1114178d65a88e48ad50cb2b6f3e475caa0f0c092d5f527c106"}, + {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bde17a1861cf10d5afce80a36fca736a86769ab3579532c03e45f83ba8a09c59"}, + {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:37a2ec1b9ff88d8773d3eb6d3784dc7e3fee7756a5317b67f923172a4748a175"}, + {file = "asyncpg-0.29.0-cp312-cp312-win32.whl", hash = "sha256:bb1292d9fad43112a85e98ecdc2e051602bce97c199920586be83254d9dafc02"}, + {file = "asyncpg-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:2245be8ec5047a605e0b454c894e54bf2ec787ac04b1cb7e0d3c67aa1e32f0fe"}, + {file = "asyncpg-0.29.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0009a300cae37b8c525e5b449233d59cd9868fd35431abc470a3e364d2b85cb9"}, + {file = "asyncpg-0.29.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cad1324dbb33f3ca0cd2074d5114354ed3be2b94d48ddfd88af75ebda7c43cc"}, + {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012d01df61e009015944ac7543d6ee30c2dc1eb2f6b10b62a3f598beb6531548"}, + {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000c996c53c04770798053e1730d34e30cb645ad95a63265aec82da9093d88e7"}, + {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0bfe9c4d3429706cf70d3249089de14d6a01192d617e9093a8e941fea8ee775"}, + {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:642a36eb41b6313ffa328e8a5c5c2b5bea6ee138546c9c3cf1bffaad8ee36dd9"}, + {file = "asyncpg-0.29.0-cp38-cp38-win32.whl", hash = "sha256:a921372bbd0aa3a5822dd0409da61b4cd50df89ae85150149f8c119f23e8c408"}, + {file = "asyncpg-0.29.0-cp38-cp38-win_amd64.whl", hash = "sha256:103aad2b92d1506700cbf51cd8bb5441e7e72e87a7b3a2ca4e32c840f051a6a3"}, + {file = "asyncpg-0.29.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5340dd515d7e52f4c11ada32171d87c05570479dc01dc66d03ee3e150fb695da"}, + {file = "asyncpg-0.29.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e17b52c6cf83e170d3d865571ba574577ab8e533e7361a2b8ce6157d02c665d3"}, + {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f100d23f273555f4b19b74a96840aa27b85e99ba4b1f18d4ebff0734e78dc090"}, + {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48e7c58b516057126b363cec8ca02b804644fd012ef8e6c7e23386b7d5e6ce83"}, + {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9ea3f24eb4c49a615573724d88a48bd1b7821c890c2effe04f05382ed9e8810"}, + {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8d36c7f14a22ec9e928f15f92a48207546ffe68bc412f3be718eedccdf10dc5c"}, + {file = "asyncpg-0.29.0-cp39-cp39-win32.whl", hash = "sha256:797ab8123ebaed304a1fad4d7576d5376c3a006a4100380fb9d517f0b59c1ab2"}, + {file = "asyncpg-0.29.0-cp39-cp39-win_amd64.whl", hash = "sha256:cce08a178858b426ae1aa8409b5cc171def45d4293626e7aa6510696d46decd8"}, + {file = "asyncpg-0.29.0.tar.gz", hash = "sha256:d1c49e1f44fffafd9a55e1a9b101590859d881d639ea2922516f5d9c512d354e"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.12.0\""} + +[package.extras] +docs = ["Sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3)"] + +[[package]] +name = "bcrypt" +version = "3.1.7" +description = "Modern password hashing for your software and your servers" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "bcrypt-3.1.7-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:d7bdc26475679dd073ba0ed2766445bb5b20ca4793ca0db32b399dccc6bc84b7"}, + {file = "bcrypt-3.1.7-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:69361315039878c0680be456640f8705d76cb4a3a3fe1e057e0f261b74be4b31"}, + {file = "bcrypt-3.1.7-cp27-cp27m-win32.whl", hash = "sha256:5432dd7b34107ae8ed6c10a71b4397f1c853bd39a4d6ffa7e35f40584cffd161"}, + {file = "bcrypt-3.1.7-cp27-cp27m-win_amd64.whl", hash = "sha256:9fe92406c857409b70a38729dbdf6578caf9228de0aef5bc44f859ffe971a39e"}, + {file = "bcrypt-3.1.7-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:763669a367869786bb4c8fcf731f4175775a5b43f070f50f46f0b59da45375d0"}, + {file = "bcrypt-3.1.7-cp34-abi3-macosx_10_6_intel.whl", hash = "sha256:a190f2a5dbbdbff4b74e3103cef44344bc30e61255beb27310e2aec407766052"}, + {file = "bcrypt-3.1.7-cp34-abi3-manylinux1_x86_64.whl", hash = "sha256:c9457fa5c121e94a58d6505cadca8bed1c64444b83b3204928a866ca2e599105"}, + {file = "bcrypt-3.1.7-cp34-cp34m-win32.whl", hash = "sha256:8b10acde4e1919d6015e1df86d4c217d3b5b01bb7744c36113ea43d529e1c3de"}, + {file = "bcrypt-3.1.7-cp34-cp34m-win_amd64.whl", hash = "sha256:cb93f6b2ab0f6853550b74e051d297c27a638719753eb9ff66d1e4072be67133"}, + {file = "bcrypt-3.1.7-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:436a487dec749bca7e6e72498a75a5fa2433bda13bac91d023e18df9089ae0b8"}, + {file = "bcrypt-3.1.7-cp35-cp35m-win32.whl", hash = "sha256:6fe49a60b25b584e2f4ef175b29d3a83ba63b3a4df1b4c0605b826668d1b6be5"}, + {file = "bcrypt-3.1.7-cp35-cp35m-win_amd64.whl", hash = "sha256:a595c12c618119255c90deb4b046e1ca3bcfad64667c43d1166f2b04bc72db09"}, + {file = "bcrypt-3.1.7-cp36-cp36m-win32.whl", hash = "sha256:74a015102e877d0ccd02cdeaa18b32aa7273746914a6c5d0456dd442cb65b99c"}, + {file = "bcrypt-3.1.7-cp36-cp36m-win_amd64.whl", hash = "sha256:0258f143f3de96b7c14f762c770f5fc56ccd72f8a1857a451c1cd9a655d9ac89"}, + {file = "bcrypt-3.1.7-cp37-cp37m-win32.whl", hash = "sha256:19a4b72a6ae5bb467fea018b825f0a7d917789bcfe893e53f15c92805d187294"}, + {file = "bcrypt-3.1.7-cp37-cp37m-win_amd64.whl", hash = "sha256:ff032765bb8716d9387fd5376d987a937254b0619eff0972779515b5c98820bc"}, + {file = "bcrypt-3.1.7-cp38-cp38-win32.whl", hash = "sha256:ce4e4f0deb51d38b1611a27f330426154f2980e66582dc5f438aad38b5f24fc1"}, + {file = "bcrypt-3.1.7-cp38-cp38-win_amd64.whl", hash = "sha256:6305557019906466fc42dbc53b46da004e72fd7a551c044a827e572c82191752"}, + {file = "bcrypt-3.1.7.tar.gz", hash = "sha256:0b0069c752ec14172c5f78208f1863d7ad6755a6fae6fe76ec2c80d13be41e42"}, +] + +[package.dependencies] +cffi = ">=1.1" +six = ">=1.4.1" + +[package.extras] +tests = ["pytest (>=3.2.1,!=3.3.0)"] + +[[package]] +name = "behave" +version = "1.2.6" +description = "behave is behaviour-driven development, Python style" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "behave-1.2.6-py2.py3-none-any.whl", hash = "sha256:ebda1a6c9e5bfe95c5f9f0a2794e01c7098b3dde86c10a95d8621c5907ff6f1c"}, + {file = "behave-1.2.6.tar.gz", hash = "sha256:b9662327aa53294c1351b0a9c369093ccec1d21026f050c3bd9b3e5cccf81a86"}, +] + +[package.dependencies] +parse = ">=1.8.2" +parse-type = ">=0.4.2" +six = ">=1.11" + +[package.extras] +develop = ["coverage", "invoke (>=0.21.0)", "modernize (>=0.5)", "path.py (>=8.1.2)", "pathlib", "pycmd", "pylint", "pytest (>=3.0)", "pytest-cov", "tox"] +docs = ["sphinx (>=1.6)", "sphinx-bootstrap-theme (>=0.6)"] + +[[package]] +name = "black" +version = "23.12.1" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, + {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, + {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, + {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, + {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, + {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, + {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, + {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, + {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, + {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, + {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, + {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, + {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, + {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, + {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, + {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, + {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, + {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, + {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, + {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, + {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, + {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "contourpy" +version = "1.2.1" +description = "Python library for calculating contours of 2D quadrilateral grids" +optional = false +python-versions = ">=3.9" +files = [ + {file = "contourpy-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd7c23df857d488f418439686d3b10ae2fbf9bc256cd045b37a8c16575ea1040"}, + {file = "contourpy-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b9eb0ca724a241683c9685a484da9d35c872fd42756574a7cfbf58af26677fd"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c75507d0a55378240f781599c30e7776674dbaf883a46d1c90f37e563453480"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11959f0ce4a6f7b76ec578576a0b61a28bdc0696194b6347ba3f1c53827178b9"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb3315a8a236ee19b6df481fc5f997436e8ade24a9f03dfdc6bd490fea20c6da"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39f3ecaf76cd98e802f094e0d4fbc6dc9c45a8d0c4d185f0f6c2234e14e5f75b"}, + {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:94b34f32646ca0414237168d68a9157cb3889f06b096612afdd296003fdd32fd"}, + {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:457499c79fa84593f22454bbd27670227874cd2ff5d6c84e60575c8b50a69619"}, + {file = "contourpy-1.2.1-cp310-cp310-win32.whl", hash = "sha256:ac58bdee53cbeba2ecad824fa8159493f0bf3b8ea4e93feb06c9a465d6c87da8"}, + {file = "contourpy-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9cffe0f850e89d7c0012a1fb8730f75edd4320a0a731ed0c183904fe6ecfc3a9"}, + {file = "contourpy-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6022cecf8f44e36af10bd9118ca71f371078b4c168b6e0fab43d4a889985dbb5"}, + {file = "contourpy-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef5adb9a3b1d0c645ff694f9bca7702ec2c70f4d734f9922ea34de02294fdf72"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6150ffa5c767bc6332df27157d95442c379b7dce3a38dff89c0f39b63275696f"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c863140fafc615c14a4bf4efd0f4425c02230eb8ef02784c9a156461e62c965"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00e5388f71c1a0610e6fe56b5c44ab7ba14165cdd6d695429c5cd94021e390b2"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4492d82b3bc7fbb7e3610747b159869468079fe149ec5c4d771fa1f614a14df"}, + {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49e70d111fee47284d9dd867c9bb9a7058a3c617274900780c43e38d90fe1205"}, + {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b59c0ffceff8d4d3996a45f2bb6f4c207f94684a96bf3d9728dbb77428dd8cb8"}, + {file = "contourpy-1.2.1-cp311-cp311-win32.whl", hash = "sha256:7b4182299f251060996af5249c286bae9361fa8c6a9cda5efc29fe8bfd6062ec"}, + {file = "contourpy-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2855c8b0b55958265e8b5888d6a615ba02883b225f2227461aa9127c578a4922"}, + {file = "contourpy-1.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:62828cada4a2b850dbef89c81f5a33741898b305db244904de418cc957ff05dc"}, + {file = "contourpy-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:309be79c0a354afff9ff7da4aaed7c3257e77edf6c1b448a779329431ee79d7e"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e785e0f2ef0d567099b9ff92cbfb958d71c2d5b9259981cd9bee81bd194c9a4"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cac0a8f71a041aa587410424ad46dfa6a11f6149ceb219ce7dd48f6b02b87a7"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af3f4485884750dddd9c25cb7e3915d83c2db92488b38ccb77dd594eac84c4a0"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce6889abac9a42afd07a562c2d6d4b2b7134f83f18571d859b25624a331c90b"}, + {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a1eea9aecf761c661d096d39ed9026574de8adb2ae1c5bd7b33558af884fb2ce"}, + {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:187fa1d4c6acc06adb0fae5544c59898ad781409e61a926ac7e84b8f276dcef4"}, + {file = "contourpy-1.2.1-cp312-cp312-win32.whl", hash = "sha256:c2528d60e398c7c4c799d56f907664673a807635b857df18f7ae64d3e6ce2d9f"}, + {file = "contourpy-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:1a07fc092a4088ee952ddae19a2b2a85757b923217b7eed584fdf25f53a6e7ce"}, + {file = "contourpy-1.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb6834cbd983b19f06908b45bfc2dad6ac9479ae04abe923a275b5f48f1a186b"}, + {file = "contourpy-1.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1d59e739ab0e3520e62a26c60707cc3ab0365d2f8fecea74bfe4de72dc56388f"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd3db01f59fdcbce5b22afad19e390260d6d0222f35a1023d9adc5690a889364"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a12a813949e5066148712a0626895c26b2578874e4cc63160bb007e6df3436fe"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe0ccca550bb8e5abc22f530ec0466136379c01321fd94f30a22231e8a48d985"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1d59258c3c67c865435d8fbeb35f8c59b8bef3d6f46c1f29f6123556af28445"}, + {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f32c38afb74bd98ce26de7cc74a67b40afb7b05aae7b42924ea990d51e4dac02"}, + {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d31a63bc6e6d87f77d71e1abbd7387ab817a66733734883d1fc0021ed9bfa083"}, + {file = "contourpy-1.2.1-cp39-cp39-win32.whl", hash = "sha256:ddcb8581510311e13421b1f544403c16e901c4e8f09083c881fab2be80ee31ba"}, + {file = "contourpy-1.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:10a37ae557aabf2509c79715cd20b62e4c7c28b8cd62dd7d99e5ed3ce28c3fd9"}, + {file = "contourpy-1.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a31f94983fecbac95e58388210427d68cd30fe8a36927980fab9c20062645609"}, + {file = "contourpy-1.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef2b055471c0eb466033760a521efb9d8a32b99ab907fc8358481a1dd29e3bd3"}, + {file = "contourpy-1.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b33d2bc4f69caedcd0a275329eb2198f560b325605810895627be5d4b876bf7f"}, + {file = "contourpy-1.2.1.tar.gz", hash = "sha256:4d8908b3bee1c889e547867ca4cdc54e5ab6be6d3e078556814a22457f49423c"}, +] + +[package.dependencies] +numpy = ">=1.20" + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.8.0)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] + +[[package]] +name = "cycler" +version = "0.12.1" +description = "Composable style cycles" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, + {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, +] + +[package.extras] +docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] +tests = ["pytest", "pytest-cov", "pytest-xdist"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "distro" +version = "1.9.0" +description = "Distro - an OS platform information API" +optional = false +python-versions = ">=3.6" +files = [ + {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, + {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, +] + +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "duckdb" +version = "0.10.3" +description = "DuckDB in-process database" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "duckdb-0.10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd25cc8d001c09a19340739ba59d33e12a81ab285b7a6bed37169655e1cefb31"}, + {file = "duckdb-0.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f9259c637b917ca0f4c63887e8d9b35ec248f5d987c886dfc4229d66a791009"}, + {file = "duckdb-0.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b48f5f1542f1e4b184e6b4fc188f497be8b9c48127867e7d9a5f4a3e334f88b0"}, + {file = "duckdb-0.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e327f7a3951ea154bb56e3fef7da889e790bd9a67ca3c36afc1beb17d3feb6d6"}, + {file = "duckdb-0.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d8b20ed67da004b4481973f4254fd79a0e5af957d2382eac8624b5c527ec48c"}, + {file = "duckdb-0.10.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d37680b8d7be04e4709db3a66c8b3eb7ceba2a5276574903528632f2b2cc2e60"}, + {file = "duckdb-0.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d34b86d6a2a6dfe8bb757f90bfe7101a3bd9e3022bf19dbddfa4b32680d26a9"}, + {file = "duckdb-0.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:73b1cb283ca0f6576dc18183fd315b4e487a545667ffebbf50b08eb4e8cdc143"}, + {file = "duckdb-0.10.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d917dde19fcec8cadcbef1f23946e85dee626ddc133e1e3f6551f15a61a03c61"}, + {file = "duckdb-0.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46757e0cf5f44b4cb820c48a34f339a9ccf83b43d525d44947273a585a4ed822"}, + {file = "duckdb-0.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:338c14d8ac53ac4aa9ec03b6f1325ecfe609ceeb72565124d489cb07f8a1e4eb"}, + {file = "duckdb-0.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:651fcb429602b79a3cf76b662a39e93e9c3e6650f7018258f4af344c816dab72"}, + {file = "duckdb-0.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3ae3c73b98b6215dab93cc9bc936b94aed55b53c34ba01dec863c5cab9f8e25"}, + {file = "duckdb-0.10.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56429b2cfe70e367fb818c2be19f59ce2f6b080c8382c4d10b4f90ba81f774e9"}, + {file = "duckdb-0.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b46c02c2e39e3676b1bb0dc7720b8aa953734de4fd1b762e6d7375fbeb1b63af"}, + {file = "duckdb-0.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:bcd460feef56575af2c2443d7394d405a164c409e9794a4d94cb5fdaa24a0ba4"}, + {file = "duckdb-0.10.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e229a7c6361afbb0d0ab29b1b398c10921263c52957aefe3ace99b0426fdb91e"}, + {file = "duckdb-0.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:732b1d3b6b17bf2f32ea696b9afc9e033493c5a3b783c292ca4b0ee7cc7b0e66"}, + {file = "duckdb-0.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5380d4db11fec5021389fb85d614680dc12757ef7c5881262742250e0b58c75"}, + {file = "duckdb-0.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:468a4e0c0b13c55f84972b1110060d1b0f854ffeb5900a178a775259ec1562db"}, + {file = "duckdb-0.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fa1e7ff8d18d71defa84e79f5c86aa25d3be80d7cb7bc259a322de6d7cc72da"}, + {file = "duckdb-0.10.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed1063ed97c02e9cf2e7fd1d280de2d1e243d72268330f45344c69c7ce438a01"}, + {file = "duckdb-0.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:22f2aad5bb49c007f3bfcd3e81fdedbc16a2ae41f2915fc278724ca494128b0c"}, + {file = "duckdb-0.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:8f9e2bb00a048eb70b73a494bdc868ce7549b342f7ffec88192a78e5a4e164bd"}, + {file = "duckdb-0.10.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6c2fc49875b4b54e882d68703083ca6f84b27536d57d623fc872e2f502b1078"}, + {file = "duckdb-0.10.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a66c125d0c30af210f7ee599e7821c3d1a7e09208196dafbf997d4e0cfcb81ab"}, + {file = "duckdb-0.10.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99dd7a1d901149c7a276440d6e737b2777e17d2046f5efb0c06ad3b8cb066a6"}, + {file = "duckdb-0.10.3-cp37-cp37m-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5ec3bbdb209e6095d202202893763e26c17c88293b88ef986b619e6c8b6715bd"}, + {file = "duckdb-0.10.3-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:2b3dec4ef8ed355d7b7230b40950b30d0def2c387a2e8cd7efc80b9d14134ecf"}, + {file = "duckdb-0.10.3-cp37-cp37m-win_amd64.whl", hash = "sha256:04129f94fb49bba5eea22f941f0fb30337f069a04993048b59e2811f52d564bc"}, + {file = "duckdb-0.10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d75d67024fc22c8edfd47747c8550fb3c34fb1cbcbfd567e94939ffd9c9e3ca7"}, + {file = "duckdb-0.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f3796e9507c02d0ddbba2e84c994fae131da567ce3d9cbb4cbcd32fadc5fbb26"}, + {file = "duckdb-0.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:78e539d85ebd84e3e87ec44d28ad912ca4ca444fe705794e0de9be3dd5550c11"}, + {file = "duckdb-0.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a99b67ac674b4de32073e9bc604b9c2273d399325181ff50b436c6da17bf00a"}, + {file = "duckdb-0.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1209a354a763758c4017a1f6a9f9b154a83bed4458287af9f71d84664ddb86b6"}, + {file = "duckdb-0.10.3-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b735cea64aab39b67c136ab3a571dbf834067f8472ba2f8bf0341bc91bea820"}, + {file = "duckdb-0.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:816ffb9f758ed98eb02199d9321d592d7a32a6cb6aa31930f4337eb22cfc64e2"}, + {file = "duckdb-0.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:1631184b94c3dc38b13bce4045bf3ae7e1b0ecbfbb8771eb8d751d8ffe1b59b3"}, + {file = "duckdb-0.10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fb98c35fc8dd65043bc08a2414dd9f59c680d7e8656295b8969f3f2061f26c52"}, + {file = "duckdb-0.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e75c9f5b6a92b2a6816605c001d30790f6d67ce627a2b848d4d6040686efdf9"}, + {file = "duckdb-0.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae786eddf1c2fd003466e13393b9348a44b6061af6fe7bcb380a64cac24e7df7"}, + {file = "duckdb-0.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9387da7b7973707b0dea2588749660dd5dd724273222680e985a2dd36787668"}, + {file = "duckdb-0.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:538f943bf9fa8a3a7c4fafa05f21a69539d2c8a68e557233cbe9d989ae232899"}, + {file = "duckdb-0.10.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6930608f35025a73eb94252964f9f19dd68cf2aaa471da3982cf6694866cfa63"}, + {file = "duckdb-0.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:03bc54a9cde5490918aad82d7d2a34290e3dfb78d5b889c6626625c0f141272a"}, + {file = "duckdb-0.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:372b6e3901d85108cafe5df03c872dfb6f0dbff66165a0cf46c47246c1957aa0"}, + {file = "duckdb-0.10.3.tar.gz", hash = "sha256:c5bd84a92bc708d3a6adffe1f554b94c6e76c795826daaaf482afc3d9c636971"}, +] + +[[package]] +name = "ecdsa" +version = "0.19.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.6" +files = [ + {file = "ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a"}, + {file = "ecdsa-0.19.0.tar.gz", hash = "sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "email-validator" +version = "1.3.1" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.5" +files = [ + {file = "email_validator-1.3.1-py2.py3-none-any.whl", hash = "sha256:49a72f5fa6ed26be1c964f0567d931d10bf3fdeeacdf97bc26ef1cd2a44e0bda"}, + {file = "email_validator-1.3.1.tar.gz", hash = "sha256:d178c5c6fa6c6824e9b04f199cf23e79ac15756786573c190d2ad13089411ad2"}, +] + +[package.dependencies] +dnspython = ">=1.15.0" +idna = ">=2.0.0" + +[[package]] +name = "faker" +version = "19.13.0" +description = "Faker is a Python package that generates fake data for you." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Faker-19.13.0-py3-none-any.whl", hash = "sha256:da880a76322db7a879c848a0771e129338e0a680a9f695fd9a3e7a6ac82b45e1"}, + {file = "Faker-19.13.0.tar.gz", hash = "sha256:14ccb0aec342d33aa3889a864a56e5b3c2d56bce1b89f9189f4fbc128b9afc1e"}, +] + +[package.dependencies] +python-dateutil = ">=2.4" + +[[package]] +name = "fastapi" +version = "0.92.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.7" +files = [ + {file = "fastapi-0.92.0-py3-none-any.whl", hash = "sha256:ae7b97c778e2f2ec3fb3cb4fb14162129411d99907fb71920f6d69a524340ebf"}, + {file = "fastapi-0.92.0.tar.gz", hash = "sha256:023a0f5bd2c8b2609014d3bba1e14a1d7df96c6abea0a73070621c9862b9a4de"}, +] + +[package.dependencies] +pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" +starlette = ">=0.25.0,<0.26.0" + +[package.extras] +all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.138)", "uvicorn[standard] (>=0.12.0,<0.21.0)"] +doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer[all] (>=0.6.1,<0.8.0)"] +test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.10.0)", "coverage[toml] (>=6.5.0,<8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<1.4.43)", "types-orjson (==3.6.2)", "types-ujson (==5.6.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] + +[[package]] +name = "fastapi-event" +version = "0.1.3" +description = "Event dispatcher for FastAPI" +optional = false +python-versions = ">=3.7" +files = [ + {file = "fastapi-event-0.1.3.tar.gz", hash = "sha256:6412100a0f86516aa8638dcdc8dacbadaeec723cbf2e2ebf14adb5f18afe9b44"}, +] + +[package.dependencies] +fastapi = "*" +pydantic = "*" + +[[package]] +name = "fonttools" +version = "4.52.4" +description = "Tools to manipulate font files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.52.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb8cd6559f0ae3a8f5e146f80ab2a90ad0325a759be8d48ee82758a0b89fa0aa"}, + {file = "fonttools-4.52.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ecb88318ff249bd2a715e7aec36774ce7ae3441128007ef72a39a60601f4a8f"}, + {file = "fonttools-4.52.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9a22cf1adaae7b2ba2ed7d8651a4193a4f348744925b4b740e6b38a94599c5b"}, + {file = "fonttools-4.52.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8873d6edd1dae5c088dd3d61c9fd4dd80c827c486fa224d368233e7f33dc98af"}, + {file = "fonttools-4.52.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:73ba38b98c012957940a04d9eb5439b42565ac892bba8cfc32e10d88e73921fe"}, + {file = "fonttools-4.52.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9725687db3c1cef13c0f40b380c3c15bea0113f4d0231b204d58edd5f2a53d90"}, + {file = "fonttools-4.52.4-cp310-cp310-win32.whl", hash = "sha256:9180775c9535389a665cae7c5282f8e07754beabf59b66aeba7f6bfeb32a3652"}, + {file = "fonttools-4.52.4-cp310-cp310-win_amd64.whl", hash = "sha256:46cc5d06ee05fd239c45d7935aaffd060ee773a88b97e901df50478247472643"}, + {file = "fonttools-4.52.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d272c7e173c3085308345ccc7fb2ad6ce7f415d777791dd6ce4e8140e354d09c"}, + {file = "fonttools-4.52.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:21921e5855c399d10ddfc373538b425cabcf8b3258720b51450909e108896450"}, + {file = "fonttools-4.52.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f6001814ec5e0c961cabe89642f7e8d7e07892b565057aa526569b9ebb711c"}, + {file = "fonttools-4.52.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b0b9eb0f55dce9c7278ad4175f1cbaed23b799dce5ecc20e3213da241584140"}, + {file = "fonttools-4.52.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:70d87f2099006304d33438bdaa5101953b7e22e23a93b1c7b7ed0f32ff44b423"}, + {file = "fonttools-4.52.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e176249292eccd89f81d39f514f2b5e8c75dfc9cef8653bdc3021d06697e9eff"}, + {file = "fonttools-4.52.4-cp311-cp311-win32.whl", hash = "sha256:bb7d206fa5ba6e082ba5d5e1b7107731029fc3a55c71c48de65121710d817986"}, + {file = "fonttools-4.52.4-cp311-cp311-win_amd64.whl", hash = "sha256:346d08ff92e577b2dc5a0c228487667d23fe2da35a8b9a8bba22c2b6ba8be21c"}, + {file = "fonttools-4.52.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d2cc7906bc0afdd2689aaf88b910307333b1f936262d1d98f25dbf8a5eb2e829"}, + {file = "fonttools-4.52.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:00d9abf4b400f98fb895566eb298f60432b4b29048e3dc02807427b09a06604e"}, + {file = "fonttools-4.52.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b419207e53db1599b3d385afd4bca6692c219d53732890d0814a2593104d0e2"}, + {file = "fonttools-4.52.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf694159528022daa71b1777cb6ec9e0ebbdd29859f3e9c845826cafaef4ca29"}, + {file = "fonttools-4.52.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9a5d1b0475050056d2e3bc378014f2ea2230e8ae434eeac8dfb182aa8efaf642"}, + {file = "fonttools-4.52.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4c3ad89204c2d7f419436f1d6fde681b070c5e20b888beb57ccf92f640628cc9"}, + {file = "fonttools-4.52.4-cp312-cp312-win32.whl", hash = "sha256:1dc626de4b204d025d029e646bae8fdbf5acd9217158283a567f4b523fda3bae"}, + {file = "fonttools-4.52.4-cp312-cp312-win_amd64.whl", hash = "sha256:309b617942041073ffa96090d320b99d75648ed16e0c67fb1aa7788e06c834de"}, + {file = "fonttools-4.52.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8b186cd6b8844f6cf04a7e0a174bc3649d3deddbfc10dc59846a4381f796d348"}, + {file = "fonttools-4.52.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9ed23a03b7d9f0e29ca0679eafe5152aeccb0580312a3fc36f0662e178b4791b"}, + {file = "fonttools-4.52.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b53386214197bd5b3e3c753895bad691de84726ced3c222a59cde1dd12d57b"}, + {file = "fonttools-4.52.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7467161f1eed557dbcec152d5ee95540200b1935709fa73307da16bc0b7ca361"}, + {file = "fonttools-4.52.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:b4cba644e2515d685d4ee3ca2fbb5d53930a0e9ec2cf332ed704dc341b145878"}, + {file = "fonttools-4.52.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:890e7a657574610330e42dd1e38d3b9e0a8cb0eff3da080f80995460a256d3dd"}, + {file = "fonttools-4.52.4-cp38-cp38-win32.whl", hash = "sha256:7dccf4666f716e5e0753f0fa28dad2f4431154c87747bc781c838b8a5dca990e"}, + {file = "fonttools-4.52.4-cp38-cp38-win_amd64.whl", hash = "sha256:a791f002d1b717268235cfae7e4957b7fd132e92e2c5400e521bf191f1b3a9a5"}, + {file = "fonttools-4.52.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:05e4291db6af66f466a203d9922e4c1d3e18ef16868f76f10b00e2c3b9814df2"}, + {file = "fonttools-4.52.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a64e72d2c144630e017ac9c1c416ddf8ac43bef9a083bf81fe08c0695f0baa95"}, + {file = "fonttools-4.52.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebb183ed8b789cece0bd6363121913fb6da4034af89a2fa5408e42a1592889a8"}, + {file = "fonttools-4.52.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4daf2751a98c69d9620717826ed6c5743b662ef0ae7bb33dc6c205425e48eba"}, + {file = "fonttools-4.52.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:15efb2ba4b8c2d012ee0bb7a850c2e4780c530cc83ec8e843b2a97f8b3a5fd4b"}, + {file = "fonttools-4.52.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:35af630404223273f1d7acd4761f399131c62820366f53eac029337069f5826a"}, + {file = "fonttools-4.52.4-cp39-cp39-win32.whl", hash = "sha256:d0184aa88865339d96f7f452e8c5b621186ef7638744d78bf9b775d67e206819"}, + {file = "fonttools-4.52.4-cp39-cp39-win_amd64.whl", hash = "sha256:e03dae26084bb3632b4a77b1cd0419159d2226911aff6dc4c7e3058df68648c6"}, + {file = "fonttools-4.52.4-py3-none-any.whl", hash = "sha256:95e8a5975d08d0b624a14eec0f987e204ad81b480e24c5436af99170054434b8"}, + {file = "fonttools-4.52.4.tar.gz", hash = "sha256:859399b7adc8ac067be8e5c80ef4bb2faddff97e9b40896a9de75606a43d0469"}, +] + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "pycairo", "scipy"] +lxml = ["lxml (>=4.0)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.1.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "gunicorn" +version = "20.1.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, + {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, +] + +[package.dependencies] +setuptools = ">=3.0" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "0.16.3" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, + {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, +] + +[package.dependencies] +anyio = ">=3.0,<5.0" +certifi = "*" +h11 = ">=0.13,<0.15" +sniffio = "==1.*" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "httpx" +version = "0.23.3" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, + {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, +] + +[package.dependencies] +certifi = "*" +httpcore = ">=0.15.0,<0.17.0" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "kiwisolver" +version = "1.4.5" +description = "A fast implementation of the Cassowary constraint solver" +optional = false +python-versions = ">=3.7" +files = [ + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, + {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, +] + +[[package]] +name = "mako" +version = "1.3.5" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"}, + {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "matplotlib" +version = "3.9.0" +description = "Python plotting package" +optional = false +python-versions = ">=3.9" +files = [ + {file = "matplotlib-3.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2bcee1dffaf60fe7656183ac2190bd630842ff87b3153afb3e384d966b57fe56"}, + {file = "matplotlib-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f988bafb0fa39d1074ddd5bacd958c853e11def40800c5824556eb630f94d3b"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe428e191ea016bb278758c8ee82a8129c51d81d8c4bc0846c09e7e8e9057241"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaf3978060a106fab40c328778b148f590e27f6fa3cd15a19d6892575bce387d"}, + {file = "matplotlib-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e7f03e5cbbfacdd48c8ea394d365d91ee8f3cae7e6ec611409927b5ed997ee4"}, + {file = "matplotlib-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:13beb4840317d45ffd4183a778685e215939be7b08616f431c7795276e067463"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:063af8587fceeac13b0936c42a2b6c732c2ab1c98d38abc3337e430e1ff75e38"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a2fa6d899e17ddca6d6526cf6e7ba677738bf2a6a9590d702c277204a7c6152"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550cdda3adbd596078cca7d13ed50b77879104e2e46392dcd7c75259d8f00e85"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cce0f31b351e3551d1f3779420cf8f6ec0d4a8cf9c0237a3b549fd28eb4abb"}, + {file = "matplotlib-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c53aeb514ccbbcbab55a27f912d79ea30ab21ee0531ee2c09f13800efb272674"}, + {file = "matplotlib-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5be985db2596d761cdf0c2eaf52396f26e6a64ab46bd8cd810c48972349d1be"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c79f3a585f1368da6049318bdf1f85568d8d04b2e89fc24b7e02cc9b62017382"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bdd1ecbe268eb3e7653e04f451635f0fb0f77f07fd070242b44c076c9106da84"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e85a1a6d732f645f1403ce5e6727fd9418cd4574521d5803d3d94911038e5"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a490715b3b9984fa609116481b22178348c1a220a4499cda79132000a79b4db"}, + {file = "matplotlib-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8146ce83cbc5dc71c223a74a1996d446cd35cfb6a04b683e1446b7e6c73603b7"}, + {file = "matplotlib-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:d91a4ffc587bacf5c4ce4ecfe4bcd23a4b675e76315f2866e588686cc97fccdf"}, + {file = "matplotlib-3.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:616fabf4981a3b3c5a15cd95eba359c8489c4e20e03717aea42866d8d0465956"}, + {file = "matplotlib-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd53c79fd02f1c1808d2cfc87dd3cf4dbc63c5244a58ee7944497107469c8d8a"}, + {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06a478f0d67636554fa78558cfbcd7b9dba85b51f5c3b5a0c9be49010cf5f321"}, + {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81c40af649d19c85f8073e25e5806926986806fa6d54be506fbf02aef47d5a89"}, + {file = "matplotlib-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52146fc3bd7813cc784562cb93a15788be0b2875c4655e2cc6ea646bfa30344b"}, + {file = "matplotlib-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:0fc51eaa5262553868461c083d9adadb11a6017315f3a757fc45ec6ec5f02888"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bd4f2831168afac55b881db82a7730992aa41c4f007f1913465fb182d6fb20c0"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:290d304e59be2b33ef5c2d768d0237f5bd132986bdcc66f80bc9bcc300066a03"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff2e239c26be4f24bfa45860c20ffccd118d270c5b5d081fa4ea409b5469fcd"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:af4001b7cae70f7eaacfb063db605280058246de590fa7874f00f62259f2df7e"}, + {file = "matplotlib-3.9.0.tar.gz", hash = "sha256:e6d29ea6c19e34b30fb7d88b7081f869a03014f66fe06d62cc77d5a6ea88ed7a"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +kiwisolver = ">=1.3.1" +numpy = ">=1.23" +packaging = ">=20.0" +pillow = ">=8" +pyparsing = ">=2.3.1" +python-dateutil = ">=2.7" + +[package.extras] +dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "numpy" +version = "1.26.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + +[[package]] +name = "openai" +version = "1.30.5" +description = "The official Python library for the openai API" +optional = false +python-versions = ">=3.7.1" +files = [ + {file = "openai-1.30.5-py3-none-any.whl", hash = "sha256:2ad95e926de0d2e09cde632a9204b0a6dca4a03c2cdcc84329b01f355784355a"}, + {file = "openai-1.30.5.tar.gz", hash = "sha256:5366562eb2c5917e6116ae0391b7ae6e3acd62b0ae3f565ada32b35d8fcfa106"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +tqdm = ">4" +typing-extensions = ">=4.7,<5" + +[package.extras] +datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pandas" +version = "1.5.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pandas-1.5.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3749077d86e3a2f0ed51367f30bf5b82e131cc0f14260c4d3e499186fccc4406"}, + {file = "pandas-1.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:972d8a45395f2a2d26733eb8d0f629b2f90bebe8e8eddbb8829b180c09639572"}, + {file = "pandas-1.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50869a35cbb0f2e0cd5ec04b191e7b12ed688874bd05dd777c19b28cbea90996"}, + {file = "pandas-1.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ac844a0fe00bfaeb2c9b51ab1424e5c8744f89860b138434a363b1f620f354"}, + {file = "pandas-1.5.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0a56cef15fd1586726dace5616db75ebcfec9179a3a55e78f72c5639fa2a23"}, + {file = "pandas-1.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:478ff646ca42b20376e4ed3fa2e8d7341e8a63105586efe54fa2508ee087f328"}, + {file = "pandas-1.5.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6973549c01ca91ec96199e940495219c887ea815b2083722821f1d7abfa2b4dc"}, + {file = "pandas-1.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c39a8da13cede5adcd3be1182883aea1c925476f4e84b2807a46e2775306305d"}, + {file = "pandas-1.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f76d097d12c82a535fda9dfe5e8dd4127952b45fea9b0276cb30cca5ea313fbc"}, + {file = "pandas-1.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e474390e60ed609cec869b0da796ad94f420bb057d86784191eefc62b65819ae"}, + {file = "pandas-1.5.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f2b952406a1588ad4cad5b3f55f520e82e902388a6d5a4a91baa8d38d23c7f6"}, + {file = "pandas-1.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc4c368f42b551bf72fac35c5128963a171b40dce866fb066540eeaf46faa003"}, + {file = "pandas-1.5.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14e45300521902689a81f3f41386dc86f19b8ba8dd5ac5a3c7010ef8d2932813"}, + {file = "pandas-1.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9842b6f4b8479e41968eced654487258ed81df7d1c9b7b870ceea24ed9459b31"}, + {file = "pandas-1.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:26d9c71772c7afb9d5046e6e9cf42d83dd147b5cf5bcb9d97252077118543792"}, + {file = "pandas-1.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fbcb19d6fceb9e946b3e23258757c7b225ba450990d9ed63ccceeb8cae609f7"}, + {file = "pandas-1.5.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:565fa34a5434d38e9d250af3c12ff931abaf88050551d9fbcdfafca50d62babf"}, + {file = "pandas-1.5.3-cp38-cp38-win32.whl", hash = "sha256:87bd9c03da1ac870a6d2c8902a0e1fd4267ca00f13bc494c9e5a9020920e1d51"}, + {file = "pandas-1.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:41179ce559943d83a9b4bbacb736b04c928b095b5f25dd2b7389eda08f46f373"}, + {file = "pandas-1.5.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c74a62747864ed568f5a82a49a23a8d7fe171d0c69038b38cedf0976831296fa"}, + {file = "pandas-1.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c4c00e0b0597c8e4f59e8d461f797e5d70b4d025880516a8261b2817c47759ee"}, + {file = "pandas-1.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a50d9a4336a9621cab7b8eb3fb11adb82de58f9b91d84c2cd526576b881a0c5a"}, + {file = "pandas-1.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd05f7783b3274aa206a1af06f0ceed3f9b412cf665b7247eacd83be41cf7bf0"}, + {file = "pandas-1.5.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f69c4029613de47816b1bb30ff5ac778686688751a5e9c99ad8c7031f6508e5"}, + {file = "pandas-1.5.3-cp39-cp39-win32.whl", hash = "sha256:7cec0bee9f294e5de5bbfc14d0573f65526071029d036b753ee6507d2a21480a"}, + {file = "pandas-1.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:dfd681c5dc216037e0b0a2c821f5ed99ba9f03ebcf119c7dac0e9a7b960b9ec9"}, + {file = "pandas-1.5.3.tar.gz", hash = "sha256:74a3fd7e5a7ec052f183273dc7b0acd3a863edf7520f5d3a1765c04ffdb3b0b1"}, +] + +[package.dependencies] +numpy = {version = ">=1.23.2", markers = "python_version >= \"3.11\""} +python-dateutil = ">=2.8.1" +pytz = ">=2020.1" + +[package.extras] +test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"] + +[[package]] +name = "pandasai" +version = "2.1" +description = "Chat with your database (SQL, CSV, pandas, polars, mongodb, noSQL, etc). PandasAI makes data analysis conversational using LLMs (GPT 3.5 / 4, Anthropic, VertexAI) and RAG." +optional = false +python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,!=3.8.*,>=3.9" +files = [ + {file = "pandasai-2.1-py3-none-any.whl", hash = "sha256:086ddb1ea6cf9d919c67b524c21b20e2ea5452dc81797263c8644583de478ace"}, + {file = "pandasai-2.1.tar.gz", hash = "sha256:b087f725ede00d0299d0441015743d42185ed05cd1d12a92635118072359ec0a"}, +] + +[package.dependencies] +astor = ">=0.8.1,<0.9.0" +duckdb = "<1" +faker = ">=19.12.0,<20.0.0" +jinja2 = ">=3.1.3,<4.0.0" +matplotlib = ">=3.7.1,<4.0.0" +openai = "<2" +pandas = "1.5.3" +pillow = ">=10.1.0,<11.0.0" +pydantic = ">=1,<3" +python-dotenv = ">=1.0.0,<2.0.0" +requests = ">=2.31.0,<3.0.0" +scipy = ">=1.9.0,<2.0.0" +sqlalchemy = ">=1.4,<3" + +[package.extras] +bedrock = ["boto3 (>=1.34.59)"] +chromadb = ["chromadb (>=0.4.22,<0.5.0)"] +connectors = ["cx-Oracle (>=8.3.0,<9.0.0)", "psycopg2-binary (>=2.9.7,<3.0.0)", "pymysql (>=1.1.0,<2.0.0)", "snowflake-sqlalchemy (>=1.5.0,<2.0.0)", "sqlalchemy-bigquery (>=1.8.0,<2.0.0)", "sqlalchemy-cockroachdb (>=2.0.2,<3.0.0)", "sqlalchemy-databricks (>=0.2.0,<0.3.0)"] +excel = ["openpyxl (>=3.0.7,<4.0.0)"] +flask = ["flask (>=3.0.2,<4.0.0)"] +ggplot = ["ggplot (>=0.11.5,<0.12.0)"] +google-ai = ["google-cloud-aiplatform (>=1.26.1,<2.0.0)", "google-generativeai (>=0.3.2,<0.4.0)"] +google-sheets = ["beautifulsoup4 (>=4.12.2,<5.0.0)"] +ibm-watsonx-ai = ["ibm-watsonx-ai (>=0.2.3,<0.3.0)"] +langchain = ["langchain (>=0.1.0,<0.2.0)"] +modin = ["modin[ray] (==0.18.1)"] +numpy = ["numpy (>=1.17,<2.0)"] +pinecone = ["pinecone-client (>=4.1.0,<5.0.0)"] +plotly = ["kaleido (==0.2.0)", "plotly (>=5.15.0,<6.0.0)"] +polars = ["polars (>=0.18.15,<0.19.0)"] +qdrant = ["qdrant-client[fastembed] (>=1.8.0,<2.0.0)"] +scikit-learn = ["scikit-learn (>=1.2.2,<2.0.0)"] +seaborn = ["seaborn (>=0.12.2,<0.13.0)"] +statsmodels = ["statsmodels (>=0.14.0,<0.15.0)"] +streamlit = ["streamlit (>=1.23.1,<2.0.0)"] +text-generation = ["text-generation (>=0.6.0)"] +yfinance = ["yfinance (>=0.2.28,<0.3.0)"] + +[[package]] +name = "parse" +version = "1.20.1" +description = "parse() is the opposite of format()" +optional = false +python-versions = "*" +files = [ + {file = "parse-1.20.1-py2.py3-none-any.whl", hash = "sha256:76ddd5214255ae711db4c512be636151fbabaa948c6f30115aecc440422ca82c"}, + {file = "parse-1.20.1.tar.gz", hash = "sha256:09002ca350ad42e76629995f71f7b518670bcf93548bdde3684fd55d2be51975"}, +] + +[[package]] +name = "parse-type" +version = "0.6.2" +description = "Simplifies to build parse types based on the parse module" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*" +files = [ + {file = "parse_type-0.6.2-py2.py3-none-any.whl", hash = "sha256:06d39a8b70fde873eb2a131141a0e79bb34a432941fb3d66fad247abafc9766c"}, + {file = "parse_type-0.6.2.tar.gz", hash = "sha256:79b1f2497060d0928bc46016793f1fca1057c4aacdf15ef876aa48d75a73a355"}, +] + +[package.dependencies] +parse = {version = ">=1.18.0", markers = "python_version >= \"3.0\""} +six = ">=1.15" + +[package.extras] +develop = ["build (>=0.5.1)", "coverage (>=4.4)", "pylint", "pytest (<5.0)", "pytest (>=5.0)", "pytest-cov", "pytest-html (>=1.19.0)", "ruff", "tox (>=2.8,<4.0)", "twine (>=1.13.0)", "virtualenv (<20.22.0)", "virtualenv (>=20.0.0)"] +docs = ["Sphinx (>=1.6)", "sphinx-bootstrap-theme (>=0.6.0)"] +testing = ["pytest (<5.0)", "pytest (>=5.0)", "pytest-html (>=1.19.0)"] + +[[package]] +name = "passlib" +version = "1.7.4" +description = "comprehensive password hashing framework supporting over 30 schemes" +optional = false +python-versions = "*" +files = [ + {file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"}, + {file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"}, +] + +[package.extras] +argon2 = ["argon2-cffi (>=18.2.0)"] +bcrypt = ["bcrypt (>=3.1.0)"] +build-docs = ["cloud-sptheme (>=1.10.1)", "sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)"] +totp = ["cryptography"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pillow" +version = "10.3.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pyasn1" +version = "0.6.0" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, + {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, +] + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pydantic" +version = "1.10.15" +description = "Data validation and settings management using python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22ed12ee588b1df028a2aa5d66f07bf8f8b4c8579c2e96d5a9c1f96b77f3bb55"}, + {file = "pydantic-1.10.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75279d3cac98186b6ebc2597b06bcbc7244744f6b0b44a23e4ef01e5683cc0d2"}, + {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50f1666a9940d3d68683c9d96e39640f709d7a72ff8702987dab1761036206bb"}, + {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82790d4753ee5d00739d6cb5cf56bceb186d9d6ce134aca3ba7befb1eedbc2c8"}, + {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d207d5b87f6cbefbdb1198154292faee8017d7495a54ae58db06762004500d00"}, + {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e49db944fad339b2ccb80128ffd3f8af076f9f287197a480bf1e4ca053a866f0"}, + {file = "pydantic-1.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:d3b5c4cbd0c9cb61bbbb19ce335e1f8ab87a811f6d589ed52b0254cf585d709c"}, + {file = "pydantic-1.10.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3d5731a120752248844676bf92f25a12f6e45425e63ce22e0849297a093b5b0"}, + {file = "pydantic-1.10.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c365ad9c394f9eeffcb30a82f4246c0006417f03a7c0f8315d6211f25f7cb654"}, + {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3287e1614393119c67bd4404f46e33ae3be3ed4cd10360b48d0a4459f420c6a3"}, + {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be51dd2c8596b25fe43c0a4a59c2bee4f18d88efb8031188f9e7ddc6b469cf44"}, + {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6a51a1dd4aa7b3f1317f65493a182d3cff708385327c1c82c81e4a9d6d65b2e4"}, + {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4e316e54b5775d1eb59187f9290aeb38acf620e10f7fd2f776d97bb788199e53"}, + {file = "pydantic-1.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:0d142fa1b8f2f0ae11ddd5e3e317dcac060b951d605fda26ca9b234b92214986"}, + {file = "pydantic-1.10.15-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7ea210336b891f5ea334f8fc9f8f862b87acd5d4a0cbc9e3e208e7aa1775dabf"}, + {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3453685ccd7140715e05f2193d64030101eaad26076fad4e246c1cc97e1bb30d"}, + {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bea1f03b8d4e8e86702c918ccfd5d947ac268f0f0cc6ed71782e4b09353b26f"}, + {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:005655cabc29081de8243126e036f2065bd7ea5b9dff95fde6d2c642d39755de"}, + {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:af9850d98fc21e5bc24ea9e35dd80a29faf6462c608728a110c0a30b595e58b7"}, + {file = "pydantic-1.10.15-cp37-cp37m-win_amd64.whl", hash = "sha256:d31ee5b14a82c9afe2bd26aaa405293d4237d0591527d9129ce36e58f19f95c1"}, + {file = "pydantic-1.10.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5e09c19df304b8123938dc3c53d3d3be6ec74b9d7d0d80f4f4b5432ae16c2022"}, + {file = "pydantic-1.10.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7ac9237cd62947db00a0d16acf2f3e00d1ae9d3bd602b9c415f93e7a9fc10528"}, + {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:584f2d4c98ffec420e02305cf675857bae03c9d617fcfdc34946b1160213a948"}, + {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbc6989fad0c030bd70a0b6f626f98a862224bc2b1e36bfc531ea2facc0a340c"}, + {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d573082c6ef99336f2cb5b667b781d2f776d4af311574fb53d908517ba523c22"}, + {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6bd7030c9abc80134087d8b6e7aa957e43d35714daa116aced57269a445b8f7b"}, + {file = "pydantic-1.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:3350f527bb04138f8aff932dc828f154847fbdc7a1a44c240fbfff1b57f49a12"}, + {file = "pydantic-1.10.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:51d405b42f1b86703555797270e4970a9f9bd7953f3990142e69d1037f9d9e51"}, + {file = "pydantic-1.10.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a980a77c52723b0dc56640ced396b73a024d4b74f02bcb2d21dbbac1debbe9d0"}, + {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f1a1fb467d3f49e1708a3f632b11c69fccb4e748a325d5a491ddc7b5d22383"}, + {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:676ed48f2c5bbad835f1a8ed8a6d44c1cd5a21121116d2ac40bd1cd3619746ed"}, + {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92229f73400b80c13afcd050687f4d7e88de9234d74b27e6728aa689abcf58cc"}, + {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2746189100c646682eff0bce95efa7d2e203420d8e1c613dc0c6b4c1d9c1fde4"}, + {file = "pydantic-1.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:394f08750bd8eaad714718812e7fab615f873b3cdd0b9d84e76e51ef3b50b6b7"}, + {file = "pydantic-1.10.15-py3-none-any.whl", hash = "sha256:28e552a060ba2740d0d2aabe35162652c1459a0b9069fe0db7f4ee0e18e74d58"}, + {file = "pydantic-1.10.15.tar.gz", hash = "sha256:ca832e124eda231a60a041da4f013e3ff24949d94a01154b137fc2f2a43c3ffb"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pylint" +version = "3.2.2" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, + {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, +] + +[package.dependencies] +astroid = ">=3.2.2,<=3.3.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, +] +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pyparsing" +version = "3.1.2" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.20.3" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-asyncio-0.20.3.tar.gz", hash = "sha256:83cbf01169ce3e8eb71c6c278ccb0574d1a7a3bb8eaaf5e50e0ad342afb33b36"}, + {file = "pytest_asyncio-0.20.3-py3-none-any.whl", hash = "sha256:f129998b209d04fcc65c96fc85c11e5316738358909a8399e93be553d7656442"}, +] + +[package.dependencies] +pytest = ">=6.1.0" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +optional = false +python-versions = "*" +files = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "scipy" +version = "1.13.1" +description = "Fundamental algorithms for scientific computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, + {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"}, + {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"}, + {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"}, + {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"}, + {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"}, + {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"}, + {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"}, + {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"}, + {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"}, + {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"}, +] + +[package.dependencies] +numpy = ">=1.22.4,<2.3" + +[package.extras] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] +test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "setuptools" +version = "70.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.30" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-win32.whl", hash = "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-win32.whl", hash = "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-win_amd64.whl", hash = "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a8e3b0a7e09e94be7510d1661339d6b52daf202ed2f5b1f9f48ea34ee6f2d57"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b60203c63e8f984df92035610c5fb76d941254cf5d19751faab7d33b21e5ddc0"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1dc3eabd8c0232ee8387fbe03e0a62220a6f089e278b1f0aaf5e2d6210741ad"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:40ad017c672c00b9b663fcfcd5f0864a0a97828e2ee7ab0c140dc84058d194cf"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e42203d8d20dc704604862977b1470a122e4892791fe3ed165f041e4bf447a1b"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-win32.whl", hash = "sha256:2a4f4da89c74435f2bc61878cd08f3646b699e7d2eba97144030d1be44e27584"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-win_amd64.whl", hash = "sha256:b6bf767d14b77f6a18b6982cbbf29d71bede087edae495d11ab358280f304d8e"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-win32.whl", hash = "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-win_amd64.whl", hash = "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-win32.whl", hash = "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-win_amd64.whl", hash = "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221"}, + {file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"}, + {file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "starlette" +version = "0.25.0" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.7" +files = [ + {file = "starlette-0.25.0-py3-none-any.whl", hash = "sha256:774f1df1983fd594b9b6fb3ded39c2aa1979d10ac45caac0f4255cbe2acb8628"}, + {file = "starlette-0.25.0.tar.gz", hash = "sha256:854c71e73736c429c2bdb07801f2c76c9cba497e7c3cf4988fde5e95fe4cdb3c"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] + +[[package]] +name = "tomlkit" +version = "0.12.5" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, +] + +[[package]] +name = "tqdm" +version = "4.66.4" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, + {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "typing-extensions" +version = "4.12.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, + {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "uvicorn" +version = "0.20.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.7" +files = [ + {file = "uvicorn-0.20.0-py3-none-any.whl", hash = "sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd"}, + {file = "uvicorn-0.20.0.tar.gz", hash = "sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "e3731978c5263130151b2658f153a03a189ec58d91d800e979489563e55942d0" diff --git a/server/pyproject.toml b/server/pyproject.toml new file mode 100644 index 000000000..06edaae3f --- /dev/null +++ b/server/pyproject.toml @@ -0,0 +1,41 @@ +[tool.poetry] +name = "pandasai-server" +version = "0.1.0" +description = "PandasAI Server" +authors = ["Arslan "] + +[tool.poetry.dependencies] +python = "^3.11" +alembic = "^1.9.4" +SQLAlchemy = "^2.0.4" +uvicorn = "^0.20.0" +fastapi = "^0.92.0" +gunicorn = "^20.1.0" +fastapi-event = "^0.1.3" +pydantic = "^1.10.5" +isort = "^5.12.0" +black = "^23.1.0" +pytest = "^7.2.1" +httpx = "^0.23.3" +email-validator = "^1.3.1" +typing-extensions="^4.0.0" +asyncpg = "^0.29.0" +greenlet = "^3.0.3" +python-jose = "^3.3.0" +pylint = "^3.2.2" +pandasai = "^2.1" +pyyaml = "^6.0.1" +passlib = "^1.7.4" +bcrypt = "3.1.7" +python-dotenv = "^1.0.1" + +[tool.poetry.dev-dependencies] +behave = "^1.2.6" +pytest-asyncio = "^0.20.3" + +[tool.poetry.group.dev.dependencies] +black = {version = "^23.1.0", allow-prereleases = true} + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/server/startup.sh b/server/startup.sh new file mode 100644 index 000000000..9bc664eea --- /dev/null +++ b/server/startup.sh @@ -0,0 +1,25 @@ +#!/bin/bash + + +# Load environment variables from .env file if it exists +if [ -f .env ]; then + log "Loading environment variables from .env file" + export $(cat .env | sed 's/#.*//g' | xargs) +else + log ".env file not found, skipping" +fi + +source $(poetry env info --path)/bin/activate + + +poetry lock --no-update + +make install + +/bin/sh wait-for-it.sh + +# Run database migrations +make migrate + +# Start the server in the background +make start diff --git a/server/tests/app/repository/test_user_repository.py b/server/tests/app/repository/test_user_repository.py new file mode 100644 index 000000000..75b348089 --- /dev/null +++ b/server/tests/app/repository/test_user_repository.py @@ -0,0 +1,51 @@ +import unittest +from unittest.mock import ANY, AsyncMock, MagicMock +from app.models import User +from app.repositories.user import UserRepository + + +class TestUserRepository(unittest.IsolatedAsyncioTestCase): + async def test_get_by_email(self): + session_mock = AsyncMock() + repository = UserRepository(User, session_mock) + repository._query = MagicMock() + repository.filter = MagicMock() + + query_mock = MagicMock() + repository._query.return_value = query_mock + repository.filter.return_value = query_mock + + email = "test@example.com" + session_mock._query.return_value.filter.return_value = query_mock + query_mock.filter.return_value = query_mock + + await repository.get_by_email(email) + + repository._query.assert_called_once() + query_mock.filter.assert_called_once() + + async def test_create_and_init_dummy_user(self): + session_mock = AsyncMock() + repository = UserRepository(User, session_mock) + + user = await repository.create_and_init_dummy_user() + + session_mock.add.assert_called() + self.assertIsNotNone(user) + self.assertIsInstance(user, User) + self.assertTrue(user.verified) + self.assertEqual(user.first_name, "pandasai") + + async def test_join_memberships(self): + session_mock = AsyncMock() + repository = UserRepository(User, session_mock) + + query_mock = AsyncMock() + + await repository._join_memberships(query_mock) + + query_mock.options.assert_called_once_with(ANY) + + +if __name__ == "__main__": + unittest.main() diff --git a/server/tests/core/app/controller/test_base_controller.py b/server/tests/core/app/controller/test_base_controller.py new file mode 100644 index 000000000..ca48a3e98 --- /dev/null +++ b/server/tests/core/app/controller/test_base_controller.py @@ -0,0 +1,159 @@ +import pytest +from unittest.mock import AsyncMock, MagicMock, Mock, patch +from uuid import UUID + +from pydantic import BaseModel +from sqlalchemy import Column, Integer, String +from core.database import Base +from core.exceptions import NotFoundException +from core.repository import BaseRepository +from core.controller import BaseController + + +class MockModel(Base): + __tablename__ = "mock_table" + id = Column(Integer, primary_key=True, autoincrement=True) + name = Column(String) # Add more attributes as needed + + +class MockSchema(BaseModel): + name: str + age: int + + +class TestBaseController: + @pytest.fixture + def mock_repository(self): + return Mock(spec=BaseRepository) + + @pytest.fixture + def base_controller(self, mock_repository): + return BaseController(MockModel, mock_repository) + + @pytest.mark.asyncio + async def test_get_by_id_found(self, base_controller, mock_repository): + mock_instance = MockModel() + mock_repository.get_by = AsyncMock(return_value=mock_instance) + + result = await base_controller.get_by_id(1) + assert result == mock_instance + mock_repository.get_by.assert_called_once_with( + field="id", value=1, join_=None, unique=True + ) + + @pytest.mark.asyncio + async def test_get_by_id_not_found(self, base_controller, mock_repository): + mock_repository.get_by = AsyncMock(return_value=None) + + with pytest.raises(NotFoundException) as exc: + await base_controller.get_by_id(1) + + assert str(exc.value) == "Mock_Table with id: 1 does not exist" + + @pytest.mark.asyncio + async def test_get_by_uuid_found(self, base_controller, mock_repository): + mock_instance = MockModel() + mock_repository.get_by = AsyncMock(return_value=mock_instance) + + uuid = UUID("12345678123456781234567812345678") + result = await base_controller.get_by_uuid(uuid) + assert result == mock_instance + mock_repository.get_by.assert_called_once_with( + field="uuid", value=uuid, join_=None, unique=True + ) + + @pytest.mark.asyncio + async def test_get_by_uuid_not_found(self, base_controller, mock_repository): + mock_repository.get_by = AsyncMock(return_value=None) + + uuid = UUID("12345678123456781234567812345678") + with pytest.raises(NotFoundException) as exc: + await base_controller.get_by_uuid(uuid) + + assert ( + str(exc.value) + == "Mock_Table with id: 12345678-1234-5678-1234-567812345678 does not exist" + ) + + @pytest.mark.asyncio + async def test_get_all(self, base_controller, mock_repository): + mock_instance = MockModel() + mock_repository.get_all = AsyncMock(return_value=[mock_instance]) + + result = await base_controller.get_all(skip=0, limit=10) + assert result == [mock_instance] + mock_repository.get_all.assert_called_once_with(0, 10, None) + + @pytest.mark.asyncio + @patch( + "core.controller.base.Transactional", + lambda *args, **kwargs: lambda func: func, + ) + @patch("core.database.session.session_context", MagicMock()) + async def test_create(self, base_controller, mock_repository): + attributes = {"name": "test"} + mock_instance = MockModel() + mock_repository.create = AsyncMock(return_value=mock_instance) + + result = await base_controller.create(attributes) + assert result == mock_instance + mock_repository.create.assert_called_once_with(attributes) + + @pytest.mark.asyncio + @patch( + "core.controller.base.Transactional", + lambda *args, **kwargs: lambda func: func, + ) + @patch("core.database.session.session_context", MagicMock()) + async def test_delete_success(self, base_controller, mock_repository): + mock_model = MockModel() + mock_repository.delete.return_value = True + + result = await base_controller.delete(mock_model) + + mock_repository.delete.assert_called_once_with(mock_model) + assert result is True + + @pytest.mark.asyncio + @patch( + "core.controller.base.Transactional", + lambda *args, **kwargs: lambda func: func, + ) + @patch("core.database.session.session_context", MagicMock()) + async def test_delete_failure(self, base_controller, mock_repository): + mock_model = MockModel() + mock_repository.delete.return_value = False + + result = await base_controller.delete(mock_model) + + mock_repository.delete.assert_called_once_with(mock_model) + assert result is False + + def test_extract_attributes_from_schema(self): + schema = MockSchema(name="test", age=30) + result = BaseController.extract_attributes_from_schema(schema) + assert result == {"name": "test", "age": 30} + + def test_extract_attributes_from_schema_with_excludes(self): + schema = MockSchema(name="test", age=30) + result = BaseController.extract_attributes_from_schema( + schema, excludes={"name"} + ) + assert result == {"age": 30} + + def test_extract_attributes_from_schema_with_empty_excludes(self): + schema = MockSchema(name="test", age=30) + result = BaseController.extract_attributes_from_schema(schema, excludes=set()) + assert result == {"name": "test", "age": 30} + + def test_extract_attributes_from_schema_with_unset_values(self): + class MockSchemaWithOptional(BaseModel): + name: str + age: int = None + address: str = None + + schema = MockSchemaWithOptional(name="test") + result = BaseController.extract_attributes_from_schema( + schema, excludes={"address"} + ) + assert result == {"name": "test"} diff --git a/server/tests/core/database/test_transactional.py b/server/tests/core/database/test_transactional.py new file mode 100644 index 000000000..b1d224f05 --- /dev/null +++ b/server/tests/core/database/test_transactional.py @@ -0,0 +1,86 @@ +import pytest +from unittest.mock import AsyncMock, patch +from core.database.transactional import Transactional, Propagation + + +async def dummy_function(): + return "dummy" + + +async def exception_function(): + raise ValueError("Error") + + +class TestTransactionalDecorator: + @pytest.mark.asyncio + @patch("core.database.transactional.session", new_callable=AsyncMock) + async def test_transactional_required_success(self, mock_session): + mock_session_instance = AsyncMock() + mock_session_instance.begin = AsyncMock() + mock_session_instance.commit = AsyncMock() + mock_session_instance.rollback = AsyncMock() + mock_session.return_value = mock_session_instance + + decorator = Transactional(propagation=Propagation.REQUIRED) + decorated_function = decorator(dummy_function) + result = await decorated_function() + + mock_session.commit.assert_called_once() + mock_session.rollback.assert_not_called() + assert result == "dummy" + + @pytest.mark.asyncio + @patch("core.database.transactional.session", new_callable=AsyncMock) + async def test_transactional_required_failure(self, mock_session): + mock_session_instance = AsyncMock() + mock_session_instance.begin = AsyncMock() + mock_session_instance.commit = AsyncMock() + mock_session_instance.rollback = AsyncMock() + mock_session.return_value = mock_session_instance + + decorator = Transactional(propagation=Propagation.REQUIRED) + decorated_function = decorator(exception_function) + + with pytest.raises(ValueError): + await decorated_function() + + mock_session.begin.assert_not_called() + mock_session.commit.assert_not_called() + mock_session.rollback.assert_called_once() + + @pytest.mark.asyncio + @patch("core.database.transactional.session", new_callable=AsyncMock) + async def test_transactional_required_new_success(self, mock_session): + mock_session_instance = AsyncMock() + mock_session_instance.begin = AsyncMock() + mock_session_instance.commit = AsyncMock() + mock_session_instance.rollback = AsyncMock() + mock_session.return_value = mock_session_instance + + decorator = Transactional(propagation=Propagation.REQUIRED_NEW) + decorated_function = decorator(dummy_function) + result = await decorated_function() + + mock_session.begin.assert_called_once() + mock_session.commit.assert_called_once() + mock_session.rollback.assert_not_called() + assert result == "dummy" + + @pytest.mark.asyncio + @patch("core.database.transactional.session", new_callable=AsyncMock) + async def test_transactional_required_new_failure(self, mock_session): + mock_session_instance = AsyncMock() + mock_session_instance.begin = AsyncMock() + mock_session_instance.commit = AsyncMock() + mock_session_instance.rollback = AsyncMock() + mock_session.return_value = mock_session_instance + + decorator = Transactional(propagation=Propagation.REQUIRED_NEW) + decorated_function = decorator(exception_function) + + with pytest.raises(ValueError): + await decorated_function() + + mock_session.begin.assert_called_once() + mock_session.commit.assert_not_called() + mock_session.rollback.assert_called_once() diff --git a/server/tests/core/factory/test_factory.py b/server/tests/core/factory/test_factory.py new file mode 100644 index 000000000..2123cc731 --- /dev/null +++ b/server/tests/core/factory/test_factory.py @@ -0,0 +1,64 @@ +import pytest +from unittest.mock import MagicMock, patch +from app.controllers import ( + AuthController, + UserController, + ChatController, + WorkspaceController, + DatasetController, + ConversationController, +) +from app.repositories import ( + UserRepository, + WorkspaceRepository, + DatasetRepository, + ConversationRepository, +) +from core.factory.factory import Factory + + +class TestFactory: + @pytest.fixture + def factory(self): + with patch("core.database.get_session") as mock_get_session: + mock_get_session.return_value = MagicMock() + yield Factory() + + def test_get_user_controller(self, factory): + user_controller = factory.get_user_controller() + assert isinstance(user_controller, UserController) + assert isinstance(user_controller.user_repository, UserRepository) + assert isinstance(user_controller.space_repository, WorkspaceRepository) + + def test_get_space_controller(self, factory): + space_controller = factory.get_space_controller() + assert isinstance(space_controller, WorkspaceController) + assert isinstance(space_controller.space_repository, WorkspaceRepository) + assert isinstance(space_controller.dataset_repository, DatasetRepository) + + def test_get_auth_controller(self, factory): + auth_controller = factory.get_auth_controller() + assert isinstance(auth_controller, AuthController) + assert isinstance(auth_controller.user_repository, UserRepository) + + def test_get_chat_controller(self, factory): + chat_controller = factory.get_chat_controller() + assert isinstance(chat_controller, ChatController) + assert isinstance(chat_controller.user_repository, UserRepository) + assert isinstance(chat_controller.space_repository, WorkspaceRepository) + assert isinstance( + chat_controller.conversation_repository, ConversationRepository + ) + + def test_get_datasets_controller(self, factory): + datasets_controller = factory.get_datasets_controller() + assert isinstance(datasets_controller, DatasetController) + assert isinstance(datasets_controller.dataset_repository, DatasetRepository) + + def test_get_conversation_controller(self, factory): + conversation_controller = factory.get_conversation_controller() + assert isinstance(conversation_controller, ConversationController) + assert isinstance(conversation_controller.user_repository, UserRepository) + assert isinstance( + conversation_controller.conversation_repository, ConversationRepository + ) diff --git a/server/tests/core/respository/test_base_repository.py b/server/tests/core/respository/test_base_repository.py new file mode 100644 index 000000000..daf527d47 --- /dev/null +++ b/server/tests/core/respository/test_base_repository.py @@ -0,0 +1,154 @@ +import pytest +from unittest.mock import ANY, AsyncMock, MagicMock, Mock +from sqlalchemy import Column, Integer, String +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.future import select + +from core.database import Base +from core.repository.base import BaseRepository + + +# Define a mock model class that inherits from Base +class MockModel(Base): + __tablename__ = "mock_table_base" + id = Column(Integer, primary_key=True, autoincrement=True) + name = Column(String) # Add more attributes as needed + + +@pytest.fixture +def mock_session(): + return AsyncMock(spec=AsyncSession) + + +@pytest.fixture +def base_repository(mock_session): + return BaseRepository(model=MockModel, db_session=mock_session) + + +class TestBaseRepository: + @pytest.mark.asyncio + async def test_create_with_attributes(self, base_repository, mock_session): + attributes = {"id": 1, "name": "Test"} + + result = await base_repository.create(attributes) + + # Assert that the session's add method was called with the created model + mock_session.add.assert_called_once() + + # Verify that the model is correctly created with the given attributes + assert result.id == attributes["id"] + assert result.name == attributes["name"] + + @pytest.mark.asyncio + async def test_create_without_attributes(self, base_repository, mock_session): + result = await base_repository.create() + + # Assert that the session's add method was called with the created model + mock_session.add.assert_called_once() + + # Verify that the model is correctly created with default attributes + assert result.id is None + assert result.name is None + + @pytest.mark.asyncio + async def test_get_all(self, base_repository, mock_session): + mock_instance = MockModel(id=1, name="Test") + mock_query_result = [mock_instance] + + # Mock the return value of session.scalars().all() + mock_scalars = AsyncMock() + mock_scalars.all = AsyncMock(return_value=mock_query_result) + mock_session.scalars.return_value = mock_scalars + + # Mock the query method to return a Select object + base_repository._query = Mock(return_value=select(MockModel)) + + # Mock _all and _all_unique methods + base_repository._all = AsyncMock(return_value=mock_query_result) + base_repository._all_unique = AsyncMock(return_value=mock_query_result) + + # Test with join_ being None + result = await base_repository.get_all() + + assert result == mock_query_result + base_repository._query.assert_called_once_with(None) + base_repository._all.assert_called_once_with(ANY) + base_repository._all_unique.assert_not_called() + + # Reset mocks for the next test case + base_repository._query.reset_mock() + base_repository._all.reset_mock() + base_repository._all_unique.reset_mock() + + # Test with join_ not being None + join_ = {"some_relation"} + result = await base_repository.get_all(join_=join_) + + assert result == mock_query_result + base_repository._query.assert_called_once_with(join_) + base_repository._all.assert_not_called() + base_repository._all_unique.assert_called_once_with(ANY) + + @pytest.mark.asyncio + async def test_get_by_id(self, base_repository, mock_session): + mock_instance = MockModel(id=1, name="Test") + mock_query_result = MagicMock() + mock_query_result.scalars().first.return_value = mock_instance + + mock_session.execute.return_value = mock_query_result + + result = await base_repository.get_by_id(1) + + # Compare the query executed with the expected query + executed_query = mock_session.execute.call_args[0][0] + expected_query = select(MockModel).filter_by(id=1) + assert str(executed_query) == str(expected_query) + + assert result == mock_instance + mock_session.execute.assert_called_once() + + @pytest.mark.asyncio + async def test_delete(self, base_repository, mock_session): + mock_instance = MockModel(id=1, name="Test") + + await base_repository.delete(mock_instance) + + mock_session.delete.assert_called_once_with(mock_instance) + + def test_query_without_join_and_order(self, base_repository): + query = base_repository._query() + + expected_query = select(MockModel) + assert str(query) == str(expected_query) + + def test_query_with_join(self, base_repository): + # Mock the _maybe_join method to simulate a join + base_repository._maybe_join = Mock(return_value="joined_query") + query = base_repository._query(join_={"relation"}) + + base_repository._maybe_join.assert_called_once_with(ANY, {"relation"}) + assert query == "joined_query" + + def test_query_with_order(self, base_repository): + # Mock the _maybe_ordered method to simulate ordering + base_repository._maybe_ordered = Mock(return_value="ordered_query") + query = base_repository._query(order_={"asc": ["name"]}) + + base_repository._maybe_ordered.assert_called_once_with(ANY, {"asc": ["name"]}) + assert query == "ordered_query" + + def test_query_with_join_and_order(self, base_repository): + # Mock the _maybe_join and _maybe_ordered methods to simulate both join and ordering + base_repository._maybe_join = Mock(return_value="joined_query") + base_repository._maybe_ordered = Mock(return_value="ordered_joined_query") + query = base_repository._query(join_={"relation"}, order_={"asc": ["name"]}) + + base_repository._maybe_join.assert_called_once_with(ANY, {"relation"}) + base_repository._maybe_ordered.assert_called_once_with( + "joined_query", {"asc": ["name"]} + ) + assert query == "ordered_joined_query" + + +if __name__ == "__main__": + pytest.main() diff --git a/server/tests/core/utils/test_json_encoder.py b/server/tests/core/utils/test_json_encoder.py new file mode 100644 index 000000000..1de0f9537 --- /dev/null +++ b/server/tests/core/utils/test_json_encoder.py @@ -0,0 +1,55 @@ +import unittest +from unittest.mock import patch +import datetime +import json +import numpy as np +import pandas as pd +from core.utils.json_encoder import jsonable_encoder, CustomEncoder + + +class TestJsonableEncoder(unittest.TestCase): + def test_encode_np_integer(self): + data = {"value": np.int64(10)} + expected_result = '{"value": 10}' + self.assertEqual(jsonable_encoder(data), json.loads(expected_result)) + + def test_encode_np_float(self): + data = {"value": np.float64(3.14)} + expected_result = '{"value": 3.14}' + self.assertEqual(jsonable_encoder(data), json.loads(expected_result)) + + def test_encode_np_array(self): + data = {"array": np.array([1, 2, 3])} + expected_result = '{"array": [1, 2, 3]}' + self.assertEqual(jsonable_encoder(data), json.loads(expected_result)) + + def test_encode_datetime(self): + data = {"timestamp": datetime.datetime(2024, 6, 7, 12, 30)} + expected_result = '{"timestamp": "2024-06-07T12:30:00"}' + self.assertEqual(jsonable_encoder(data), json.loads(expected_result)) + + def test_encode_pd_timestamp(self): + data = {"timestamp": pd.Timestamp("2024-06-07")} + expected_result = '{"timestamp": "2024-06-07T00:00:00"}' + self.assertEqual(jsonable_encoder(data), json.loads(expected_result)) + + def test_other_objects(self): + data = {"name": "John", "age": 30} + expected_result = '{"name": "John", "age": 30}' + self.assertEqual(jsonable_encoder(data), json.loads(expected_result)) + + @patch("core.utils.json_encoder.json.dumps") + def test_custom_encoder_called(self, mock_dumps): + data = {"value": np.int64(10)} + + mock_dumps.return_value = '{"value": 10}' + + result = jsonable_encoder(data) + + mock_dumps.assert_called_once_with(data, cls=CustomEncoder) + + self.assertEqual(result, data) + + +if __name__ == "__main__": + unittest.main() diff --git a/server/wait-for-it.sh b/server/wait-for-it.sh new file mode 100644 index 000000000..c9ad9558f --- /dev/null +++ b/server/wait-for-it.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +host="$1" +shift +cmd="$@" + +until nc -z "$host" 5432; do + >&2 echo "PostgreSQL is unavailable - sleeping" + sleep 1 +done + +>&2 echo "PostgreSQL is up - executing command" +exec $cmd