Skip to content

Commit

Permalink
Add company to migration and new domain jobs including model
Browse files Browse the repository at this point in the history
  • Loading branch information
shri committed Jul 30, 2024
1 parent 8c799ea commit 835be76
Show file tree
Hide file tree
Showing 13 changed files with 496 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# type: ignore
"""Make tenant_id mandatory in user and add models company and job post
Revision ID: a05c476c0ae9
Revises: b6ffcfae703a
Create Date: 2024-07-30 12:21:24.309890+00:00
"""
from __future__ import annotations

import warnings
from typing import TYPE_CHECKING

import sqlalchemy as sa
from alembic import op
from advanced_alchemy.types import EncryptedString, EncryptedText, GUID, ORA_JSONB, DateTimeUTC
from sqlalchemy import Text # noqa: F401
from sqlalchemy.dialects import postgresql

from app.db.models.custom_types import LocationType, FundingType
if TYPE_CHECKING:
from collections.abc import Sequence

__all__ = ["downgrade", "upgrade", "schema_upgrades", "schema_downgrades", "data_upgrades", "data_downgrades"]

sa.GUID = GUID
sa.DateTimeUTC = DateTimeUTC
sa.ORA_JSONB = ORA_JSONB
sa.EncryptedString = EncryptedString
sa.EncryptedText = EncryptedText

# revision identifiers, used by Alembic.
revision = 'a05c476c0ae9'
down_revision = 'b6ffcfae703a'
branch_labels = None
depends_on = None


def upgrade() -> None:
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=UserWarning)
with op.get_context().autocommit_block():
schema_upgrades()
data_upgrades()

def downgrade() -> None:
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=UserWarning)
with op.get_context().autocommit_block():
data_downgrades()
schema_downgrades()

def schema_upgrades() -> None:
"""schema upgrade migrations go here."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('company',
sa.Column('id', sa.GUID(length=16), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.Column('description', sa.String(length=500), nullable=True),
sa.Column('type', sa.String(), nullable=True),
sa.Column('industry', sa.String(), nullable=True),
sa.Column('headcount', sa.Integer(), nullable=True),
sa.Column('founded_year', sa.Integer(), nullable=True),
sa.Column('url', sa.String(length=2083), nullable=True),
sa.Column('profile_pic_url', sa.String(), nullable=True),
sa.Column('linkedin_profile_url', sa.String(), nullable=True),
sa.Column('hq_location', LocationType(astext_type=Text()), nullable=True),
sa.Column('last_funding', FundingType(astext_type=Text()), nullable=True),
sa.Column('slug', sa.String(length=100), nullable=False),
sa.Column('sa_orm_sentinel', sa.Integer(), nullable=True),
sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
sa.PrimaryKeyConstraint('id', name=op.f('pk_company')),
sa.UniqueConstraint('slug', name='uq_company_slug')
)
with op.batch_alter_table('company', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_company_headcount'), ['headcount'], unique=False)
batch_op.create_index(batch_op.f('ix_company_industry'), ['industry'], unique=False)
batch_op.create_index(batch_op.f('ix_company_name'), ['name'], unique=False)
batch_op.create_index('ix_company_slug_unique', ['slug'], unique=True)
batch_op.create_index(batch_op.f('ix_company_type'), ['type'], unique=False)

op.create_table('job_post',
sa.Column('id', sa.GUID(length=16), nullable=False),
sa.Column('title', sa.String(), nullable=False),
sa.Column('body', sa.Text(), nullable=True),
sa.Column('location', LocationType(astext_type=Text()), nullable=True),
sa.Column('seniority_level', sa.String(), nullable=True),
sa.Column('employment_type', sa.String(), nullable=True),
sa.Column('job_functions', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('total_applicants', sa.Integer(), nullable=True),
sa.Column('url', sa.String(length=2083), nullable=True),
sa.Column('apply_url', sa.String(length=2083), nullable=True),
sa.Column('external_id', sa.String(), nullable=True),
sa.Column('company_id', sa.GUID(length=16), nullable=True),
sa.Column('sa_orm_sentinel', sa.Integer(), nullable=True),
sa.Column('created_at', sa.DateTimeUTC(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTimeUTC(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['company_id'], ['company.id'], name=op.f('fk_job_post_company_id_company')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_job_post'))
)
with op.batch_alter_table('job_post', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_job_post_title'), ['title'], unique=False)

with op.batch_alter_table('user_account', schema=None) as batch_op:
batch_op.alter_column('tenant_id',
existing_type=sa.UUID(),
nullable=False)

# ### end Alembic commands ###

def schema_downgrades() -> None:
"""schema downgrade migrations go here."""
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('user_account', schema=None) as batch_op:
batch_op.alter_column('tenant_id',
existing_type=sa.UUID(),
nullable=True)

with op.batch_alter_table('job_post', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_job_post_title'))

op.drop_table('job_post')
with op.batch_alter_table('company', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_company_type'))
batch_op.drop_index('ix_company_slug_unique')
batch_op.drop_index(batch_op.f('ix_company_name'))
batch_op.drop_index(batch_op.f('ix_company_industry'))
batch_op.drop_index(batch_op.f('ix_company_headcount'))

op.drop_table('company')
# ### end Alembic commands ###

def data_upgrades() -> None:
"""Add any optional data upgrade migrations here!"""

def data_downgrades() -> None:
"""Add any optional data downgrade migrations here!"""
5 changes: 2 additions & 3 deletions src/app/db/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .user_role import UserRole
from .tenant import Tenant
from .company import Company
from .custom_types import Funding, Location
from .job_post import JobPost

__all__ = (
"User",
Expand All @@ -25,6 +25,5 @@
"TeamRoles",
"Tenant",
"Company",
"Funding",
"Location",
"JobPost"
)
5 changes: 1 addition & 4 deletions src/app/db/models/company.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
from app.lib.schema import Location, Funding
from .custom_types import LocationType, FundingType

if TYPE_CHECKING:
from .user import User


class Company(UUIDAuditBase, SlugKey):
"""A company or an organization."""
Expand All @@ -24,7 +21,7 @@ class Company(UUIDAuditBase, SlugKey):
industry: Mapped[str | None] = mapped_column(nullable=True, default=None, index=True)
headcount: Mapped[int | None] = mapped_column(nullable=True, default=None, index=True)
founded_year: Mapped[int | None] = mapped_column(nullable=True, default=None)
url: Mapped[str | None] = mapped_column(nullable=True, default=None)
url: Mapped[str | None] = mapped_column(String(length=2083), nullable=True, default=None)
profile_pic_url: Mapped[str | None] = mapped_column(nullable=True, default=None)
linkedin_profile_url: Mapped[str | None] = mapped_column(nullable=True, default=None)
hq_location: Mapped[Location | None] = mapped_column(LocationType, nullable=True, default=None)
Expand Down
37 changes: 37 additions & 0 deletions src/app/db/models/job_post.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from uuid import UUID
from advanced_alchemy.base import SlugKey, UUIDAuditBase
from sqlalchemy import String, Text, ForeignKey
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.lib.schema import Location, Funding
from .custom_types import LocationType, FundingType
from .company import Company


class JobPost(UUIDAuditBase):
"""A job post."""

__tablename__ = "job_post"
__pii_columns__ = {}
title: Mapped[str] = mapped_column(nullable=False, index=True)
body: Mapped[str | None] = mapped_column(Text, nullable=True, default=None)
location: Mapped[Location | None] = mapped_column(LocationType, nullable=True, default=None)
seniority_level: Mapped[str | None] = mapped_column(nullable=True, default=None)
employment_type: Mapped[str | None] = mapped_column(nullable=True, default=None)
job_functions: Mapped[list[str] | None] = mapped_column(JSONB, nullable=True, default=None)
total_applicants: Mapped[int | None] = mapped_column(nullable=True, default=None)
url: Mapped[str | None] = mapped_column(String(length=2083), nullable=True, default=None)
apply_url: Mapped[str | None] = mapped_column(String(length=2083), nullable=True, default=None)
external_id: Mapped[str | None] = mapped_column(nullable=True, default=None)
company_id: Mapped[UUID] = mapped_column(ForeignKey("company.id"), nullable=True)
# -----------
# ORM Relationships
# ------------
company: Mapped[Company] = relationship(
lazy="joined",
)
4 changes: 4 additions & 0 deletions src/app/domain/jobs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Job Application Module."""
from . import controllers, dependencies, schemas, services

__all__ = ["controllers", "services", "schemas", "dependencies"]
3 changes: 3 additions & 0 deletions src/app/domain/jobs/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .job_posts import JobPostController

__all__ = ["JobPostController"]
130 changes: 130 additions & 0 deletions src/app/domain/jobs/controllers/job_posts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""Job Post Controllers."""

from __future__ import annotations

from typing import TYPE_CHECKING, Annotated

from litestar import Controller, delete, get, patch, post
from litestar.di import Provide

from app.config import constants
from app.db.models import User as UserModel
from app.domain.accounts.guards import requires_active_user
from app.domain.jobs import urls
from app.domain.jobs.dependencies import provide_job_posts_service
from app.domain.jobs.schemas import JobPost, JobPostCreate, JobPostUpdate
from app.domain.jobs.services import JobPostService

if TYPE_CHECKING:
from uuid import UUID

from advanced_alchemy.service.pagination import OffsetPagination
from litestar.params import Dependency, Parameter

from app.lib.dependencies import FilterTypes


class JobPostController(Controller):
"""JobPost operations."""

tags = ["Job Posts"]
dependencies = {"job_posts_service": Provide(provide_job_posts_service)}
guards = [requires_active_user]
signature_namespace = {
"JobPostService": JobPostService,
}
dto = None
return_dto = None

@get(
operation_id="ListJobPosts",
name="jobs:list-post",
summary="List Job Posts",
path=urls.JOBS_LIST,
)
async def list_job_posts(
self,
job_posts_service: JobPostService,
filters: Annotated[list[FilterTypes], Dependency(skip_validation=True)],
) -> OffsetPagination[JobPost]:
"""List job_posts that your account can access.."""
results, total = await job_posts_service.get_job_posts(*filters)
return job_posts_service.to_schema(data=results, total=total, schema_type=JobPost, filters=filters)

@post(
operation_id="CreateJobPost",
name="jobs:create-post",
summary="Create a new job post.",
path=urls.JOBS_CREATE,
)
async def create_job_post(
self,
job_posts_service: JobPostService,
data: JobPostCreate,
) -> JobPostCreate:
"""Create a new job post."""
obj = data.to_dict()
db_obj = await job_posts_service.create(obj)
return job_posts_service.to_schema(schema_type=JobPost, data=db_obj)

@get(
operation_id="GetJobPost",
name="jobs:get-post",
summary="Retrieve the details of a job post.",
path=urls.JOBS_DETAIL,
)
async def get_job_post(
self,
job_posts_service: JobPostService,
job_post_id: Annotated[
UUID,
Parameter(
title="JobPost ID",
description="The job_post to retrieve.",
),
],
) -> JobPost:
"""Get details about a job post."""
db_obj = await job_posts_service.get(job_post_id)
return job_posts_service.to_schema(schema_type=JobPost, data=db_obj)

@patch(
operation_id="UpdateJobPost",
name="jobs:update-post",
path=urls.JOBS_UPDATE,
)
async def update_job_post(
self,
data: JobPostUpdate,
job_posts_service: JobPostService,
job_post_id: Annotated[
UUID,
Parameter(
title="JobPost ID",
description="The job_post to update.",
),
],
) -> JobPost:
"""Update a job post."""
db_obj = await job_posts_service.update(
item_id=job_post_id,
data=data.to_dict(),
)
return job_posts_service.to_schema(schema_type=JobPost, data=db_obj)

@delete(
operation_id="DeleteJobPost",
name="jobs:delete-post",
summary="Remove JobPost",
path=urls.JOBS_DELETE,
)
async def delete_job_post(
self,
job_posts_service: JobPostService,
job_post_id: Annotated[
UUID,
Parameter(title="JobPost ID", description="The job_post to delete."),
],
) -> None:
"""Delete a job_post."""
_ = await job_posts_service.delete(job_post_id)
27 changes: 27 additions & 0 deletions src/app/domain/jobs/dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Job Post Controllers."""

from __future__ import annotations

from typing import TYPE_CHECKING

from sqlalchemy.orm import joinedload, noload, selectinload

from app.db.models import JobPost
from app.domain.jobs.services import JobPostService

__all__ = ("provide_job_posts_service", )


if TYPE_CHECKING:
from collections.abc import AsyncGenerator

from sqlalchemy.ext.asyncio import AsyncSession


async def provide_job_posts_service(db_session: AsyncSession) -> AsyncGenerator[JobPostService, None]:
"""Construct repository and service objects for the request."""
async with JobPostService.new(
session=db_session,
load=[],
) as service:
yield service
Loading

0 comments on commit 835be76

Please sign in to comment.