Skip to content

Commit

Permalink
Added basic endpoints.
Browse files Browse the repository at this point in the history
  • Loading branch information
dsuhoi committed Jul 20, 2023
1 parent dd5d6d7 commit 8c8202f
Show file tree
Hide file tree
Showing 11 changed files with 398 additions and 49 deletions.
2 changes: 1 addition & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ RUN pip3 install poetry
COPY *.py pytest.ini poetry.lock pyproject.toml ./
COPY core/ ./core/
COPY routers/ ./routers/
# COPY tasks/ ./tasks/
COPY data/ ./data/
COPY tests/ ./tests/

RUN poetry config virtualenvs.create false \
Expand Down
2 changes: 2 additions & 0 deletions backend/core/init_data_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import geoalchemy2 as gsa
# import geopandas
import shapely
import sqlalchemy as sa

from core.auth import get_password_hash
from core.model_utils import get_count

from .database import async_session, init_db
from .model_utils import get_city_and_districts
from .models import City, City_property, District, District_property, User

DATA_DIR = pathlib.Path(__file__).parent.parent.joinpath("data")
Expand Down
159 changes: 150 additions & 9 deletions backend/core/model_utils.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,172 @@
from sqlalchemy import select
import sqlalchemy as sa
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.sql import func

from .database import Base
from .models import User
from .models import City, City_property, District, District_property, User


async def get_user_by_id(id: int, db: AsyncSession) -> User:
query = select(User).where(User.id == id)
query = sa.select(User).where(User.id == id)
return (await db.scalars(query)).first()


async def get_user_by_username(username: str, db: AsyncSession) -> User:
query = select(User).where(User.username == username)
query = sa.select(User).where(User.username == username)
return (await db.scalars(query)).first()


async def get_count(db: AsyncSession, model: Base):
return (await db.scalars(func.count(model.id))).first()
return (await db.scalars(sa.sql.func.count(model.id))).first()


async def get_by_id(db: AsyncSession, model: Base, id: int):
return (await db.scalars(select(model).where(model.id == id))).first()
return (await db.scalars(sa.select(model).where(model.id == id))).first()


async def get_all(db: AsyncSession, model: Base):
return (await db.scalars(select(model))).all()
async def get_all(db: AsyncSession, model: Base, limit: int = None):
if limit:
return (await db.scalars(sa.select(model).limit(limit))).all()
return (await db.scalars(sa.select(model))).all()


def exec_query_geojson(func_query):
async def wrapper(db: AsyncSession, *args, **kwargs):
query = func_query(*args, **kwargs)
data = (await db.execute(query)).first()[0]
if data["features"]:
return data
return {}

wrapper.__name__ = func_query.__name__
return wrapper


@exec_query_geojson
def get_blocks_geojson():
return


@exec_query_geojson
def get_districts_geojson():
return (
sa.select(
sa.func.json_build_object(
"type",
"FeatuerCollection",
"features",
sa.func.json_agg(
sa.func.json_build_object(
"type",
"Feature",
"properties",
sa.func.json_build_object(
"title",
District.title,
"population",
District_property.population,
"area",
District_property.area,
),
"geometry",
sa.func.ST_AsGeoJSON(District.geom).cast(sa.JSON),
)
),
)
)
.select_from(District)
.outerjoin(District_property, District.id == District_property.district_id)
)


@exec_query_geojson
def get_cities_geojson():
return (
sa.select(
sa.func.json_build_object(
"type",
"FeatuerCollection",
"features",
sa.func.json_agg(
sa.func.json_build_object(
"type",
"Feature",
"properties",
sa.func.json_build_object(
"title",
City.title,
"population",
City_property.population,
"area",
City_property.area,
),
"geometry",
sa.func.ST_AsGeoJSON(City.geom).cast(sa.JSON),
)
),
)
)
.select_from(City)
.outerjoin(City_property, City.id == City_property.city_id)
)


async def get_city_and_districts(db: AsyncSession, city_title: str):
query = (
sa.select(
sa.func.json_build_object(
"type",
"Feature",
"properties",
sa.func.json_build_object(
"title",
City.title,
"population",
City_property.population,
"area",
City_property.area,
),
"geometry",
sa.func.ST_AsGeoJSON(City.geom).cast(sa.JSON),
),
sa.func.json_build_object(
"type",
"FeatureCollection",
"features",
sa.func.json_agg(
sa.func.json_build_object(
"type",
"Feature",
"properties",
sa.func.json_build_object(
"title",
District.title,
"population",
District_property.population,
"area",
District_property.area,
),
"geometry",
sa.func.ST_AsGeoJSON(District.geom).cast(sa.JSON),
)
),
),
)
.select_from(City)
.outerjoin(District, City.id == District.city_id)
.outerjoin(District_property, District.id == District_property.district_id)
.outerjoin(City_property, City.id == City_property.city_id)
.where(City.title == city_title)
.group_by(
City.title,
City.geom,
City_property.population,
City_property.area,
)
)

if data := (await db.execute(query)).first():
return {"city": data[0], "districts": data[1]}
return {}


async def create_model(db: AsyncSession, model: Base):
Expand Down
21 changes: 18 additions & 3 deletions backend/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,14 @@ class District(Base):
gsa.Geometry(geometry_type="MULTIPOLYGON", srid=4326), nullable=False
)

@property
def geometry(self):
shape = gsa.shape.to_shape(self.geom)
return shape.__geo_interface__

city = sa.orm.relationship("City", back_populates="districts", lazy="selectin")
properties = sa.orm.relationship(
"District_property", back_populates="district", uselist=False
"District_property", back_populates="district", uselist=False, lazy="selectin"
)


Expand All @@ -106,9 +111,14 @@ class Block(Base):
gsa.Geometry(geometry_type="MULTIPOLYGON", srid=4326), nullable=False
)

@property
def geometry(self):
shape = gsa.shape.to_shape(self.geom)
return shape.__geo_interface__

city = sa.orm.relationship("City", back_populates="blocks", lazy="selectin")
properties = sa.orm.relationship(
"Block_property", back_populates="block", uselist=False
"Block_property", back_populates="block", uselist=False, lazy="selectin"
)


Expand All @@ -122,8 +132,13 @@ class City(Base):
gsa.Geometry(geometry_type="MULTIPOLYGON", srid=4326), nullable=False
)

@property
def geometry(self):
shape = gsa.shape.to_shape(self.geom)
return shape.__geo_interface__

districts = sa.orm.relationship("District", back_populates="city", lazy="selectin")
blocks = sa.orm.relationship("Block", back_populates="city", lazy="selectin")
properties = sa.orm.relationship(
"City_property", back_populates="city", uselist=False
"City_property", back_populates="city", uselist=False, lazy="selectin"
)
76 changes: 76 additions & 0 deletions backend/core/schemas.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime
from typing import Annotated

import shapely
from pydantic import BaseModel, Field

Lat = Annotated[float, Field(example=10.345, description="Широта")]
Expand Down Expand Up @@ -51,3 +52,78 @@ class UserPatchResponse(BaseModel):
class LoginResponse(BaseModel):
access_token: str = Field(description="Токен аутентификации")
token_type: str = Field(description="Тип токена")


class Properties(BaseModel):
population: int = Field(ge=0, description="Население")
area: float = Field(gt=0, description="Площадь(км^2)")

class Config:
from_attributes = True


class Geometry(BaseModel):
type: str
coordinates: list


class CityInfo(BaseModel):
title: str = Field(description="Название города")

class Config:
from_attributes = True


class DistrictList(BaseModel):
id: int
title: str = Field(description="Название района")

class Config:
from_attributes = True


class District(BaseModel):
title: str = Field(description="Название района")
properties: Properties
city: CityInfo
geometry: Geometry

class Config:
from_attributes = True


class BlockList(BaseModel):
id: int
title: str = Field(description="Название квартала")

class Config:
from_attributes = True


class Block(BaseModel):
title: str = Field(description="Название района")
properties: Properties
city: CityInfo
geometry: Geometry

class Config:
from_attributes = True


class CityList(BaseModel):
id: int
title: str = Field(description="Название города")

class Config:
from_attributes = True


class City(BaseModel):
title: str = Field(description="Название города")
properties: Properties
districts: list[DistrictList] | None
blocks: list[BlockList] | None
geometry: Geometry

class Config:
from_attributes = True
4 changes: 2 additions & 2 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from core.database import init_db
from core.init_data_db import init_data
from routers import users
from routers import geo, users

app = FastAPI(
title="Аналитика гетто",
Expand All @@ -23,7 +23,7 @@
)

app.include_router(users.router)
# app.include_router(geo.router)
app.include_router(geo.router)
# app.include_router(gql.router, prefix="/graphql")


Expand Down
Loading

0 comments on commit 8c8202f

Please sign in to comment.