Skip to content

Commit

Permalink
Merge pull request #831 from hngprojects/dev
Browse files Browse the repository at this point in the history
chore: merging 'dev' into staging
  • Loading branch information
Goketech authored Aug 8, 2024
2 parents 3e1ddb0 + 48edbbe commit 83c44d2
Show file tree
Hide file tree
Showing 53 changed files with 1,919 additions and 1,065 deletions.
26 changes: 26 additions & 0 deletions alembic/versions/299ab5d402fc_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""empty message
Revision ID: 299ab5d402fc
Revises: 224b03e9169c, 44f7da26ee88
Create Date: 2024-08-08 18:59:46.314362
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = '299ab5d402fc'
down_revision: Union[str, None] = ('224b03e9169c', '44f7da26ee88')
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
pass


def downgrade() -> None:
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Ensure id and foreign keys are of type String
Revision ID: 44f7da26ee88
Revises: b27028b0327e
Create Date: 2024-08-08 18:21:40.759920
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = '44f7da26ee88'
down_revision: Union[str, None] = 'b27028b0327e'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""Ensure id and foreign keys are of type String
Revision ID: b27028b0327e
Revises: 8caa1a06240c
Create Date: 2024-08-08 17:49:49.925575
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = 'b27028b0327e'
down_revision: Union[str, None] = '8caa1a06240c'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('permissions', sa.Column('title', sa.String(), nullable=False))
op.drop_constraint('permissions_name_key', 'permissions', type_='unique')
op.create_unique_constraint(None, 'permissions', ['title'])
op.drop_column('permissions', 'name')
op.add_column('roles', sa.Column('description', sa.Text(), nullable=True))
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('roles', 'description')
op.add_column('permissions', sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False))
op.drop_constraint(None, 'permissions', type_='unique')
op.create_unique_constraint('permissions_name_key', 'permissions', ['name'])
op.drop_column('permissions', 'title')
# ### end Alembic commands ###
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Ensure id and foreign keys are of type String
Revision ID: ff92a0037698
Revises: 299ab5d402fc
Create Date: 2024-08-08 19:03:17.115224
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = 'ff92a0037698'
down_revision: Union[str, None] = '299ab5d402fc'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('product_comments', sa.Column('user_id', sa.String(), nullable=True))
op.create_foreign_key(None, 'product_comments', 'users', ['user_id'], ['id'], ondelete='SET NULL')
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'product_comments', type_='foreignkey')
op.drop_column('product_comments', 'user_id')
# ### end Alembic commands ###
2 changes: 1 addition & 1 deletion api/v1/models/permissions/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ class Permission(BaseTableModel):
__tablename__ = 'permissions'

#id = Column(String, primary_key=True, index=True, default=lambda: str(uuid7()))
name = Column(String, unique=True, nullable=False)
title = Column(String, unique=True, nullable=False)

3 changes: 2 additions & 1 deletion api/v1/models/permissions/role.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from sqlalchemy import Column, String, Boolean
from sqlalchemy import Column, String, Boolean, Text
from api.v1.models.base_model import BaseTableModel
from uuid_extensions import uuid7

Expand All @@ -7,4 +7,5 @@ class Role(BaseTableModel):

id = Column(String, primary_key=True, index=True, default=lambda: str(uuid7()))
name = Column(String, unique=True, nullable=False)
description = Column(Text, nullable=True)
is_builtin = Column(Boolean, default=False) # True for built-in roles, False for custom roles
5 changes: 2 additions & 3 deletions api/v1/routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from api.v1.routes.auth import auth
from api.v1.routes.newsletter import newsletter
from api.v1.routes.user import user_router
from api.v1.routes.product import product
from api.v1.routes.product import product, non_organization_product
from api.v1.routes.product_comment import product_comment
from api.v1.routes.notification import notification
from api.v1.routes.testimonial import testimonial
Expand Down Expand Up @@ -43,7 +43,6 @@
from api.v1.routes.privacy import privacies
from api.v1.routes.settings import settings
from api.v1.routes.terms_and_conditions import terms_and_conditions
from api.v1.routes.statistics import statistics

api_version_one = APIRouter(prefix="/api/v1")

Expand All @@ -54,6 +53,7 @@
api_version_one.include_router(user_router)
api_version_one.include_router(profile)
api_version_one.include_router(organization)
api_version_one.include_router(non_organization_product)
api_version_one.include_router(product)
api_version_one.include_router(payment)
api_version_one.include_router(bill_plan)
Expand Down Expand Up @@ -89,4 +89,3 @@
api_version_one.include_router(team)
api_version_one.include_router(terms_and_conditions)
api_version_one.include_router(product_comment)
api_version_one.include_router(statistics)
38 changes: 30 additions & 8 deletions api/v1/routes/billing_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
from api.v1.services.user import user_service
from api.v1.schemas.plans import CreateSubscriptionPlan

bill_plan = APIRouter(prefix='/organizations', tags=['Billing-Plan'])
bill_plan = APIRouter(prefix="/organisations", tags=["Billing-Plan"])

@bill_plan.get('/{organization_id}/billing-plans', response_model=success_response)

@bill_plan.get("/{organization_id}/billing-plans", response_model=success_response)
async def retrieve_all_billing_plans(
organization_id: str,
db: Session = Depends(get_db)
organization_id: str, db: Session = Depends(get_db)
):
"""
Endpoint to get all billing plans
Expand All @@ -33,6 +33,7 @@ async def retrieve_all_billing_plans(
},
)


@bill_plan.post("/billing-plans", response_model=success_response)
async def create_new_billing_plan(
request: CreateSubscriptionPlan,
Expand All @@ -51,12 +52,13 @@ async def create_new_billing_plan(
data=jsonable_encoder(plan),
)

@bill_plan.patch('/billing-plans/{billing_plan_id}', response_model=success_response)

@bill_plan.patch("/billing-plans/{billing_plan_id}", response_model=success_response)
async def update_a_billing_plan(
billing_plan_id: str,
request: CreateSubscriptionPlan,
current_user: User = Depends(user_service.get_current_super_admin),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
):
"""
Endpoint to update a billing plan by ID
Expand All @@ -70,11 +72,12 @@ async def update_a_billing_plan(
data=jsonable_encoder(plan),
)

@bill_plan.delete('/billing-plans/{billing_plan_id}', response_model=success_response)

@bill_plan.delete("/billing-plans/{billing_plan_id}", response_model=success_response)
async def delete_a_billing_plan(
billing_plan_id: str,
current_user: User = Depends(user_service.get_current_super_admin),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
):
"""
Endpoint to delete a billing plan by ID
Expand All @@ -86,3 +89,22 @@ async def delete_a_billing_plan(
status_code=status.HTTP_200_OK,
message="Plan deleted successfully",
)


@bill_plan.get('/billing-plans/{billing_plan_id}', response_model=success_response)
async def retrieve_single_billing_plans(
billing_plan_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user)
):
"""
Endpoint to get single billing plan by id
"""

billing_plan = billing_plan_service.fetch(db, billing_plan_id)

return success_response(
status_code=status.HTTP_200_OK,
message="Plan fetched successfully",
data=jsonable_encoder(billing_plan)
)
44 changes: 40 additions & 4 deletions api/v1/routes/dashboard.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, status
from api.db.database import get_db
from sqlalchemy.orm import Session

Expand All @@ -11,11 +11,24 @@
DashboardSingleProductResponse,
DashboardProductListResponse
)
from typing import Annotated
from fastapi.security import OAuth2
from datetime import datetime, timedelta
from api.v1.services.user import oauth2_scheme
from api.v1.services.analytics import analytics_service, AnalyticsServices


dashboard = APIRouter(prefix="/dashboard", tags=['Dashboard'])


def get_current_month_date_range():
now = datetime.utcnow()
start_date = datetime(now.year, now.month, 1)
end_date = (start_date + timedelta(days=32)
).replace(day=1) - timedelta(seconds=1)
return start_date, end_date


@dashboard.get("/products/count", response_model=DashboardProductCountResponse)
async def get_products_count(
db: Session = Depends(get_db),
Expand All @@ -28,7 +41,7 @@ async def get_products_count(
message="Products count fetched successfully",
data={"count": len(products)}
)


@dashboard.get("/products", response_model=DashboardProductListResponse)
async def get_products(
Expand Down Expand Up @@ -56,7 +69,7 @@ async def get_products(
message="Products fetched successfully",
data=payment_data
)


@dashboard.get("/products/{product_id}", response_model=DashboardSingleProductResponse)
async def get_product(
Expand All @@ -79,4 +92,27 @@ async def get_product(
"archived": prod.archived,
"created_at": prod.created_at.isoformat(),
}
)
)


@dashboard.get('/statistics', status_code=status.HTTP_200_OK)
async def get_analytics_summary(
token: Annotated[str, Depends(oauth2_scheme)],
db: Annotated[Session, Depends(get_db)],
analytics_service: Annotated[AnalyticsServices, Depends()],
start_date: datetime = None,
end_date: datetime = None
):
"""
Retrieves analytics summary data for an organization or super admin.
Args:
token: access_token
db: database Session object
start_date: start date for filtering
end_date: end date for filtering
Returns:
analytics response: contains the analytics summary data
"""
if not start_date or not end_date:
start_date, end_date = get_current_month_date_range()
return analytics_service.get_analytics_summary(token=token, db=db, start_date=start_date, end_date=end_date)
31 changes: 20 additions & 11 deletions api/v1/routes/invitations.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@ async def add_user_to_organization(
logging.info(f"Processing invitation ID: {invite_id}")

return invite.InviteService.add_user_to_organization(invite_id, session)


@invites.delete("", status_code=status.HTTP_204_NO_CONTENT)
def delete_all_invite(
db: Session = Depends(get_session),
admin: User = Depends(user_service.get_current_super_admin)
):
"""Delete all invitations from the database
Args:
db (Session, optional): _description_. Defaults to Depends(get_session).
admin (User, optional): _description_. Defaults to Depends(user_service.get_current_super_admin).
"""
print("Deleting all invites")
invite.InviteService.delete_all(db)

logging.info("Deleted all invites successfully")


@invites.delete("/{invite_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_invite(
invite_id: str,
Expand All @@ -53,14 +72,4 @@ def delete_invite(
if not invite_is_deleted:
raise HTTPException(status_code=404, detail="Invalid invitation id")

logging.info(f"Deleted invite. ID: {invite_id}")

@invites.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT,response_model=None,tags=["Invitation Management"])
def delete_invitation(
id: str,
db: Session = Depends(get_session),
current_user: User = Depends(user_service.get_current_super_admin)
):
'''Delete invitation from the database'''
invite.InviteService.delete_invitation(id, db)

logging.info(f"Deleted invite. ID: {invite_id}")
Loading

0 comments on commit 83c44d2

Please sign in to comment.