Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feature/user management #24

Draft
wants to merge 12 commits into
base: staging
Choose a base branch
from
Binary file added .DS_Store
Binary file not shown.
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add this file to .gitignore

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add the .DS_Store and clean them up

"python.linting.banditEnabled": false,
"python.linting.enabled": true,
"python.linting.flake8Enabled": true
}
Binary file added alembic/.DS_Store
Binary file not shown.
Binary file added alembic/versions/.DS_Store
Binary file not shown.
41 changes: 34 additions & 7 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from typing import List
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from app.settings import settings # Settings module must be imported before all modules so that it will be available
from app import models
from app.models import SessionLocal, engine
from app.routers import router
# Settings module must be imported before all modules so that it will be available
from app.settings import settings
from app.models import SessionLocal
from .routers import users, movie, music
from .dependencies import jwt_authentication

app = FastAPI(title="Ocena")

app = FastAPI()

app.add_middleware(
CORSMiddleware,
Expand All @@ -19,7 +19,29 @@
allow_origin_regex=settings.origins_regex
)

app.include_router(router)
app.include_router(
users.fastapi_users.get_users_router(),
prefix="/auth",
tags=["users"],
)
app.include_router(
users.fastapi_users.get_register_router(),
prefix="/auth",
tags=["auth"],
)
app.include_router(
users.fastapi_users.get_auth_router(jwt_authentication),
prefix="/auth",
tags=["auth"],
)
app.include_router(
users.fastapi_users.get_reset_password_router(),
prefix="/auth",
tags=["auth"],
)
app.include_router(movie.router)
app.include_router(music.router)


# keeps clashing with alembic for table creation
# Uncomment to use poor man's table creation
Expand All @@ -33,3 +55,8 @@ def get_db():
yield db
finally:
db.close()


@app.get("/")
async def root():
return {"message": "Ocena, 'Rating'!"}
35 changes: 35 additions & 0 deletions app/dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from fastapi import Depends, Request
from fastapi_users import BaseUserManager
from typing import Optional
from fastapi_users.authentication import JWTAuthentication

from app.models.models import UserCreate, UserDB
# from .db import get_user_db
from app.settings import SECRET


class UserManager(BaseUserManager[UserCreate, UserDB]):
user_db_model = UserDB
reset_password_token_secret = SECRET
verification_token_secret = SECRET

async def on_after_register(self, user: UserDB, request: Optional[Request] = None):
print(f"User {user.id} has registered.")

async def on_after_forgot_password(
self, user: UserDB, token: str, request: Optional[Request] = None
):
print(f"User {user.id} has forgot their password. Reset token: {token}")

async def on_after_request_verify(
self, user: UserDB, token: str, request: Optional[Request] = None
):
print(
f"Verification requested for user {user.id}. Verification token: {token}")


async def get_user_manager(user_db):
yield UserManager(user_db)

jwt_authentication = JWTAuthentication(
secret=SECRET, lifetime_seconds=3600, tokenUrl="auth/login")
6 changes: 4 additions & 2 deletions app/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
Base = declarative_base()

if settings.debug:
engine = create_engine(settings.database_url, connect_args={"check_same_thread": False})
engine = create_engine(settings.database_url, connect_args={
"check_same_thread": False})
else:
engine = create_engine(settings.database_url)

Expand Down Expand Up @@ -42,7 +43,8 @@ def __eq__(self, other):
return self.__hash__() == other.__hash__()


SessionLocal = sessionmaker(autocommit=False, class_=HashableSession, autoflush=False, bind=engine)
SessionLocal = sessionmaker(
autocommit=False, class_=HashableSession, autoflush=False, bind=engine)


def get_db():
Expand Down
28 changes: 23 additions & 5 deletions app/models/models.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
from fastapi_users import models
import datetime
from sqlalchemy import (Boolean, Column, ForeignKey,
Integer, DateTime, String,
Integer, DateTime, String,
UniqueConstraint, JSON, DateTime,
)
)
from sqlalchemy.orm import relationship

from app.models import Base


class User(models.BaseUser):
pass


class UserCreate(models.BaseUserCreate):
pass


class UserUpdate(models.BaseUserUpdate):
pass


class UserDB(User, models.BaseUserDB):
pass


class Movie(Base):
__tablename__ = "movies"
__table_args__ = (UniqueConstraint('name', 'engine'),)

# meta field names
id = Column(Integer, primary_key=True, index=True)
date_created = Column(DateTime)
#actual field names mapped from gophie_core
# actual field names mapped from gophie_core
name = Column(String)
engine = Column(String)
description = Column(String)
Expand Down Expand Up @@ -74,12 +91,13 @@ class Rating(Base):

owner = relationship("Movie", back_populates="ratings")


class Music(Base):
__tablename__ = "music"
__table_args__ = (UniqueConstraint('source', 'download_link'),)
index = Column(Integer, primary_key=True, index=True)
date_created = Column(DateTime)
#actual field names mapped from mythra
# actual field names mapped from mythra
artiste = Column(String)
title = Column(String)
collection = Column(String)
Expand Down
3 changes: 2 additions & 1 deletion app/models/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ def int_for_range(start_id, end_id):
else:
return column >= start_id

q = session.query(column, func.row_number().over(order_by=column).label('rownum'))
q = session.query(column, func.row_number().over(
order_by=column).label('rownum'))
if windowsize > 1:
q = q.filter(sqlalchemy.text("movies.id %% %d=1" % windowsize))

Expand Down
Empty file added app/routers/__init__.py
Empty file.
62 changes: 23 additions & 39 deletions app/routers.py → app/routers/movie.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from typing import List
import contextlib
import requests
import logging

from fastapi import Depends, FastAPI, HTTPException, APIRouter
from fastapi import Depends, APIRouter
from sqlalchemy.orm import Session

from app.settings import settings
Expand All @@ -12,97 +11,99 @@

router = APIRouter()

@router.post("/movie/ratings/average/", response_model=schemas.AverageRating)

@router.post("/movie/ratings/average/", response_model=schemas.AverageRating, tags=["movie"])
def get_average_ratings(movie: schemas.MovieRating, db: Session = Depends(get_db)):
"""
Get average movie ratings and number of people who have rated
"""
return crud.get_movie_average_ratings(db=db, movie=movie)


@router.post("/movie/rating/", response_model=schemas.Rating)
@router.post("/movie/rating/", response_model=schemas.Rating, tags=["movie"])
def get_ip_rating(spec_rating: schemas.SpecificRating, db: Session = Depends(get_db)):
"""
Get Rating of a movie by an ip_address
"""
return crud.get_rating(db=db, spec_rating=spec_rating)


@router.post("/movie/downloads/", response_model=int)
@router.post("/movie/downloads/", response_model=int, tags=["movie"])
def get_downloads(movie: schemas.MovieRating, db: Session = Depends(get_db)):
"""
Gets number of downloads of a movie
"""
return crud.get_number_of_downloads(db=db, movie=movie)


@router.post("/movie/referrals/", response_model=int)
@router.post("/movie/referrals/", response_model=int, tags=["movie"])
def get_referrals(movie: schemas.MovieRating, db: Session = Depends(get_db)):
"""
Gets total number of referrals of a movie
"""
return crud.get_no_of_referrals(db=db, movie=movie)


@router.post("/rate/", response_model=schemas.Rating)
@router.post("/rate/", response_model=schemas.Rating, tags=["movie"])
def create_or_update_rating(spec_rating: schemas.SpecificRatingScore, db: Session = Depends(get_db)):
"""
Create or update a movie rating by an ip_address
"""
return crud.create_or_update_rating(db=db, spec_rating=spec_rating)


@router.post("/referral/", response_model=schemas.Referral)
@router.post("/referral/", response_model=schemas.Referral, tags=["movie"])
def refer_to_movie(referral: schemas.ReferralCreate, db: Session = Depends(get_db)):
"""
Create a referral object for a movie and return referral id
"""
return crud.create_referral(db=db, referral=referral)


@router.post("/referral/id/", response_model=schemas.MovieReferral)
@router.post("/referral/id/", response_model=schemas.MovieReferral, tags=["movie"])
def get_referral_by_id(referral_id: str, db: HashableSession = Depends(get_db)):
"""
Get movie object by referral id
"""
return crud.get_movie_by_referral_id(db=db, referral_id=referral_id)


@router.post("/download/", response_model=schemas.Download)
@router.post("/download/", response_model=schemas.Download, tags=["movie"])
def download_movie(download: schemas.DownloadCreate, db: Session = Depends(get_db)):
"""
Create a download object for a movie by ip_address
"""
return crud.create_download(db=db, download=download)


@router.post("/download/highest/", response_model=List[schemas.MovieDownloads])
@router.post("/download/highest/", response_model=List[schemas.MovieDownloads], tags=["movie"])
def filter_highest_downloads(filter_: schemas.DownloadFilter, db: HashableSession = Depends(get_db)):
"""
Get the most downloaded movies in a period
"""
highest = crud.get_highest_downloads(db=db, filter_=filter_)
print(crud.get_highest_downloads.cache_info(), print(db.__hash__(), filter_.__hash__()))
print(crud.get_highest_downloads.cache_info(),
print(db.__hash__(), filter_.__hash__()))
return highest


@router.post("/movie/", response_model=schemas.Movie)
@router.post("/movie/", response_model=schemas.Movie, tags=["movie"])
def get_movie_by_schema(movie: schemas.MovieBase, db: Session = Depends(get_db)):
"""
Return full schema of a movie
"""
return crud.get_movie_by_schema(db=db, movie=movie).first()


@router.post("/rating/", response_model=List[schemas.Rating])
@router.post("/rating/", response_model=List[schemas.Rating], tags=["movie"])
def get_ratings(movie: schemas.MovieRating, db: Session = Depends(get_db)):
"""
Retrieves all the rating objects of a movie
"""
return crud.get_movie_ratings(db=db, movie=movie)


@router.get("/list/", response_model=List[schemas.MovieReferral])
@router.get("/list/", response_model=List[schemas.MovieReferral], tags=["movie"])
def list_movies(engine: str = "netnaija", page: int = 1, num: int = 20, db: HashableSession = Depends(get_db)):
"""
Lists movies from an engine
Expand All @@ -118,14 +119,15 @@ def list_movies(engine: str = "netnaija", page: int = 1, num: int = 20, db: Hash
})
movies = []
with contextlib.suppress(utils.GophieHostException):
movies = utils.get_movies_from_remote(f"{settings.gophie_host}/list", params, engine, db)
movies = utils.get_movies_from_remote(
f"{settings.gophie_host}/list", params, engine, db)
if not movies:
movies = crud.list_movies(db=db, engine=engine, page=page, num=num)
logging.info(utils.get_movies_from_remote.cache_info())
return movies


@router.get("/search/", response_model=List[schemas.MovieReferral])
@router.get("/search/", response_model=List[schemas.MovieReferral], tags=["movie"])
def search_movies(engine: str = "netnaija", query: str = "hello", page: int = 1, num: int = 20, db: HashableSession = Depends(get_db)):
"""
Searches movies from an engine using partial ratio
Expand All @@ -140,28 +142,10 @@ def search_movies(engine: str = "netnaija", query: str = "hello", page: int = 1,
})
movies = []
with contextlib.suppress(utils.GophieHostException):
movies = utils.get_movies_from_remote(f"{settings.gophie_host}/search", params, engine, db)
movies = utils.get_movies_from_remote(
f"{settings.gophie_host}/search", params, engine, db)
if not movies:
movies = crud.search_movies(db=db, engine=engine, query=query, page=page, num=num)
movies = crud.search_movies(
db=db, engine=engine, query=query, page=page, num=num)
logging.info(utils.get_movies_from_remote.cache_info())
return movies

@router.get("/music/search/", response_model=List[schemas.Music])
def search_music(engine: str = "freemp3cloud", query: str = "Mirrors Justin Timberlake", db: HashableSession = Depends(get_db)):
"""
Searches music from an engine using partial ratio

engine: the engine to list data from
query: the search term urlencoded
"""
params = HashableParams({
"query": query,
"engine": engine,
})
music = []
with contextlib.suppress(utils.MythraHostException):
music = utils.get_music_from_remote(f"{settings.mythra_host}/search", params, engine, db)
if not music:
movies = crud.search_music(db=db, engine=engine, query=query)
logging.info(utils.get_music_from_remote.cache_info())
return music
Loading