From f5d87f517f604f9d557f6eb786385de52e636188 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Wed, 20 Jan 2021 12:24:45 +0200 Subject: [PATCH 01/31] added search feature --- app/database/models.py | 30 +++++- app/dependencies.py | 9 ++ app/internal/search.py | 27 +++++ app/main.py | 26 +++-- app/routers/search.py | 42 ++++++++ app/templates/profile.html | 207 +++++++++++++++++++------------------ app/templates/search.html | 69 +++++++++++++ requirements.txt | 80 +++++++++----- tests/conftest.py | 18 ++++ tests/test_search.py | 76 ++++++++++++++ 10 files changed, 442 insertions(+), 142 deletions(-) create mode 100644 app/internal/search.py create mode 100644 app/routers/search.py create mode 100644 app/templates/search.html create mode 100644 tests/conftest.py create mode 100644 tests/test_search.py diff --git a/app/database/models.py b/app/database/models.py index 1b3bf771..b2990269 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -1,7 +1,11 @@ -from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String -from sqlalchemy.orm import relationship +import datetime -from .database import Base +from app.database.database import Base +from sqlalchemy import (DDL, Boolean, Column, DateTime, ForeignKey, Index, + Integer, String, event) +from sqlalchemy.dialects import postgresql +from sqlalchemy.dialects.postgresql import TSVECTOR +from sqlalchemy.orm import relationship class User(Base): @@ -23,7 +27,25 @@ class Event(Base): id = Column(Integer, primary_key=True, index=True) title = Column(String) content = Column(String) - date = Column(DateTime) + date = Column(DateTime, default=datetime.datetime.utcnow) owner_id = Column(Integer, ForeignKey("users.id")) + + # PostgreSQL + events_tsv = Column(TSVECTOR) owner = relationship("User", back_populates="events") + + # PostgreSQL + __table_args__ = (Index('events_tsv_idx', 'events_tsv', postgresql_using = 'gin'),) + + +# PostgreSQL +trigger_snippet = DDL(""" +CREATE TRIGGER ix_events_tsv_update BEFORE INSERT OR UPDATE +ON events +FOR EACH ROW EXECUTE PROCEDURE +tsvector_update_trigger(events_tsv,'pg_catalog.english', 'title', 'content') +""") + +# PostgreSQL +event.listen(Event.__table__, 'after_create', trigger_snippet.execute_if(dialect = 'postgresql')) diff --git a/app/dependencies.py b/app/dependencies.py index e69de29b..0922a113 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -0,0 +1,9 @@ +from app.database.database import SessionLocal + +# Dependency +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/app/internal/search.py b/app/internal/search.py new file mode 100644 index 00000000..92871e98 --- /dev/null +++ b/app/internal/search.py @@ -0,0 +1,27 @@ +from app.database.database import SessionLocal +from app.database.models import Event +from sqlalchemy.exc import SQLAlchemyError + + +def get_results_by_keywords(session: SessionLocal, keywords: str, owner_id: int): + """Returns possible results for a search in the 'events' database table. + + Args: + keywords (str): search string + owner_id (int): current user id + + Returns: + list: a list of data from the database. + + Uses PostgreSQL's built in 'Full-text search' feature (doesn't work with SQLite)""" + + keywords = " ".join(keywords.split()) + keywords = keywords.replace(" ", ":* & ") + ":*" + + try: + return session.query(Event).filter( + Event.owner_id == owner_id, + Event.events_tsv.match(keywords)).all() + + except SQLAlchemyError: + return [] \ No newline at end of file diff --git a/app/main.py b/app/main.py index 3050a284..643e3a6b 100644 --- a/app/main.py +++ b/app/main.py @@ -1,13 +1,25 @@ -from fastapi import FastAPI, Request +import os + +from fastapi import FastAPI, Form, Request from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates +from sqlalchemy.orm import Session +from app.config import session +from app.database.database import Base, SessionLocal, engine +from app.database.models import Event, User +from app.dependencies import get_db +from app.internal.search import get_results_by_keywords +from app.routers import search -app = FastAPI() - -app.mount("/static", StaticFiles(directory="static"), name="static") -templates = Jinja2Templates(directory="templates") +app = FastAPI() +app_path = os.path.dirname(os.path.realpath(__file__)) +static_path = os.path.join(app_path, "static") +templates_path = os.path.join(app_path, "templates") +app.include_router(search.router) +app.mount("/static", StaticFiles(directory=static_path), name="static") +templates = Jinja2Templates(directory=templates_path) @app.get("/") @@ -22,11 +34,11 @@ def home(request: Request): def profile(request: Request): # Get relevant data from database - upcouming_events = range(5) + upcoming_events = range(5) current_username = "Chuck Norris" return templates.TemplateResponse("profile.html", { "request": request, "username": current_username, - "events": upcouming_events + "events": upcoming_events }) diff --git a/app/routers/search.py b/app/routers/search.py new file mode 100644 index 00000000..4a98423a --- /dev/null +++ b/app/routers/search.py @@ -0,0 +1,42 @@ +from app.dependencies import get_db +from app.internal.search import get_results_by_keywords +from fastapi import APIRouter, Depends, Form, Request +from fastapi.templating import Jinja2Templates +from sqlalchemy.orm import Session + + +router = APIRouter() +templates = Jinja2Templates(directory="app/templates") + + +@router.get("/search") +def search(request: Request): + current_username = "Chuck Norris" + return templates.TemplateResponse("search.html", { + "request": request, + "username": current_username + }) + + +@router.post("/search") +async def show_results(request: Request, keywords: str = Form(None), db: Session = Depends(get_db)): + current_username = "Chuck Norris" + current_user = 1 + + message = "" + + if not keywords: + message = "Invalid request." + results = None + else: + results = get_results_by_keywords(db, keywords, owner_id=current_user) + if len(results) == 0: + message = f"No matching results for '{keywords}'." + + return templates.TemplateResponse("search.html", { + "request": request, + "username": current_username, + "message": message, + "results": results, + "keywords": keywords + }) diff --git a/app/templates/profile.html b/app/templates/profile.html index 31761af2..1d88357e 100644 --- a/app/templates/profile.html +++ b/app/templates/profile.html @@ -3,123 +3,124 @@ {% block content %} -
-
- -
- -
- Profile image -
-
{{ username }}
-

- - - Settings - -

-
-

- - Short description - -

-
+
+
+ +
+ +
+ Profile image +
+
{{ username }}
+

+ + + Settings + +

+
+

+ + Short description + +

- +
+ + + +
+
+

+ Features +

+ +
+
+ - -
-
-

- Features +

+ + +
+ + +
+ + {% for event in events %} + + +
+
+ + Upcoming Event On December 01, 2021 + + + + + + + +
+
+

+ The Event {{ event }} - description ...

- +
+ Last updated 3 mins ago
- - + + + {% endfor %}
- - -
- - -
- - {% for event in events %} - - -
-
- - Upcoming Event On December 01, 2021 - - - - - - - -
-
-

- The Event {{ event }} - description ... -

-
- Last updated 3 mins ago -
-
- - {% endfor %} +
+ + +
+ + +
+
+

+ Explore MeetUps near you +

-
- - -
- - -
-
-

- Explore MeetUps near you -

-
-
- -
-
-

- Your Card -

-
+ +
+
+

+ Your Card +

+
- -
-
-

- Your Card -

-
+ +
+
+

+ Your Card +

-
- -{% endblock %} +
+ + +{% endblock %} \ No newline at end of file diff --git a/app/templates/search.html b/app/templates/search.html new file mode 100644 index 00000000..31fdfc7a --- /dev/null +++ b/app/templates/search.html @@ -0,0 +1,69 @@ +{% extends "base.html" %} + + +{% block content %} + +
+

Hello, {{ username }}

+
+
+
+ + +
+
+ +
+ +
+ +{% if message %} +
+ {{ message }} +
+{% endif %} + + +{% if results %} +
+
+ Showing results for '{{ keywords }}': +
+ +
+
+ {% for result in results %} + +
+
+ + {{ loop.index }}. {{ result.title }} + +
+
+

+ {{ result.content }} +

+
+ {{ result.date }} +
+
+ + {% endfor %} +
+
+ {% endif %} + + + + + {% endblock %} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index ffed4ae5..8d0c8e09 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,28 +1,52 @@ -aiofiles==0.6.0 -atomicwrites==1.4.0 -attrs==20.3.0 -click==7.1.2 -colorama==0.4.4 -fastapi==0.63.0 -h11==0.12.0 -h2==4.0.0 -hpack==4.0.0 -hyperframe==6.0.0 -importlib-metadata==3.3.0 -iniconfig==1.1.1 -Jinja2==2.11.2 -MarkupSafe==1.1.1 -packaging==20.8 -pluggy==0.13.1 -priority==1.3.0 -py==1.10.0 -pydantic==1.7.3 -pyparsing==2.4.7 -pytest==6.2.1 -SQLAlchemy==1.3.22 -starlette==0.13.6 -toml==0.10.2 -typing-extensions==3.7.4.3 -uvicorn==0.13.3 -wsproto==1.0.0 -zipp==3.4.0 +aiofiles==0.6.0 +astroid==2.4.2 +atomicwrites==1.4.0 +attrs==20.3.0 +certifi==2020.12.5 +chardet==4.0.0 +click==7.1.2 +colorama==0.4.4 +decorator==4.4.2 +fastapi==0.63.0 +h11==0.12.0 +h2==4.0.0 +hpack==4.0.0 +hyperframe==6.0.0 +idna==2.10 +importlib-metadata==3.3.0 +iniconfig==1.1.1 +isort==5.7.0 +Jinja2==2.11.2 +lazy-object-proxy==1.4.3 +MarkupSafe==1.1.1 +mccabe==0.6.1 +mypy==0.790 +mypy-extensions==0.4.3 +packaging==20.8 +pluggy==0.13.1 +priority==1.3.0 +psycopg2==2.8.6 +py==1.10.0 +pydantic==1.7.3 +pylint==2.6.0 +pyparsing==2.4.7 +pytest==6.2.1 +python-dotenv==0.15.0 +python-multipart==0.0.5 +PyYAML==5.3.1 +requests==2.25.1 +six==1.15.0 +SQLAlchemy==1.3.22 +SQLAlchemy-Utils==0.36.8 +starlette==0.13.6 +toml==0.10.2 +typed-ast==1.4.2 +typing-extensions==3.7.4.3 +urllib3==1.26.2 +uvicorn==0.13.3 +validators==0.18.2 +watchgod==0.6 +websockets==8.1 +wrapt==1.12.1 +wsproto==1.0.0 +zipp==3.4.0 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..c78c10b9 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,18 @@ +import pytest +from app.database.database import Base, SessionLocal, engine +from app.main import app +from fastapi.testclient import TestClient + + +@pytest.fixture +def client(): + return TestClient(app) + + +@pytest.fixture +def session(): + Base.metadata.create_all(bind=engine) + session = SessionLocal() + yield session + session.close() + Base.metadata.drop_all(bind=engine) \ No newline at end of file diff --git a/tests/test_search.py b/tests/test_search.py new file mode 100644 index 00000000..361b4492 --- /dev/null +++ b/tests/test_search.py @@ -0,0 +1,76 @@ +from app.database.models import User, Event + +from fastapi import status +import pytest + + +class TestSearch: + SEARCH = '/search' + GOOD_KEYWORDS = [ + ({'keywords': 'lov'}, b'test'), + ({'keywords': 'very emotional'}, b'second event'), + ({'keywords': 'event'}, b'My second event'), + ({'keywords': 'event'}, b'My first event'), + ({'keywords': 'jam'}, b'is fun'), + ({'keywords': ' jam '}, b'is fun') + ] + BAD_KEYWORDS = [ + ({'keywords': ''}, b'Invalid'), + ({'keywords': 'ev!@&'}, b'No matching'), + ({'keywords': '[]'}, b'No matching'), + ({'keywords': 'firsttttt'}, b'No matching') + ] + + @staticmethod + def create_user(session): + user = User(username='testuser', email='test@abc.com', password='1234') + session.add(user) + session.commit() + return user + + @staticmethod + def add_event(session, title, content, owner_id): + event = Event(title=title, content=content, owner_id=owner_id) + session.add(event) + session.commit() + + @staticmethod + def create_data(session): + user = TestSearch.create_user(session) + events = [ + {'title': "My first event", 'content': 'I am so excited', 'owner_id': 1}, + {'title': "My second event", 'content': 'I am very emotional', 'owner_id': 1}, + {'title': "Pick up my nephews", 'content': 'Very important', 'owner_id': 1}, + {'title': "Solve this ticket", 'content': 'I can do this', 'owner_id': 1}, + {'title': "Jam with my friends", 'content': "Jamming is fun", 'owner_id': 1}, + {'title': 'test', 'content': 'love string', 'owner_id': 1} + ] + + for event in events: + TestSearch.add_event(session, + title=event['title'], + content=event['content'], + owner_id=event['owner_id'] + ) + + @staticmethod + def test_search_page_exists(client): + resp = client.get(TestSearch.SEARCH) + assert resp.status_code == status.HTTP_200_OK + assert b'Search event by keyword' in resp.content + + +@pytest.mark.parametrize('data, string', TestSearch.GOOD_KEYWORDS) +def test_search_good_keywords(data, string, client, session): + ts = TestSearch() + ts.create_data(session) + resp = client.post(ts.SEARCH, data=data) + assert string in resp.content + + +@pytest.mark.parametrize('data, string', TestSearch.BAD_KEYWORDS) +def test_search_bad_keywords(data, string, client, session): + ts = TestSearch() + ts.create_data(session) + resp = client.post(ts.SEARCH, data=data) + assert string in resp.content \ No newline at end of file From 32c3cb698b143cbd725da00d795f781bbb804726 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Wed, 20 Jan 2021 12:45:51 +0200 Subject: [PATCH 02/31] starting merge --- app/database/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/database/models.py b/app/database/models.py index b2990269..a53ac726 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -27,9 +27,10 @@ class Event(Base): id = Column(Integer, primary_key=True, index=True) title = Column(String) content = Column(String) - date = Column(DateTime, default=datetime.datetime.utcnow) + start = Column(DateTime, nullable=False) + end = Column(DateTime, nullable=False) owner_id = Column(Integer, ForeignKey("users.id")) - + # PostgreSQL events_tsv = Column(TSVECTOR) From 9919f2e0b1ededfe19e49ed02e79649289a40ec7 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Wed, 20 Jan 2021 12:46:26 +0200 Subject: [PATCH 03/31] help --- app/database/database.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/database/database.py b/app/database/database.py index 43626378..025322e9 100644 --- a/app/database/database.py +++ b/app/database/database.py @@ -5,11 +5,12 @@ from sqlalchemy.orm import sessionmaker -SQLALCHEMY_DATABASE_URL = os.getenv("DATABASE_CONNECTION_STRING") +SQLALCHEMY_DATABASE_URL = "postgresql://postgres:h1h2h3h4@localhost/postgres" engine = create_engine( - SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} + SQLALCHEMY_DATABASE_URL ) + SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) -Base = declarative_base() +Base = declarative_base() \ No newline at end of file From 7349c9ec8bba1835803c53d4ccd5c240d5e7b06f Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Wed, 20 Jan 2021 12:53:20 +0200 Subject: [PATCH 04/31] merging --- app/database/models.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/app/database/models.py b/app/database/models.py index 0c92ae94..a15190e5 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -1,7 +1,11 @@ -from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String -from sqlalchemy.orm import relationship +import datetime -from .database import Base +from app.database.database import Base +from sqlalchemy import (DDL, Boolean, Column, DateTime, ForeignKey, Index, + Integer, String, event) +from sqlalchemy.dialects import postgresql +from sqlalchemy.dialects.postgresql import TSVECTOR +from sqlalchemy.orm import relationship class User(Base): @@ -30,5 +34,23 @@ class Event(Base): start = Column(DateTime, nullable=False) end = Column(DateTime, nullable=False) owner_id = Column(Integer, ForeignKey("users.id")) + + # PostgreSQL + events_tsv = Column(TSVECTOR) owner = relationship("User", back_populates="events") + + # PostgreSQL + __table_args__ = (Index('events_tsv_idx', 'events_tsv', postgresql_using = 'gin'),) + + +# PostgreSQL +trigger_snippet = DDL(""" +CREATE TRIGGER ix_events_tsv_update BEFORE INSERT OR UPDATE +ON events +FOR EACH ROW EXECUTE PROCEDURE +tsvector_update_trigger(events_tsv,'pg_catalog.english', 'title', 'content') +""") + +# PostgreSQL +event.listen(Event.__table__, 'after_create', trigger_snippet.execute_if(dialect = 'postgresql')) From 5a572c24f324f32e47babae30fac19088e12862d Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Wed, 20 Jan 2021 14:13:09 +0200 Subject: [PATCH 05/31] fixes --- app/database/database.py | 2 +- app/database/models.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/database/database.py b/app/database/database.py index 85354084..0cbbe19c 100644 --- a/app/database/database.py +++ b/app/database/database.py @@ -6,7 +6,7 @@ from app import config -SQLALCHEMY_DATABASE_URL = "postgresql://postgres:h1h2h3h4@localhost/postgres" +SQLALCHEMY_DATABASE_URL = "postgresql://postgres:1234@localhost/postgres" engine = create_engine( SQLALCHEMY_DATABASE_URL diff --git a/app/database/models.py b/app/database/models.py index 37edc858..4cd6b8d2 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -44,7 +44,7 @@ class Event(Base): owner = relationship("User", back_populates="events") # PostgreSQL - __table_args__ = (Index('events_tsv_idx', 'events_tsv', postgresql_using = 'gin'),) + __table_args__ = (Index('events_tsv_idx', 'events_tsv', postgresql_using='gin'),) # PostgreSQL @@ -56,4 +56,4 @@ class Event(Base): """) # PostgreSQL -event.listen(Event.__table__, 'after_create', trigger_snippet.execute_if(dialect = 'postgresql')) +event.listen(Event.__table__, 'after_create', trigger_snippet.execute_if(dialect='postgresql')) From 491f62bd7543f823601f639f895648a4fce0a19e Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Thu, 21 Jan 2021 12:18:30 +0200 Subject: [PATCH 06/31] changes for yam --- app/config.py.example | 16 ++++++++++++++++ app/database/database.py | 17 ++++++++++++----- app/database/models.py | 32 ++++++++++++++++---------------- app/internal/search.py | 16 ++++++++++++---- app/main.py | 15 +++++++++++---- tests/test_search.py | 20 +++++++++++++++++--- 6 files changed, 84 insertions(+), 32 deletions(-) diff --git a/app/config.py.example b/app/config.py.example index b4c8ccf2..63fd4db9 100644 --- a/app/config.py.example +++ b/app/config.py.example @@ -1,10 +1,26 @@ +import os + +from fastapi_mail import ConnectionConfig # flake8: noqa # DATABASE DEVELOPMENT_DATABASE_STRING = "sqlite:///./dev.db" +# Set the following True if working on PSQL environment or set False otherwise +PSQL_ENVIRONMENT = False # MEDIA MEDIA_DIRECTORY = 'media' PICTURE_EXTENSION = '.png' AVATAR_SIZE = (120, 120) + +email_conf = ConnectionConfig( + MAIL_USERNAME=os.getenv("MAIL_USERNAME") or "user", + MAIL_PASSWORD=os.getenv("MAIL_PASSWORD") or "password", + MAIL_FROM=os.getenv("MAIL_FROM") or "a@a.com", + MAIL_PORT=587, + MAIL_SERVER="smtp.gmail.com", + MAIL_TLS=True, + MAIL_SSL=False, + USE_CREDENTIALS=True, +) \ No newline at end of file diff --git a/app/database/database.py b/app/database/database.py index 0cbbe19c..e295d733 100644 --- a/app/database/database.py +++ b/app/database/database.py @@ -6,11 +6,18 @@ from app import config -SQLALCHEMY_DATABASE_URL = "postgresql://postgres:1234@localhost/postgres" - -engine = create_engine( - SQLALCHEMY_DATABASE_URL -) +SQLALCHEMY_DATABASE_URL = os.getenv( + "DATABASE_CONNECTION_STRING", config.DEVELOPMENT_DATABASE_STRING) + +if not config.PSQL_ENVIRONMENT: + engine = create_engine( + SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} + ) + +else: + engine = create_engine( + SQLALCHEMY_DATABASE_URL + ) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) diff --git a/app/database/models.py b/app/database/models.py index 4cd6b8d2..b5c65147 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -1,5 +1,6 @@ import datetime +from app.config import PSQL_ENVIRONMENT from app.database.database import Base from sqlalchemy import (DDL, Boolean, Column, DateTime, ForeignKey, Index, Integer, String, event) @@ -34,26 +35,25 @@ class Event(Base): start = Column(DateTime, nullable=False) end = Column(DateTime, nullable=False) owner_id = Column(Integer, ForeignKey("users.id")) - - # PostgreSQL - events_tsv = Column(TSVECTOR) + owner = relationship("User", back_populates="events") # PostgreSQL - events_tsv = Column(TSVECTOR) - - owner = relationship("User", back_populates="events") + if PSQL_ENVIRONMENT: + events_tsv = Column(TSVECTOR) + __table_args__ = (Index('events_tsv_idx', 'events_tsv', postgresql_using='gin'),) - # PostgreSQL - __table_args__ = (Index('events_tsv_idx', 'events_tsv', postgresql_using='gin'),) +class PSQLEnvironmentError(Exception): + pass -# PostgreSQL -trigger_snippet = DDL(""" -CREATE TRIGGER ix_events_tsv_update BEFORE INSERT OR UPDATE -ON events -FOR EACH ROW EXECUTE PROCEDURE -tsvector_update_trigger(events_tsv,'pg_catalog.english', 'title', 'content') -""") # PostgreSQL -event.listen(Event.__table__, 'after_create', trigger_snippet.execute_if(dialect='postgresql')) +if PSQL_ENVIRONMENT: + trigger_snippet = DDL(""" + CREATE TRIGGER ix_events_tsv_update BEFORE INSERT OR UPDATE + ON events + FOR EACH ROW EXECUTE PROCEDURE + tsvector_update_trigger(events_tsv,'pg_catalog.english', 'title', 'content') + """) + + event.listen(Event.__table__, 'after_create', trigger_snippet.execute_if(dialect='postgresql')) diff --git a/app/internal/search.py b/app/internal/search.py index 69c2b1a6..b7c7d7e9 100644 --- a/app/internal/search.py +++ b/app/internal/search.py @@ -1,9 +1,18 @@ from app.database.database import SessionLocal from app.database.models import Event from sqlalchemy.exc import SQLAlchemyError +from typing import List -def get_results_by_keywords(session: SessionLocal, keywords: str, owner_id: int): +def get_stripped_keywords(keywords: str) -> str: + '''Gets a string of keywords to search for from the user form and returns a stripped ready-to-db-search keywords string''' + + keywords = " ".join(keywords.split()) + keywords = keywords.replace(" ", ":* & ") + ":*" + return keywords + + +def get_results_by_keywords(session: SessionLocal, keywords: str, owner_id: int) -> List[Event]: """Returns possible results for a search in the 'events' database table. Args: @@ -11,12 +20,11 @@ def get_results_by_keywords(session: SessionLocal, keywords: str, owner_id: int) owner_id (int): current user id Returns: - list: a list of data from the database. + list: a list of events from the database matching the inserted keywords. Uses PostgreSQL's built in 'Full-text search' feature (doesn't work with SQLite)""" - keywords = " ".join(keywords.split()) - keywords = keywords.replace(" ", ":* & ") + ":*" + keywords = get_stripped_keywords(keywords) try: return session.query(Event).filter( diff --git a/app/main.py b/app/main.py index 228b34f0..a119ba71 100644 --- a/app/main.py +++ b/app/main.py @@ -2,15 +2,22 @@ from fastapi import FastAPI, Form, Request from fastapi.staticfiles import StaticFiles - +from sqlalchemy.exc import SQLAlchemyError +from app.config import PSQL_ENVIRONMENT from app.database import models from app.database.database import engine -from app.dependencies import ( - MEDIA_PATH, STATIC_PATH, templates) +from app.dependencies import MEDIA_PATH, STATIC_PATH, templates from app.routers import agenda, event, profile, search -models.Base.metadata.create_all(bind=engine) +if 'sqlite' in str(engine.url) and PSQL_ENVIRONMENT: + raise models.PSQLEnvironmentError( + "You're trying to use PSQL features on SQLite env.\n" + "Please set app.config.PSQL_ENVIRONMENT to False and run the app again." + ) +else: + models.Base.metadata.create_all(bind=engine) + app = FastAPI() app.mount("/static", StaticFiles(directory=STATIC_PATH), name="static") diff --git a/tests/test_search.py b/tests/test_search.py index 4c208011..14370903 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -1,8 +1,9 @@ -from app.database.models import User, Event from datetime import datetime -from fastapi import status import pytest +from app.database.models import Event, User +from app.internal.search import get_stripped_keywords +from fastapi import status class TestSearch: @@ -83,4 +84,17 @@ def test_search_bad_keywords(data, string, client, session): ts = TestSearch() ts.create_data(session) resp = client.post(ts.SEARCH, data=data) - assert string in resp.content \ No newline at end of file + assert string in resp.content + + +STRIPPED_KEYWORDS = [ + (" love string ", "love:* & string:*"), + ("test ", "test:*"), + ("i am awesome", "i:* & am:* & awesome:*"), + ("a lot of spaces", "a:* & lot:* & of:* & spaces:*") +] + + +@pytest.mark.parametrize('input_string, output_string', STRIPPED_KEYWORDS) +def test_search_stripped_keywords(input_string, output_string): + assert get_stripped_keywords(input_string) == output_string From e8c448ca42a0745d81bc1ee50221295f80f39403 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Thu, 21 Jan 2021 12:23:02 +0200 Subject: [PATCH 07/31] changes for yam2 --- app/internal/search.py | 3 ++- app/main.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/internal/search.py b/app/internal/search.py index b7c7d7e9..662a83b2 100644 --- a/app/internal/search.py +++ b/app/internal/search.py @@ -1,7 +1,8 @@ +from typing import List + from app.database.database import SessionLocal from app.database.models import Event from sqlalchemy.exc import SQLAlchemyError -from typing import List def get_stripped_keywords(keywords: str) -> str: diff --git a/app/main.py b/app/main.py index a119ba71..54c965dc 100644 --- a/app/main.py +++ b/app/main.py @@ -2,7 +2,7 @@ from fastapi import FastAPI, Form, Request from fastapi.staticfiles import StaticFiles -from sqlalchemy.exc import SQLAlchemyError + from app.config import PSQL_ENVIRONMENT from app.database import models from app.database.database import engine From 6be4ae3e06267fca78d9c7697ae428a8c81cbd72 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Thu, 21 Jan 2021 12:27:50 +0200 Subject: [PATCH 08/31] changes for yam3 --- app/routers/search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routers/search.py b/app/routers/search.py index ce82e96e..7cb33670 100644 --- a/app/routers/search.py +++ b/app/routers/search.py @@ -31,7 +31,7 @@ async def show_results(request: Request, keywords: str = Form(None), db: Session results = None else: results = get_results_by_keywords(db, keywords, owner_id=current_user) - if len(results) == 0: + if results: message = f"No matching results for '{keywords}'." return templates.TemplateResponse("search.html", { From b389e8452feec5e91d9f955329038cc4032a1b23 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 10:50:51 +0200 Subject: [PATCH 09/31] merge developinto feature/search --- requirements1.txt | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 requirements1.txt diff --git a/requirements1.txt b/requirements1.txt deleted file mode 100644 index ffed4ae5..00000000 --- a/requirements1.txt +++ /dev/null @@ -1,28 +0,0 @@ -aiofiles==0.6.0 -atomicwrites==1.4.0 -attrs==20.3.0 -click==7.1.2 -colorama==0.4.4 -fastapi==0.63.0 -h11==0.12.0 -h2==4.0.0 -hpack==4.0.0 -hyperframe==6.0.0 -importlib-metadata==3.3.0 -iniconfig==1.1.1 -Jinja2==2.11.2 -MarkupSafe==1.1.1 -packaging==20.8 -pluggy==0.13.1 -priority==1.3.0 -py==1.10.0 -pydantic==1.7.3 -pyparsing==2.4.7 -pytest==6.2.1 -SQLAlchemy==1.3.22 -starlette==0.13.6 -toml==0.10.2 -typing-extensions==3.7.4.3 -uvicorn==0.13.3 -wsproto==1.0.0 -zipp==3.4.0 From 8b3de7ed574b9d8695dbc4203d8cca04d7da2c0d Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 10:52:17 +0200 Subject: [PATCH 10/31] merge develop into feature/search --- app/mydb.db | Bin 110592 -> 0 bytes app/postgres.session.sql | 1 - 2 files changed, 1 deletion(-) delete mode 100644 app/mydb.db delete mode 100644 app/postgres.session.sql diff --git a/app/mydb.db b/app/mydb.db deleted file mode 100644 index 8042df66372c3e7fc80e20330028fce48d149916..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110592 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 Date: Sun, 24 Jan 2021 10:53:14 +0200 Subject: [PATCH 11/31] merge develop into feature/search --- mydb.db | Bin 45056 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 mydb.db diff --git a/mydb.db b/mydb.db deleted file mode 100644 index 69ad32ec43cad9e5f1bb9e04b457c45f3784fde8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45056 zcmeI(&yLzg90zcZVeQ3)?p`X3_A*jifhAOhZmKAUl>)3cYlT48fut)nLJl^Y$+9@b z-ak!MwX{9<)_YHViJbckeSp40AD}Y>rhr&h9C|?ddNnr8{5bARXj@!yKQ(F6eqKmY;|fB*y_0D(l{FsqrL<;hDn@Pli1-3cy(K^%3MKYoy{ zxpt*xQ>SrYe?@28X|qAcF|^2hYjkYrWJFQ(?L2CTx?-}AMP2*QcI`&Zenu~%U>M18!>VR9^AnSt zBsQ{*4&EOdpw(uxZdV#N$+R2JaoeWFWF$)jCY5q1SyMZ*Ymd3lv|Fb^i+4-3 zY*~h7ZENPfNfZedebeVtqKYY*~>Cu{`JD> zyk=|V$ULA>jDl0Bs|BqDBh zc-0TPX97UKF-qLZu6 z^uc~%d^IafYdXX5iuwBm<`?+UkxPoNw|Eird`;G11px>^00Izz00bZa0SG_<0uX?} zrV1EFO8ID#e2S!$J2~-WSJsGme^SK%0Y1Rwwb2tWV=5P$## NAOHafKwu4le*vYa&*K09 From ad2ba2fdde8817b8b9776b362d779981c1c4e714 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 11:30:22 +0200 Subject: [PATCH 12/31] fixed flake8 errors --- app/database/crud.py | 40 --------------------------------------- app/database/models.py | 15 +++++++++++---- app/database/schemas.py | 38 ------------------------------------- app/internal/search.py | 24 ++++++++++++++--------- app/main.py | 7 +++---- app/routers/search.py | 21 +++++++++++++-------- tests/test_search.py | 42 ++++++++++++++++++++++++++--------------- 7 files changed, 69 insertions(+), 118 deletions(-) delete mode 100644 app/database/crud.py diff --git a/app/database/crud.py b/app/database/crud.py deleted file mode 100644 index 43d8049b..00000000 --- a/app/database/crud.py +++ /dev/null @@ -1,40 +0,0 @@ -from sqlalchemy.orm import Session -from werkzeug.security import check_password_hash, generate_password_hash -from . import models, schemas - - -def get_user(db: Session, user_id: int): - return db.query(models.User).filter(models.User.id == user_id).first() - - -def get_user_by_email(db: Session, email: str): - return db.query(models.User).filter(models.User.email == email).first() - - -def get_user_by_username(db: Session, username: str): - db.query(models.User).filter(models.User.username == username).first() - - -def get_users(db: Session, skip: int = 0, limit: int = 100): - return db.query(models.User).offset(skip).limit(limit).all() - - -def create_user(db: Session, user: schemas.UserCreate): - fake_hashed_password = generate_password_hash(user.password) - db_user = models.User(email=user.email, hashed_password=fake_hashed_password) - db.add(db_user) - db.commit() - db.refresh(db_user) - return db_user - - -def get_events(db: Session, skip: int = 0, limit: int = 100): - return db.query(models.Event).offset(skip).limit(limit).all() - - -def create_user_event(db: Session, event: schemas.EventCreate, user_id: int): - db_event = models.Event(**event.dict(), owner_id=user_id) - db.add(db_event) - db.commit() - db.refresh(db_event) - return db_event \ No newline at end of file diff --git a/app/database/models.py b/app/database/models.py index 591dd0be..ba8f4e28 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -4,7 +4,6 @@ from app.database.database import Base from sqlalchemy import (DDL, Boolean, Column, DateTime, ForeignKey, Index, Integer, String, event) -from sqlalchemy.dialects import postgresql from sqlalchemy.dialects.postgresql import TSVECTOR from sqlalchemy.orm import relationship @@ -58,7 +57,11 @@ class Event(Base): # PostgreSQL if PSQL_ENVIRONMENT: events_tsv = Column(TSVECTOR) - __table_args__ = (Index('events_tsv_idx', 'events_tsv', postgresql_using='gin'),) + __table_args__ = (Index( + 'events_tsv_idx', + 'events_tsv', + postgresql_using='gin'), + ) def __repr__(self): return f'' @@ -74,10 +77,14 @@ class PSQLEnvironmentError(Exception): CREATE TRIGGER ix_events_tsv_update BEFORE INSERT OR UPDATE ON events FOR EACH ROW EXECUTE PROCEDURE - tsvector_update_trigger(events_tsv,'pg_catalog.english', 'title', 'content') + tsvector_update_trigger(events_tsv,'pg_catalog.english','title','content') """) - event.listen(Event.__table__, 'after_create', trigger_snippet.execute_if(dialect='postgresql')) + event.listen( + Event.__table__, + 'after_create', + trigger_snippet.execute_if(dialect='postgresql') + ) class Invitation(Base): diff --git a/app/database/schemas.py b/app/database/schemas.py index 8cd256a2..e69de29b 100644 --- a/app/database/schemas.py +++ b/app/database/schemas.py @@ -1,38 +0,0 @@ -from typing import List, Optional -from datetime import datetime -from pydantic import BaseModel - - -class EventBase(BaseModel): - title: str - content: Optional[str] = None - - -class EventCreate(EventBase): - pass - - -class Event(EventBase): - id: int - owner_id: int - - class Config: - orm_mode = True - - -class UserBase(BaseModel): - username: str - email: str - - -class UserCreate(UserBase): - password: str - - -class User(UserBase): - id: int - is_active: bool - events: List[Event] = [] - - class Config: - orm_mode = True \ No newline at end of file diff --git a/app/internal/search.py b/app/internal/search.py index e97d9d63..2ef2323d 100644 --- a/app/internal/search.py +++ b/app/internal/search.py @@ -6,31 +6,37 @@ def get_stripped_keywords(keywords: str) -> str: - '''Gets a string of keywords to search for from the user form and returns a stripped ready-to-db-search keywords string''' + '''Gets a string of keywords to search for from the user form + and returns a stripped ready-to-db-search keywords string''' keywords = " ".join(keywords.split()) keywords = keywords.replace(" ", ":* & ") + ":*" return keywords -def get_results_by_keywords(session: SessionLocal, keywords: str, owner_id: int) -> List[Event]: - """Returns possible results for a search in the 'events' database table. +def get_results_by_keywords( + session: SessionLocal, + keywords: str, + owner_id: int + ) -> List[Event]: + """Returns possible results for a search in the 'events' database table Args: keywords (str): search string owner_id (int): current user id Returns: - list: a list of events from the database matching the inserted keywords. + list: a list of events from the database matching the inserted keywords - Uses PostgreSQL's built in 'Full-text search' feature (doesn't work with SQLite)""" + Uses PostgreSQL's built in 'Full-text search' feature + (doesn't work with SQLite)""" keywords = get_stripped_keywords(keywords) - - try: + + try: return session.query(Event).filter( - Event.owner_id == owner_id, - Event.events_tsv.match(keywords)).all() + Event.owner_id == owner_id, + Event.events_tsv.match(keywords)).all() except (SQLAlchemyError, AttributeError): return [] diff --git a/app/main.py b/app/main.py index dddf0631..14752a94 100644 --- a/app/main.py +++ b/app/main.py @@ -1,6 +1,4 @@ -import os - -from fastapi import FastAPI, Form, Request +from fastapi import FastAPI, Request from fastapi.staticfiles import StaticFiles from app.config import PSQL_ENVIRONMENT @@ -13,7 +11,8 @@ if 'sqlite' in str(engine.url) and PSQL_ENVIRONMENT: raise models.PSQLEnvironmentError( "You're trying to use PSQL features on SQLite env.\n" - "Please set app.config.PSQL_ENVIRONMENT to False and run the app again." + "Please set app.config.PSQL_ENVIRONMENT to False " + "and run the app again." ) else: models.Base.metadata.create_all(bind=engine) diff --git a/app/routers/search.py b/app/routers/search.py index c4581743..331bef5d 100644 --- a/app/routers/search.py +++ b/app/routers/search.py @@ -7,12 +7,13 @@ router = APIRouter() -templates = Jinja2Templates(directory="app/templates") @router.get("/search") def search(request: Request): - current_username = "Chuck Norris" # Made up until there's a user login system + # Made up user details until there's a user login system + current_username = "Chuck Norris" + return templates.TemplateResponse("search.html", { "request": request, "username": current_username @@ -20,10 +21,14 @@ def search(request: Request): @router.post("/search") -async def show_results(request: Request, keywords: str = Form(None), db: Session = Depends(get_db)): - current_username = "Chuck Norris" # Made up until there's a user login system - current_user = 1 # Made up until there's a user login system - +async def show_results( + request: Request, + keywords: str = Form(None), + db: Session = Depends(get_db)): + # Made up user details until there's a user login system + current_username = "Chuck Norris" + current_user = 1 + message = "" if not keywords: @@ -33,11 +38,11 @@ async def show_results(request: Request, keywords: str = Form(None), db: Session results = get_results_by_keywords(db, keywords, owner_id=current_user) if not results: message = f"No matching results for '{keywords}'." - + return templates.TemplateResponse("search.html", { "request": request, "username": current_username, "message": message, "results": results, "keywords": keywords - }) + }) \ No newline at end of file diff --git a/tests/test_search.py b/tests/test_search.py index a1def4f9..b5d895f5 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -31,7 +31,7 @@ def create_user(session): session.add(user) session.commit() return user - + @staticmethod def add_event(session, title, content, owner_id): event = Event( @@ -44,26 +44,38 @@ def add_event(session, title, content, owner_id): session.add(event) session.commit() - + @staticmethod def create_data(session): - user = TestSearch.create_user(session) + TestSearch.create_user(session) events = [ - {'title': "My first event", 'content': 'I am so excited', 'owner_id': 1}, - {'title': "My second event", 'content': 'I am very emotional', 'owner_id': 1}, - {'title': "Pick up my nephews", 'content': 'Very important', 'owner_id': 1}, - {'title': "Solve this ticket", 'content': 'I can do this', 'owner_id': 1}, - {'title': "Jam with my friends", 'content': "Jamming is fun", 'owner_id': 1}, - {'title': 'test', 'content': 'love string', 'owner_id': 1} + {'title': "My first event", + 'content': 'I am so excited', + 'owner_id': 1}, + {'title': "My second event", + 'content': 'I am very emotional', + 'owner_id': 1}, + {'title': "Pick up my nephews", + 'content': 'Very important', + 'owner_id': 1}, + {'title': "Solve this ticket", + 'content': 'I can do this', + 'owner_id': 1}, + {'title': "Jam with my friends", + 'content': "Jamming is fun", + 'owner_id': 1}, + {'title': 'test', + 'content': 'love string', + 'owner_id': 1} ] for event in events: - TestSearch.add_event(session, - title=event['title'], - content=event['content'], - owner_id=event['owner_id'] - ) - + TestSearch.add_event(session, + title=event['title'], + content=event['content'], + owner_id=event['owner_id'] + ) + @staticmethod def test_search_page_exists(client): resp = client.get(TestSearch.SEARCH) From f2126b515f8a5cee9fae0ec2d830713c730660f7 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 11:43:02 +0200 Subject: [PATCH 13/31] fixed flake8 errors2 --- app/internal/search.py | 8 +++--- app/main.py | 2 +- app/routers/search.py | 18 +++++++------ tests/test_search.py | 61 +++++++++++++++++++++++++----------------- 4 files changed, 52 insertions(+), 37 deletions(-) diff --git a/app/internal/search.py b/app/internal/search.py index 2ef2323d..47a83d4f 100644 --- a/app/internal/search.py +++ b/app/internal/search.py @@ -15,10 +15,10 @@ def get_stripped_keywords(keywords: str) -> str: def get_results_by_keywords( - session: SessionLocal, - keywords: str, - owner_id: int - ) -> List[Event]: + session: SessionLocal, + keywords: str, + owner_id: int + ) -> List[Event]: """Returns possible results for a search in the 'events' database table Args: diff --git a/app/main.py b/app/main.py index 14752a94..9399a227 100644 --- a/app/main.py +++ b/app/main.py @@ -16,7 +16,7 @@ ) else: models.Base.metadata.create_all(bind=engine) - + app = FastAPI() app.mount("/static", StaticFiles(directory=STATIC_PATH), name="static") diff --git a/app/routers/search.py b/app/routers/search.py index 331bef5d..e6ac2a6b 100644 --- a/app/routers/search.py +++ b/app/routers/search.py @@ -2,7 +2,6 @@ from app.dependencies import templates from app.internal.search import get_results_by_keywords from fastapi import APIRouter, Depends, Form, Request -from fastapi.templating import Jinja2Templates from sqlalchemy.orm import Session @@ -39,10 +38,13 @@ async def show_results( if not results: message = f"No matching results for '{keywords}'." - return templates.TemplateResponse("search.html", { - "request": request, - "username": current_username, - "message": message, - "results": results, - "keywords": keywords - }) \ No newline at end of file + return templates.TemplateResponse( + "search.html", + { + "request": request, + "username": current_username, + "message": message, + "results": results, + "keywords": keywords + } + ) diff --git a/tests/test_search.py b/tests/test_search.py index b5d895f5..e1335548 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -49,31 +49,44 @@ def add_event(session, title, content, owner_id): def create_data(session): TestSearch.create_user(session) events = [ - {'title': "My first event", - 'content': 'I am so excited', - 'owner_id': 1}, - {'title': "My second event", - 'content': 'I am very emotional', - 'owner_id': 1}, - {'title': "Pick up my nephews", - 'content': 'Very important', - 'owner_id': 1}, - {'title': "Solve this ticket", - 'content': 'I can do this', - 'owner_id': 1}, - {'title': "Jam with my friends", - 'content': "Jamming is fun", - 'owner_id': 1}, - {'title': 'test', - 'content': 'love string', - 'owner_id': 1} - ] - + { + 'title': "My first event", + 'content': 'I am so excited', + 'owner_id': 1 + }, + { + 'title': "My second event", + 'content': 'I am very emotional', + 'owner_id': 1 + }, + { + 'title': "Pick up my nephews", + 'content': 'Very important', + 'owner_id': 1 + }, + { + 'title': "Solve this ticket", + 'content': 'I can do this', + 'owner_id': 1 + }, + { + 'title': "Jam with my friends", + 'content': "Jamming is fun", + 'owner_id': 1 + }, + { + 'title': 'test', + 'content': 'love string', + 'owner_id': 1 + } + ] + for event in events: - TestSearch.add_event(session, - title=event['title'], - content=event['content'], - owner_id=event['owner_id'] + TestSearch.add_event( + session, + title=event['title'], + content=event['content'], + owner_id=event['owner_id'] ) @staticmethod From 5491fd85fb9c6734077000670cd53d75def57cd8 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 11:48:34 +0200 Subject: [PATCH 14/31] fixed flake8 errors3 --- app/routers/search.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/routers/search.py b/app/routers/search.py index e6ac2a6b..c6fd9fb6 100644 --- a/app/routers/search.py +++ b/app/routers/search.py @@ -21,9 +21,9 @@ def search(request: Request): @router.post("/search") async def show_results( - request: Request, - keywords: str = Form(None), - db: Session = Depends(get_db)): + request: Request, + keywords: str = Form(None), + db: Session = Depends(get_db)): # Made up user details until there's a user login system current_username = "Chuck Norris" current_user = 1 From bf7cd02cedabda6ed849bbf0583ac397147af21b Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 12:03:33 +0200 Subject: [PATCH 15/31] fixed pytest errors --- tests/conftest.py | 24 ++++++++++++++++-------- tests/test_search.py | 3 +++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 4a5cf6a2..eb9e3fa3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,9 @@ import pytest +from app.config import PSQL_ENVIRONMENT +from app.database.database import Base from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from app.database.database import Base pytest_plugins = [ 'tests.user_fixture', @@ -14,13 +15,20 @@ ] # When testing in a PostgreSQL environment please make sure that: -# Base string is a PSQL string -# app.config.PSQL_ENVIRONMENT is set to True -SQLALCHEMY_TEST_DATABASE_URL = "postgresql://postgres:1234@localhost/postgres" - -test_engine = create_engine( - SQLALCHEMY_TEST_DATABASE_URL -) +# - Base string is a PSQL string +# - app.config.PSQL_ENVIRONMENT is set to True + +if PSQL_ENVIRONMENT: + SQLALCHEMY_TEST_DATABASE_URL = "postgresql://postgres:1234@localhost/postgres" + test_engine = create_engine( + SQLALCHEMY_TEST_DATABASE_URL + ) + +else: + SQLALCHEMY_TEST_DATABASE_URL = "sqlite:///./test.db" + test_engine = create_engine( + SQLALCHEMY_TEST_DATABASE_URL, connect_args={"check_same_thread": False} + ) TestingSessionLocal = sessionmaker( autocommit=False, autoflush=False, bind=test_engine) diff --git a/tests/test_search.py b/tests/test_search.py index e1335548..e5aa5b3b 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -1,6 +1,7 @@ from datetime import datetime import pytest +from app.config import PSQL_ENVIRONMENT from app.database.models import Event, User from app.internal.search import get_stripped_keywords from fastapi import status @@ -96,6 +97,7 @@ def test_search_page_exists(client): assert b'Search event by keyword' in resp.content +@pytest.mark.skipif(not PSQL_ENVIRONMENT, reason="Not PSQL environment") @pytest.mark.parametrize('data, string', TestSearch.GOOD_KEYWORDS) def test_search_good_keywords(data, string, client, session): ts = TestSearch() @@ -104,6 +106,7 @@ def test_search_good_keywords(data, string, client, session): assert string in resp.content +@pytest.mark.skipif(not PSQL_ENVIRONMENT, reason="Not PSQL environment") @pytest.mark.parametrize('data, string', TestSearch.BAD_KEYWORDS) def test_search_bad_keywords(data, string, client, session): ts = TestSearch() From 2b56c3d3d6210dc1a4c140cd2332cde9a452a143 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 12:10:21 +0200 Subject: [PATCH 16/31] fixed pytest errors2 --- tests/conftest.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index eb9e3fa3..4796b73f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,7 +19,10 @@ # - app.config.PSQL_ENVIRONMENT is set to True if PSQL_ENVIRONMENT: - SQLALCHEMY_TEST_DATABASE_URL = "postgresql://postgres:1234@localhost/postgres" + SQLALCHEMY_TEST_DATABASE_URL = ( + "postgresql://postgres:1234" + "@localhost/postgres" + ) test_engine = create_engine( SQLALCHEMY_TEST_DATABASE_URL ) From 5140285261fea3f9d4dc2742f5f4b1975cd4ece7 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 12:30:26 +0200 Subject: [PATCH 17/31] added tests for non-psql env --- tests/test_search.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_search.py b/tests/test_search.py index e5aa5b3b..ffe94dae 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -25,6 +25,14 @@ class TestSearch: ({'keywords': '[]'}, b'No matching'), ({'keywords': 'firsttttt'}, b'No matching') ] + NOT_PSQL_ENV_KEYWORDS = [ + ({'keywords': 'lov'}, b'No matching'), + ({'keywords': 'very emotional'}, b'No matching'), + ({'keywords': 'event'}, b'No matching'), + ({'keywords': 'jam'}, b'No matching'), + ({'keywords': ' jam '}, b'No matching'), + ({'keywords': ''}, b'Invalid') + ] @staticmethod def create_user(session): @@ -115,6 +123,15 @@ def test_search_bad_keywords(data, string, client, session): assert string in resp.content +@pytest.mark.skipif(PSQL_ENVIRONMENT, reason="PSQL environment") +@pytest.mark.parametrize('data, string', TestSearch.NOT_PSQL_ENV_KEYWORDS) +def test_search_not_psql_env_keywords(data, string, client, session): + ts = TestSearch() + ts.create_data(session) + resp = client.post(ts.SEARCH, data=data) + assert string in resp.content + + STRIPPED_KEYWORDS = [ (" love string ", "love:* & string:*"), ("test ", "test:*"), From 6542bf2ff57b4fe69324643285ce4a8c0b749a55 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 12:50:13 +0200 Subject: [PATCH 18/31] added tests for non-psql env2 --- tests/test_search.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/test_search.py b/tests/test_search.py index ffe94dae..91a96b88 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -3,7 +3,7 @@ import pytest from app.config import PSQL_ENVIRONMENT from app.database.models import Event, User -from app.internal.search import get_stripped_keywords +from app.internal.search import get_results_by_keywords, get_stripped_keywords from fastapi import status @@ -33,6 +33,13 @@ class TestSearch: ({'keywords': ' jam '}, b'No matching'), ({'keywords': ''}, b'Invalid') ] + KEYWORDS_FOR_FUNC = [ + 'lov', + 'very emotional', + 'event', + 'jam', + ' jam ' + ] @staticmethod def create_user(session): @@ -132,6 +139,14 @@ def test_search_not_psql_env_keywords(data, string, client, session): assert string in resp.content +@pytest.mark.skipif(PSQL_ENVIRONMENT, reason="Not PSQL environment") +@pytest.mark.parametrize('input_string', TestSearch.KEYWORDS_FOR_FUNC) +def test_get_results_by_keywords_func(input_string, client, session): + ts = TestSearch() + ts.create_data(session) + assert not get_results_by_keywords(session, input_string, 1) + + STRIPPED_KEYWORDS = [ (" love string ", "love:* & string:*"), ("test ", "test:*"), From 36ccb5faaf5430dbf608f7c723abe70cf50f8c03 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 13:41:16 +0200 Subject: [PATCH 19/31] added tests for non-psql env3 --- tests/test_search.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_search.py b/tests/test_search.py index 91a96b88..7e2f0610 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -158,3 +158,19 @@ def test_get_results_by_keywords_func(input_string, client, session): @pytest.mark.parametrize('input_string, output_string', STRIPPED_KEYWORDS) def test_search_stripped_keywords(input_string, output_string): assert get_stripped_keywords(input_string) == output_string + + +def test_events_tsv_column_exists(): + column_created = True + + try: + Event.events_tsv + except AttributeError: + column_created = False + + if PSQL_ENVIRONMENT: + assert column_created + else: + assert not column_created + + From 810e7247e6edf48712c7f16ea601e1040aae76f3 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 13:44:02 +0200 Subject: [PATCH 20/31] added tests for non-psql env4 --- tests/test_search.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_search.py b/tests/test_search.py index 7e2f0610..806ebeaf 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -172,5 +172,3 @@ def test_events_tsv_column_exists(): assert column_created else: assert not column_created - - From 51c09b328c85300bb26e649222683269cf40dc89 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 18:26:50 +0200 Subject: [PATCH 21/31] added psql_environment test --- app/main.py | 22 ++++++++++++---------- tests/conftest.py | 16 ++++++++++++++++ tests/test_psql_environment.py | 11 +++++++++++ tests/test_search.py | 2 +- 4 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 tests/test_psql_environment.py diff --git a/app/main.py b/app/main.py index 9399a227..911f8cbf 100644 --- a/app/main.py +++ b/app/main.py @@ -8,16 +8,18 @@ from app.routers import agenda, email, event, invitation, profile, search -if 'sqlite' in str(engine.url) and PSQL_ENVIRONMENT: - raise models.PSQLEnvironmentError( - "You're trying to use PSQL features on SQLite env.\n" - "Please set app.config.PSQL_ENVIRONMENT to False " - "and run the app again." - ) -else: - models.Base.metadata.create_all(bind=engine) - - +def create_tables(engine, psql_environment): + if 'sqlite' in str(engine.url) and psql_environment: + raise models.PSQLEnvironmentError( + "You're trying to use PSQL features on SQLite env.\n" + "Please set app.config.PSQL_ENVIRONMENT to False " + "and run the app again." + ) + else: + models.Base.metadata.create_all(bind=engine) + + +create_tables(engine, PSQL_ENVIRONMENT) app = FastAPI() app.mount("/static", StaticFiles(directory=STATIC_PATH), name="static") app.mount("/media", StaticFiles(directory=MEDIA_PATH), name="media") diff --git a/tests/conftest.py b/tests/conftest.py index 4796b73f..4a82399b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -48,3 +48,19 @@ def session(): yield session session.close() Base.metadata.drop_all(bind=test_engine) + + +@pytest.fixture +def sqlite_engine(): + SQLALCHEMY_TEST_DATABASE_URL = "sqlite:///./test.db" + sqlite_test_engine = create_engine( + SQLALCHEMY_TEST_DATABASE_URL, connect_args={"check_same_thread": False} + ) + + TestingSession = sessionmaker( + autocommit=False, autoflush=False, bind=sqlite_test_engine) + + yield sqlite_test_engine + session = TestingSession() + session.close() + Base.metadata.drop_all(bind=sqlite_test_engine) diff --git a/tests/test_psql_environment.py b/tests/test_psql_environment.py new file mode 100644 index 00000000..7276397d --- /dev/null +++ b/tests/test_psql_environment.py @@ -0,0 +1,11 @@ +import pytest +from app.database.models import PSQLEnvironmentError +from app.main import create_tables + + +def test_main_create_tables_error(sqlite_engine): + raised_error = False + with pytest.raises(PSQLEnvironmentError): + create_tables(sqlite_engine, True) + raised_error = True + assert raised_error diff --git a/tests/test_search.py b/tests/test_search.py index 806ebeaf..9d7a6de8 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -2,7 +2,7 @@ import pytest from app.config import PSQL_ENVIRONMENT -from app.database.models import Event, User +from app.database.models import Base, Event, User from app.internal.search import get_results_by_keywords, get_stripped_keywords from fastapi import status From de4a6a9f8f81dce29cccff45cd17c41755938414 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 18:28:33 +0200 Subject: [PATCH 22/31] added psql_environment test1 --- tests/test_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_search.py b/tests/test_search.py index 9d7a6de8..806ebeaf 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -2,7 +2,7 @@ import pytest from app.config import PSQL_ENVIRONMENT -from app.database.models import Base, Event, User +from app.database.models import Event, User from app.internal.search import get_results_by_keywords, get_stripped_keywords from fastapi import status From be648fce6a421b254f3e46862cdadd27d79d3b35 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 18:53:13 +0200 Subject: [PATCH 23/31] added psql_environment test2 --- app/database/database.py | 16 ++++++++-------- tests/test_psql_environment.py | 11 +++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/app/database/database.py b/app/database/database.py index d7fcccbd..07a24f5a 100644 --- a/app/database/database.py +++ b/app/database/database.py @@ -10,16 +10,16 @@ SQLALCHEMY_DATABASE_URL = os.getenv( "DATABASE_CONNECTION_STRING", config.DEVELOPMENT_DATABASE_STRING) -if not config.PSQL_ENVIRONMENT: - engine = create_engine( - SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} - ) -else: - engine = create_engine( - SQLALCHEMY_DATABASE_URL - ) +def create_env_engine(psql_environment): + if not psql_environment: + return create_engine( + SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) + return create_engine(SQLALCHEMY_DATABASE_URL) + + +engine = create_env_engine(config.PSQL_ENVIRONMENT) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() diff --git a/tests/test_psql_environment.py b/tests/test_psql_environment.py index 7276397d..747fa2a6 100644 --- a/tests/test_psql_environment.py +++ b/tests/test_psql_environment.py @@ -1,4 +1,6 @@ import pytest +from app.config import PSQL_ENVIRONMENT +from app.database.database import create_env_engine from app.database.models import PSQLEnvironmentError from app.main import create_tables @@ -9,3 +11,12 @@ def test_main_create_tables_error(sqlite_engine): create_tables(sqlite_engine, True) raised_error = True assert raised_error + + +def test_database_create_engine(): + if PSQL_ENVIRONMENT: + engine = create_env_engine(True) + assert 'postgres' in str(engine.url) + else: + engine = create_env_engine(False) + assert 'sqlite' in str(engine.url) From 877a072dd5d04cfc07a59ab8b66724069bcfb017 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 19:18:20 +0200 Subject: [PATCH 24/31] Coverage improvement --- app/database/database.py | 8 ++++---- tests/test_psql_environment.py | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/database/database.py b/app/database/database.py index 07a24f5a..c0544c0c 100644 --- a/app/database/database.py +++ b/app/database/database.py @@ -11,15 +11,15 @@ "DATABASE_CONNECTION_STRING", config.DEVELOPMENT_DATABASE_STRING) -def create_env_engine(psql_environment): +def create_env_engine(psql_environment, sqlalchemy_database_url): if not psql_environment: return create_engine( - SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) + sqlalchemy_database_url, connect_args={"check_same_thread": False}) - return create_engine(SQLALCHEMY_DATABASE_URL) + return create_engine(sqlalchemy_database_url) -engine = create_env_engine(config.PSQL_ENVIRONMENT) +engine = create_env_engine(config.PSQL_ENVIRONMENT, SQLALCHEMY_DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() diff --git a/tests/test_psql_environment.py b/tests/test_psql_environment.py index 747fa2a6..b522c190 100644 --- a/tests/test_psql_environment.py +++ b/tests/test_psql_environment.py @@ -14,9 +14,9 @@ def test_main_create_tables_error(sqlite_engine): def test_database_create_engine(): - if PSQL_ENVIRONMENT: - engine = create_env_engine(True) - assert 'postgres' in str(engine.url) - else: - engine = create_env_engine(False) - assert 'sqlite' in str(engine.url) + sqlalchemy_database_url = "postgresql://postgres:h1h2h3h4@localhost/postgres" + engine = create_env_engine(True, sqlalchemy_database_url) + assert 'postgres' in str(engine.url) + sqlalchemy_database_url = "sqlite:///./test1.db" + engine = create_env_engine(False, sqlalchemy_database_url) + assert 'sqlite' in str(engine.url) From e7e1d7bf014f4bff6cf4fb9c4dfb474ec4249711 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 19:19:53 +0200 Subject: [PATCH 25/31] Coverage improvement1 --- tests/test_psql_environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_psql_environment.py b/tests/test_psql_environment.py index b522c190..78274705 100644 --- a/tests/test_psql_environment.py +++ b/tests/test_psql_environment.py @@ -14,7 +14,7 @@ def test_main_create_tables_error(sqlite_engine): def test_database_create_engine(): - sqlalchemy_database_url = "postgresql://postgres:h1h2h3h4@localhost/postgres" + sqlalchemy_database_url = "postgresql://postgres:1234@localhost/postgres" engine = create_env_engine(True, sqlalchemy_database_url) assert 'postgres' in str(engine.url) sqlalchemy_database_url = "sqlite:///./test1.db" From 93ca6b6cb5f69cfe2d5b7f32a01bef48682d50ed Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 19:21:16 +0200 Subject: [PATCH 26/31] Coverage improvement --- tests/test_psql_environment.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_psql_environment.py b/tests/test_psql_environment.py index 78274705..bc2aeab4 100644 --- a/tests/test_psql_environment.py +++ b/tests/test_psql_environment.py @@ -1,5 +1,4 @@ import pytest -from app.config import PSQL_ENVIRONMENT from app.database.database import create_env_engine from app.database.models import PSQLEnvironmentError from app.main import create_tables From fec4d9d47a5cfa318a8bd2ef9c56b132ce270516 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 20:00:15 +0200 Subject: [PATCH 27/31] Coverage improvement3 --- app/database/models.py | 40 +++++++++++++++++++++++++--------- tests/conftest.py | 17 +++++++++++++++ tests/test_psql_environment.py | 7 +++++- 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/app/database/models.py b/app/database/models.py index ba8f4e28..92d49c0c 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -40,6 +40,18 @@ def __repr__(self): return f'' +def create_events_tsv(psql_environment): + if psql_environment: + events_tsv = Column(TSVECTOR) + __table_args__ = (Index( + 'events_tsv_idx', + 'events_tsv', + postgresql_using='gin'), + ) + return True + return False + + class Event(Base): __tablename__ = "events" @@ -67,12 +79,8 @@ def __repr__(self): return f'' -class PSQLEnvironmentError(Exception): - pass - - # PostgreSQL -if PSQL_ENVIRONMENT: +def create_event_listen(psql_environment): trigger_snippet = DDL(""" CREATE TRIGGER ix_events_tsv_update BEFORE INSERT OR UPDATE ON events @@ -80,11 +88,23 @@ class PSQLEnvironmentError(Exception): tsvector_update_trigger(events_tsv,'pg_catalog.english','title','content') """) - event.listen( - Event.__table__, - 'after_create', - trigger_snippet.execute_if(dialect='postgresql') - ) + if psql_environment: + event.listen( + Event.__table__, + 'after_create', + trigger_snippet.execute_if(dialect='postgresql') + ) + return True + return False + + +# PostgreSQL +create_event_listen(PSQL_ENVIRONMENT) + + +# PostgreSQL +class PSQLEnvironmentError(Exception): + pass class Invitation(Base): diff --git a/tests/conftest.py b/tests/conftest.py index 4a82399b..1c75e566 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -64,3 +64,20 @@ def sqlite_engine(): session = TestingSession() session.close() Base.metadata.drop_all(bind=sqlite_test_engine) + + +@pytest.fixture +def psql_session(): + SQLALCHEMY_TEST_DATABASE_URL = ( + "postgresql://postgres:1234" + "@localhost/postgres" + ) + psql_test_engine = create_engine(SQLALCHEMY_TEST_DATABASE_URL) + TestingPsqlSession = sessionmaker( + autocommit=False, autoflush=False, bind=psql_test_engine) + + Base.metadata.create_all(bind=psql_test_engine) + session = TestingPsqlSession() + yield session + session.close() + Base.metadata.drop_all(bind=psql_test_engine) diff --git a/tests/test_psql_environment.py b/tests/test_psql_environment.py index bc2aeab4..d817cef9 100644 --- a/tests/test_psql_environment.py +++ b/tests/test_psql_environment.py @@ -1,6 +1,6 @@ import pytest from app.database.database import create_env_engine -from app.database.models import PSQLEnvironmentError +from app.database.models import PSQLEnvironmentError, create_event_listen from app.main import create_tables @@ -19,3 +19,8 @@ def test_database_create_engine(): sqlalchemy_database_url = "sqlite:///./test1.db" engine = create_env_engine(False, sqlalchemy_database_url) assert 'sqlite' in str(engine.url) + + +def test_database_psql_event_listen(psql_session): + assert create_event_listen(True) + assert not create_event_listen(False) From c3c1db4abdfa7847a5c6ff79e097183a5d00ea2e Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Sun, 24 Jan 2021 20:05:18 +0200 Subject: [PATCH 28/31] Coverage improvement --- app/database/models.py | 40 +++++++++------------------------- tests/conftest.py | 17 --------------- tests/test_psql_environment.py | 7 +----- 3 files changed, 11 insertions(+), 53 deletions(-) diff --git a/app/database/models.py b/app/database/models.py index 92d49c0c..ba8f4e28 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -40,18 +40,6 @@ def __repr__(self): return f'' -def create_events_tsv(psql_environment): - if psql_environment: - events_tsv = Column(TSVECTOR) - __table_args__ = (Index( - 'events_tsv_idx', - 'events_tsv', - postgresql_using='gin'), - ) - return True - return False - - class Event(Base): __tablename__ = "events" @@ -79,8 +67,12 @@ def __repr__(self): return f'' +class PSQLEnvironmentError(Exception): + pass + + # PostgreSQL -def create_event_listen(psql_environment): +if PSQL_ENVIRONMENT: trigger_snippet = DDL(""" CREATE TRIGGER ix_events_tsv_update BEFORE INSERT OR UPDATE ON events @@ -88,23 +80,11 @@ def create_event_listen(psql_environment): tsvector_update_trigger(events_tsv,'pg_catalog.english','title','content') """) - if psql_environment: - event.listen( - Event.__table__, - 'after_create', - trigger_snippet.execute_if(dialect='postgresql') - ) - return True - return False - - -# PostgreSQL -create_event_listen(PSQL_ENVIRONMENT) - - -# PostgreSQL -class PSQLEnvironmentError(Exception): - pass + event.listen( + Event.__table__, + 'after_create', + trigger_snippet.execute_if(dialect='postgresql') + ) class Invitation(Base): diff --git a/tests/conftest.py b/tests/conftest.py index 1c75e566..4a82399b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -64,20 +64,3 @@ def sqlite_engine(): session = TestingSession() session.close() Base.metadata.drop_all(bind=sqlite_test_engine) - - -@pytest.fixture -def psql_session(): - SQLALCHEMY_TEST_DATABASE_URL = ( - "postgresql://postgres:1234" - "@localhost/postgres" - ) - psql_test_engine = create_engine(SQLALCHEMY_TEST_DATABASE_URL) - TestingPsqlSession = sessionmaker( - autocommit=False, autoflush=False, bind=psql_test_engine) - - Base.metadata.create_all(bind=psql_test_engine) - session = TestingPsqlSession() - yield session - session.close() - Base.metadata.drop_all(bind=psql_test_engine) diff --git a/tests/test_psql_environment.py b/tests/test_psql_environment.py index d817cef9..bc2aeab4 100644 --- a/tests/test_psql_environment.py +++ b/tests/test_psql_environment.py @@ -1,6 +1,6 @@ import pytest from app.database.database import create_env_engine -from app.database.models import PSQLEnvironmentError, create_event_listen +from app.database.models import PSQLEnvironmentError from app.main import create_tables @@ -19,8 +19,3 @@ def test_database_create_engine(): sqlalchemy_database_url = "sqlite:///./test1.db" engine = create_env_engine(False, sqlalchemy_database_url) assert 'sqlite' in str(engine.url) - - -def test_database_psql_event_listen(psql_session): - assert create_event_listen(True) - assert not create_event_listen(False) From 64d49a1d312b3615f7917d0c0ca97182a544d2a7 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Mon, 25 Jan 2021 17:56:34 +0200 Subject: [PATCH 29/31] Fixes for yam --- .gitignore | 5 +++++ app/routers/search.py | 18 ++++++++---------- tests/conftest.py | 6 +++--- tests/test_search.py | 11 +++++------ 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 72930072..f12d728b 100644 --- a/.gitignore +++ b/.gitignore @@ -134,3 +134,8 @@ dmypy.json # Pyre type checker .pyre/ + + +# settings +.vscode/ +app/.vscode/ \ No newline at end of file diff --git a/app/routers/search.py b/app/routers/search.py index c6fd9fb6..60fee140 100644 --- a/app/routers/search.py +++ b/app/routers/search.py @@ -38,13 +38,11 @@ async def show_results( if not results: message = f"No matching results for '{keywords}'." - return templates.TemplateResponse( - "search.html", - { - "request": request, - "username": current_username, - "message": message, - "results": results, - "keywords": keywords - } - ) + return templates.TemplateResponse("search.html", { + "request": request, + "username": current_username, + "message": message, + "results": results, + "keywords": keywords + } + ) diff --git a/tests/conftest.py b/tests/conftest.py index 4a82399b..a5e0defd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,9 +20,9 @@ if PSQL_ENVIRONMENT: SQLALCHEMY_TEST_DATABASE_URL = ( - "postgresql://postgres:1234" - "@localhost/postgres" - ) + "postgresql://postgres:1234" + "@localhost/postgres" + ) test_engine = create_engine( SQLALCHEMY_TEST_DATABASE_URL ) diff --git a/tests/test_search.py b/tests/test_search.py index 806ebeaf..17c4236d 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -98,12 +98,11 @@ def create_data(session): ] for event in events: - TestSearch.add_event( - session, - title=event['title'], - content=event['content'], - owner_id=event['owner_id'] - ) + TestSearch.add_event(session, + title=event['title'], + content=event['content'], + owner_id=event['owner_id'] + ) @staticmethod def test_search_page_exists(client): From 585afeba287509342ced98a1fe6ed6fb0398281e Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Mon, 25 Jan 2021 18:06:01 +0200 Subject: [PATCH 30/31] Fixes for yam & flake8 --- app/main.py | 3 ++- tests/test_search.py | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/main.py b/app/main.py index 911f8cbf..33d9c932 100644 --- a/app/main.py +++ b/app/main.py @@ -5,7 +5,8 @@ from app.database import models from app.database.database import engine from app.dependencies import MEDIA_PATH, STATIC_PATH, templates -from app.routers import agenda, email, event, invitation, profile, search +from app.routers import (agenda, dayview, email, event, invitation, profile, + search) def create_tables(engine, psql_environment): diff --git a/tests/test_search.py b/tests/test_search.py index 17c4236d..6a62f844 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -99,10 +99,9 @@ def create_data(session): for event in events: TestSearch.add_event(session, - title=event['title'], - content=event['content'], - owner_id=event['owner_id'] - ) + title=event['title'], + content=event['content'], + owner_id=event['owner_id']) @staticmethod def test_search_page_exists(client): From e8f067dfa14be857e0556f0af7cdbfe3009304d1 Mon Sep 17 00:00:00 2001 From: Adva Alkalay Date: Tue, 26 Jan 2021 12:02:53 +0200 Subject: [PATCH 31/31] Removed settings.json --- .vscode/settings.json | 18 ------------------ app/.vscode/settings.json | 18 ------------------ 2 files changed, 36 deletions(-) delete mode 100644 .vscode/settings.json delete mode 100644 app/.vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index e9e2ae6b..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "python.pythonPath": "..\\calendar-env\\Scripts\\python.exe", - "python.linting.pylintEnabled": false, - "python.linting.mypyEnabled": true, - "python.linting.enabled": true, - "sqltools.connections": [ - { - "previewLimit": 50, - "server": "localhost", - "port": 5432, - "driver": "PostgreSQL", - "name": "Postgre", - "database": "postgres", - "username": "postgres", - "password": "h1h2h3h4" - } - ] -} \ No newline at end of file diff --git a/app/.vscode/settings.json b/app/.vscode/settings.json deleted file mode 100644 index f2389e1e..00000000 --- a/app/.vscode/settings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "python.linting.pylintEnabled": false, - "python.linting.mypyEnabled": true, - "python.linting.enabled": true, - "sqltools.connections": [ - { - "previewLimit": 50, - "server": "localhost", - "port": 5432, - "driver": "PostgreSQL", - "name": "postgres", - "database": "postgres", - "username": "postgres", - "password": "h1h2h3h4" - } - ], - "python.pythonPath": "f:\\calendar-env\\Scripts\\python.exe" -} \ No newline at end of file