diff --git a/Makefile b/Makefile index ae53886..9a73ad1 100644 --- a/Makefile +++ b/Makefile @@ -133,9 +133,6 @@ run-pgadmin: echo "$$SERVERS_JSON" > ./pgadmin/servers.json && \ docker volume create pgadmin_data && \ docker compose -f pgadmin.yml up --force-recreate - -load-server-pgadmin: - docker exec -it pgadmin python /pgadmin4/setup.py --load-servers servers.json clean-pgadmin: docker volume rm pgadmin_data diff --git a/README.md b/README.md index d627434..e042394 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Developing web applications can be a challenging process, especially when dealin - Development Best Practices: We apply code formatting, type checking, and static analysis tools to ensure that the code is readable, robust, and reliable. ## Table of Contents -1. [Set environment variables](#set-environment-variables) +1. [Prerequisites](#prerequisites) 2. [Run the project using Docker containers and forcing build containers](#run-the-project-using-docker-containers-and-forcing-build-containers) 3. [Run project using Docker containers](#run-project-using-docker-containers) 4. [Setup database with initial data](#setup-database-with-initial-data) @@ -36,10 +36,72 @@ Developing web applications can be a challenging process, especially when dealin 20. [TODO List](#todo-list) 21. [License](#license) +# Prerequisites + ## Set environment variables Create an **.env** file on root folder and copy the content from **.env.example**. Feel free to change it according to your own configuration. +## Docker engine +This project utilizes Docker and Docker Compose, so please ensure that you have installed the latest version compatible with your operating system. If you haven't already installed Docker, you can find detailed instructions on how to do so [here](https://docs.docker.com/engine/install/). Docker desktop can be good for a dev computer. + +You can check if it is installed with this command +``` +docker --version +``` + +## Make +"Make" is a build automation tool that is primarily used to manage the compilation and building of software projects. It reads a file called a "Makefile" which specifies a set of rules and dependencies for building a project, and then executes the necessary commands to build the project according to those rules. Depending of your OS you will requiere to install it in different ways. + +Mac +``` +xcode-select --install +``` + +Ubuntu +``` +sudo apt-get install build-essential +sudo apt-get -y install make +``` + +You can check if it is installed with this command +``` +make --version +``` + +## Python ">3.9,<3.12" +If you haven't already installed Python. You can download and install python from [here](https://www.python.org/downloads/). + +You can check yu python version: +``` +python --version +``` + +## Poetry + +Python Poetry is a tool for dependency management and packaging in Python. It provides a modern and efficient approach to managing Python projects' dependencies, virtual environments, and packaging. You can find detailed instructions on how install it [here](https://python-poetry.org/docs/#installing-with-the-official-installer). Poetry manages packages in **pyproject.toml** file; In this project you can find it in the folder backend/app. + +You can check if it is installed with this command +``` +poetry --version +``` + +### Dev tip to activate virtual environment +When you are opening python files do this cna help you to vscode detect installed packages. + +``` +cd backend/app/ +poetry shell +``` + +After that you can show the interpreted path. You can copy that path and set as the default for the project in vscode. Press on **Enter interpreter path ..** and past path. + +

+ +

+ + + ## Run the project using Docker containers and forcing build containers *Using docker compose command* @@ -92,12 +154,7 @@ You can connect to the Database using pgAdmin4 and use the credentials from .env make run-pgadmin ``` -*Load server configuration (It is required just the first time)* -```sh -make load-server-pgadmin -``` - -This starts pgamin in [http://localhost:15432](http://localhost:15432). +This starts pgamin in [http://localhost:15432](http://localhost:15432). When connecting to db server introduce the password by default it is **postgres** if you didn't change it in .env file.

@@ -298,13 +355,13 @@ make mypy ``` ## Basic chatbot example with Langchain and OpenAI -In addition to its core features, this project template demonstrates how to integrate an basic chatbot powered by Langchain and OpenAI through websockets. +In addition to its core features, this project template demonstrates how to integrate an basic chatbot powered by Langchain and OpenAI through websockets. You can use [PieSocket Websocket Tester](https://chromewebstore.google.com/detail/oilioclnckkoijghdniegedkbocfpnip) to test websockets. To begin experimenting with the basic chatbot, follow these steps: 1. **Obtain an OpenAI API Key**: You'll need to set the `OPENAI_API_KEY` environment variable, which you can obtain from [OpenAI's platform](https://platform.openai.com/). -2. **Test Websocket Connection**: You can test the websocket connection by using the following URL: [ws://fastapi.localhost/chat/\](ws://fastapi.localhost/chat/). Replace `` with a user identifier of your choice. +2. **Test Websocket Connection**: You can test the websocket connection by using the following URL: [ws://fastapi.localhost/chat/\](ws://fastapi.localhost/chat/). Replace `` with a user identifier of your choice. It should be the ID of your user. 3. **Sending and Receiving Messages**: You should be able to send messages to the chatbot using the provided websocket connection. To do this, use the following message structure: @@ -313,6 +370,9 @@ To begin experimenting with the basic chatbot, follow these steps: ``` Once you send a message, the chatbot will respond with generated responses based on the content of your input. +

+ +

## Inspiration and References diff --git a/backend/app/app/api/v1/endpoints/login.py b/backend/app/app/api/v1/endpoints/login.py index 07a3968..126bee5 100644 --- a/backend/app/app/api/v1/endpoints/login.py +++ b/backend/app/app/api/v1/endpoints/login.py @@ -158,7 +158,7 @@ async def get_new_access_token( status_code=status.HTTP_403_FORBIDDEN, detail="Error when decoding the token. Please check your request.", ) - except MissingRequiredClaimError as e: + except MissingRequiredClaimError: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="There is no required field in your token. Please contact the administrator.", diff --git a/backend/app/app/crud/base_crud.py b/backend/app/app/crud/base_crud.py index 993771a..8f13c80 100644 --- a/backend/app/app/crud/base_crud.py +++ b/backend/app/app/crud/base_crud.py @@ -85,7 +85,7 @@ async def get_multi_paginated( db_session = db_session or self.db.session if query is None: query = select(self.model) - + output = await paginate(db_session, query, params) return output diff --git a/backend/app/app/db/session.py b/backend/app/app/db/session.py index 2f70633..14cdc28 100644 --- a/backend/app/app/db/session.py +++ b/backend/app/app/db/session.py @@ -34,8 +34,8 @@ str(settings.ASYNC_CELERY_BEAT_DATABASE_URI), # echo=True, future=True, - #pool_size=POOL_SIZE, - #max_overflow=64, + # pool_size=POOL_SIZE, + # max_overflow=64, ) SessionLocalCelery = sessionmaker( diff --git a/backend/app/app/main.py b/backend/app/app/main.py index 3c3c0a5..c178806 100644 --- a/backend/app/app/main.py +++ b/backend/app/app/main.py @@ -17,7 +17,6 @@ from fastapi_cache.backends.redis import RedisBackend from fastapi_limiter import FastAPILimiter from fastapi_limiter.depends import WebSocketRateLimiter -from fastapi_pagination import add_pagination from jwt import DecodeError, ExpiredSignatureError, MissingRequiredClaimError from langchain.chat_models import ChatOpenAI from langchain.schema import HumanMessage @@ -189,7 +188,7 @@ async def websocket_endpoint(websocket: WebSocket, user_id: UUID): # Receive and send back the client message data = await websocket.receive_json() await ws_ratelimit(websocket) - user_message = IUserMessage.parse_obj(data) + user_message = IUserMessage.model_validate(data) user_message.user_id = user_id resp = IChatResponse( @@ -227,7 +226,7 @@ async def websocket_endpoint(websocket: WebSocket, user_id: UUID): message_id="", id="", sender="bot", - message="Sorry, something went wrong. Your user limit of api usages has been reached.", + message="Sorry, something went wrong. Your user limit of api usages has been reached or check your API key.", type="error", ) await websocket.send_json(resp.dict()) @@ -237,4 +236,4 @@ async def websocket_endpoint(websocket: WebSocket, user_id: UUID): # Add Routers -app.include_router(api_router_v1, prefix=settings.API_V1_STR) \ No newline at end of file +app.include_router(api_router_v1, prefix=settings.API_V1_STR) diff --git a/backend/app/app/models/base_uuid_model.py b/backend/app/app/models/base_uuid_model.py index 3fc4838..d7e89cf 100644 --- a/backend/app/app/models/base_uuid_model.py +++ b/backend/app/app/models/base_uuid_model.py @@ -4,6 +4,7 @@ from sqlalchemy.orm import declared_attr from datetime import datetime + # id: implements proposal uuid7 draft4 class SQLModel(_SQLModel): @declared_attr # type: ignore diff --git a/backend/app/app/models/team_model.py b/backend/app/app/models/team_model.py index a6fd909..cb0f727 100644 --- a/backend/app/app/models/team_model.py +++ b/backend/app/app/models/team_model.py @@ -4,7 +4,6 @@ from uuid import UUID - class TeamBase(SQLModel): name: str = Field(index=True) headquarters: str diff --git a/backend/app/app/models/user_follow_model.py b/backend/app/app/models/user_follow_model.py index e1f5225..2f9f3f4 100644 --- a/backend/app/app/models/user_follow_model.py +++ b/backend/app/app/models/user_follow_model.py @@ -10,4 +10,6 @@ class UserFollowBase(SQLModel): class UserFollow(BaseUUIDModel, UserFollowBase, table=True): - is_mutual: bool | None = Field(default=None, sa_column=Column(Boolean(), server_default="0")) + is_mutual: bool | None = Field( + default=None, sa_column=Column(Boolean(), server_default="0") + ) diff --git a/backend/app/app/schemas/common_schema.py b/backend/app/app/schemas/common_schema.py index 2267106..487ae3d 100644 --- a/backend/app/app/schemas/common_schema.py +++ b/backend/app/app/schemas/common_schema.py @@ -28,7 +28,7 @@ class TokenType(str, Enum): class IUserMessage(BaseModel): """User message schema.""" - user_id: UUID | None + user_id: UUID | None = None message: str diff --git a/backend/app/app/schemas/hero_schema.py b/backend/app/app/schemas/hero_schema.py index 7ddfe97..0878e6f 100644 --- a/backend/app/app/schemas/hero_schema.py +++ b/backend/app/app/schemas/hero_schema.py @@ -6,7 +6,7 @@ class IHeroCreate(HeroBase): - @field_validator('age') + @field_validator("age") def check_age(cls, value): if value < 0: raise ValueError("Invalid age") diff --git a/backend/app/app/schemas/image_media_schema.py b/backend/app/app/schemas/image_media_schema.py index d20c96e..61382d0 100644 --- a/backend/app/app/schemas/image_media_schema.py +++ b/backend/app/app/schemas/image_media_schema.py @@ -1,6 +1,6 @@ from app.models.image_media_model import ImageMedia, ImageMediaBase from app.models.media_model import Media -from pydantic import model_validator, root_validator +from pydantic import model_validator from .media_schema import IMediaRead from app.utils.partial import optional @@ -20,11 +20,11 @@ class IImageMediaRead(ImageMediaBase): media: IMediaRead | None -#Todo make it compatible with pydantic v2 +# Todo make it compatible with pydantic v2 class IImageMediaReadCombined(ImageMediaBase): link: str | None - @model_validator(mode='before') + @model_validator(mode="before") def combine_attributes(cls, values): link_fields = {"link": values.get("link", None)} if "media" in values: diff --git a/backend/app/app/schemas/team_schema.py b/backend/app/app/schemas/team_schema.py index eb78ba5..968abfd 100644 --- a/backend/app/app/schemas/team_schema.py +++ b/backend/app/app/schemas/team_schema.py @@ -1,4 +1,3 @@ -from typing import Any from app.models.hero_model import HeroBase from app.models.team_model import TeamBase from .user_schema import IUserBasicInfo diff --git a/backend/app/app/schemas/user_schema.py b/backend/app/app/schemas/user_schema.py index 7c1710d..47038b0 100644 --- a/backend/app/app/schemas/user_schema.py +++ b/backend/app/app/schemas/user_schema.py @@ -4,7 +4,7 @@ from pydantic import BaseModel from uuid import UUID from enum import Enum -from .image_media_schema import IImageMediaReadCombined, IImageMediaRead +from .image_media_schema import IImageMediaRead from .role_schema import IRoleRead diff --git a/backend/app/app/utils/partial.py b/backend/app/app/utils/partial.py index f0054d4..b703a0d 100644 --- a/backend/app/app/utils/partial.py +++ b/backend/app/app/utils/partial.py @@ -2,7 +2,6 @@ # https://github.com/pydantic/pydantic/pull/3179 # https://github.com/pydantic/pydantic/issues/1673 -from pydantic import BaseModel from copy import deepcopy from typing import Any, Callable, Optional, Type, TypeVar from pydantic import BaseModel, create_model diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index d6d9822..64bd660 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -18,23 +18,22 @@ services: - caddy_reverse_proxy:storage.localhost database: - image: bitnami/postgresql:13.3.0 + image: bitnami/postgresql restart: always container_name: database env_file: ".env" user: root volumes: - - ./db_docker:/bitnami/postgresql + - db_docker:/bitnami/postgresql - ./create-dbs.sql:/docker-entrypoint-initdb.d/create-dbs.sql ports: - 5454:5432 # Remove this on production expose: - 5432 environment: - - POSTGRES_USERNAME=${DATABASE_USER} - - POSTGRES_PASSWORD=${DATABASE_PASSWORD} - - POSTGRES_DATABASE=${DATABASE_NAME} - - POSTGRES_HOST_AUTH_METHOD= "trust" + - POSTGRESQL_USERNAME=${DATABASE_USER} + - POSTGRESQL_PASSWORD=${DATABASE_PASSWORD} + - POSTGRESQL_DATABASE=${DATABASE_NAME} redis_server: image: redis:alpine @@ -108,5 +107,6 @@ services: - caddy_config:/config volumes: + db_docker: caddy_data: caddy_config: \ No newline at end of file diff --git a/docker-compose-sonarqube.yml b/docker-compose-sonarqube.yml index 0b9a83b..14e7954 100644 --- a/docker-compose-sonarqube.yml +++ b/docker-compose-sonarqube.yml @@ -3,7 +3,7 @@ version: "3.9" services: sonarqube: container_name: "sonarqube" - image: "sonarqube:9.9.1-community" + image: "sonarqube:9.9.2-community" volumes: - ./sonarqube/extensions:/opt/sonarqube/extensions - ./sonarqube/logs:/opt/sonarqube/logs diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 1010382..688ddaa 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -22,23 +22,22 @@ services: - caddy_reverse_proxy:storage.localhost database: - image: bitnami/postgresql:13.3.0 + image: bitnami/postgresql restart: always container_name: database env_file: ".env" user: root volumes: - - ./db_docker:/bitnami/postgresql + - db_docker:/bitnami/postgresql - ./create-dbs.sql:/docker-entrypoint-initdb.d/create-dbs.sql ports: - 5454:5432 # Remove this on production expose: - 5432 environment: - - POSTGRES_USERNAME=${DATABASE_USER} - - POSTGRES_PASSWORD=${DATABASE_PASSWORD} - - POSTGRES_DATABASE=${DATABASE_NAME} - - POSTGRES_HOST_AUTH_METHOD= "trust" + - POSTGRESQL_USERNAME=${DATABASE_USER} + - POSTGRESQL_PASSWORD=${DATABASE_PASSWORD} + - POSTGRESQL_DATABASE=${DATABASE_NAME} redis_server: image: redis:alpine @@ -52,10 +51,7 @@ services: container_name: celery_worker restart: always # platform: linux/arm64/v8 - build: - context: ./backend - args: - INSTALL_DEV: "true" + build: ./backend command: "watchfiles 'celery -A app.core.celery worker -l info' " volumes: - ./backend/app:/code @@ -115,5 +111,6 @@ services: - caddy_config:/config volumes: + db_docker: caddy_data: caddy_config: \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index f3b5c34..ee37411 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,24 +16,24 @@ services: links: - caddy_reverse_proxy:storage.localhost - database: - image: bitnami/postgresql:13.3.0 - restart: always - container_name: database - env_file: ".env" - user: root - volumes: - - ./db_docker:/bitnami/postgresql - - ./create-dbs.sql:/docker-entrypoint-initdb.d/create-dbs.sql - ports: - - 5454:5432 # Remove this on production - expose: - - 5432 - environment: - - POSTGRES_USERNAME=${DATABASE_USER} - - POSTGRES_PASSWORD=${DATABASE_PASSWORD} - - POSTGRES_DATABASE=${DATABASE_NAME} - - POSTGRES_HOST_AUTH_METHOD= "trust" + ## You need to set your external database credentials in .env file or uncomment below lines to run database as container (No recommended because db needs persitence) + # database: + # image: bitnami/postgresql + # restart: always + # container_name: database + # env_file: ".env" + # user: root + # volumes: + # - db_docker:/bitnami/postgresql + # - ./create-dbs.sql:/docker-entrypoint-initdb.d/create-dbs.sql + # ports: + # - 5454:5432 # Remove this on production + # expose: + # - 5432 + # environment: + # - POSTGRESQL_USERNAME=${DATABASE_USER} + # - POSTGRESQL_PASSWORD=${DATABASE_PASSWORD} + # - POSTGRESQL_DATABASE=${DATABASE_NAME} redis_server: diff --git a/static/python_int.png b/static/python_int.png new file mode 100644 index 0000000..e51dcc9 Binary files /dev/null and b/static/python_int.png differ diff --git a/static/ws.png b/static/ws.png new file mode 100644 index 0000000..3ae715d Binary files /dev/null and b/static/ws.png differ