Skip to content

Commit

Permalink
[feat] 연령 템플릿 데이터 실제 데이터 반환
Browse files Browse the repository at this point in the history
  • Loading branch information
pingpingy1 committed Nov 23, 2023
1 parent df60a62 commit a6a4123
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 92 deletions.
4 changes: 2 additions & 2 deletions model/MongoDB.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ def __init__(self):
self.client = None
self.council_db = None
self.district_db = None
self.age_hist_db = None
self.stats_db = None

def connect(self):
self.client = AsyncIOMotorClient(os.getenv("MONGO_CONNECTION_URI"))
self.council_db = AsyncIOMotorDatabase(self.client, "council")
self.district_db = AsyncIOMotorDatabase(self.client, "district")
self.age_hist_db = AsyncIOMotorDatabase(self.client, "age_hist")
self.stats_db = AsyncIOMotorDatabase(self.client, "stats")

def close(self):
self.client.close()
Expand Down
2 changes: 1 addition & 1 deletion model/ScrapResult.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class GenderTemplateData(BaseModel):
class AgeTemplateData(BaseModel):
class AgeRankingParagraphData(BaseModel):
class AgeRankingAllIndices(BaseModel):
metroId: int
localId: int
rank: int
ageDiversityIndex: float

Expand Down
55 changes: 21 additions & 34 deletions routers/ageHist.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,16 @@ async def getMetroAgeHistData(
}
)

match ageHistType:
case AgeHistDataTypes.elected:
collection_name = f"지선-당선_{year}_1level_{method}"
case AgeHistDataTypes.candidate:
collection_name = f"지선-후보_{year}_1level_{method}"

if collection_name not in await MongoDB.client.age_hist_db.list_collection_names():
return BasicResponse.ErrorResponse.model_validate(
{
"error": "CollectionNotExistError",
"code": BasicResponse.COLLECTION_NOT_EXIST_ERR,
"message": f"No collection with name f{collection_name}. Perhaps the year is wrong?",
}
)

histogram = await MongoDB.client.age_hist_db[collection_name].find_one(
{"metroId": metroId}
histogram = await MongoDB.client.stats_db["age_hist"].find_one(
{
"level": 1,
"councilorType": (
"elected" if ageHistType == AgeHistDataTypes.elected else "candidate"
),
"year": year,
"method": method,
"metroId": metroId,
}
)

return MetroAgeHistData.model_validate(
Expand Down Expand Up @@ -70,23 +63,17 @@ async def getLocalAgeHistData(
}
)

match ageHistType:
case AgeHistDataTypes.elected:
collection_name = f"지선-당선_{year}_2level_{method}"
case AgeHistDataTypes.candidate:
collection_name = f"지선-후보_{year}_2level_{method}"

if collection_name not in await MongoDB.client.age_hist_db.list_collection_names():
return BasicResponse.ErrorResponse.model_validate(
{
"error": "CollectionNotExistError",
"code": BasicResponse.COLLECTION_NOT_EXIST_ERR,
"message": f"No collection with name f{collection_name}. Perhaps the year is wrong?",
}
)

histogram = await MongoDB.client.age_hist_db[collection_name].find_one(
{"metroId": metroId, "localId": localId}
histogram = await MongoDB.client.stats_db["age_hist"].find_one(
{
"level": 2,
"councilorType": (
"elected" if ageHistType == AgeHistDataTypes.elected else "candidate"
),
"year": year,
"method": method,
"metroId": metroId,
"localId": localId,
}
)

return MetroAgeHistData.model_validate(
Expand Down
191 changes: 137 additions & 54 deletions routers/scrapResult.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
from typing import TypeVar
from fastapi import APIRouter
from model import BasicResponse, MongoDB, ScrapResult
from model.BasicResponse import ErrorResponse, REGION_CODE_ERR
from model.MongoDB import client
from model.ScrapResult import (
GenderTemplateData,
GenderChartDataPoint,
AgeTemplateData,
AgeChartDataPoint,
PartyTemplateData,
PartyChartDataPoint,
FactorType,
ChartData,
)
from utils import diversity
from typing import TypeVar


router = APIRouter(prefix="/localCouncil", tags=["localCouncil"])
Expand All @@ -11,45 +22,122 @@

@router.get("/template-data/{metroId}/{localId}")
async def getLocalTemplateData(
metroId: int, localId: int, factor: ScrapResult.FactorType
) -> BasicResponse.ErrorResponse | ScrapResult.GenderTemplateData | ScrapResult.AgeTemplateData | ScrapResult.PartyTemplateData:
metroId: int, localId: int, factor: FactorType
) -> ErrorResponse | GenderTemplateData | AgeTemplateData | PartyTemplateData:
if (
await MongoDB.client.district_db["local_district"].find_one(
await client.district_db["local_district"].find_one(
{"localId": localId, "metroId": metroId}
)
is None
):
return BasicResponse.ErrorResponse.model_validate(
return ErrorResponse.model_validate(
{
"error": "RegionCodeError",
"code": BasicResponse.REGION_CODE_ERR,
"code": REGION_CODE_ERR,
"message": f"No local district with metroId {metroId} and localId {localId}.",
}
)

councilors = MongoDB.client.council_db["local_councilor"].find({"localId": localId})
local_stat = await client.stats_db["diversity_index"].find_one({"localId": localId})

match factor:
case ScrapResult.FactorType.gender:
gender_list = [councilor["gender"] async for councilor in councilors]
gender_diversity_index = diversity.gini_simpson(gender_list)
return ScrapResult.GenderTemplateData.model_validate(
{"genderDiversityIndex": gender_diversity_index}
case FactorType.gender:
return GenderTemplateData.model_validate(
{"genderDiversityIndex": local_stat["genderDiversityIndex"]}
)

case ScrapResult.FactorType.age:
age_list = [councilor["age"] async for councilor in councilors]
age_diversity_index = diversity.gini_simpson(age_list, stair=AGE_STAIR)
return ScrapResult.AgeTemplateData.model_validate(
case FactorType.age:
# ============================
# rankingParagraph
# ============================
age_diversity_index = local_stat["ageDiversityIndex"]

localIds_of_same_metroId = [
doc["localId"]
async for doc in client.district_db["local_district"].find(
{"metroId": metroId}
)
]
all_indices = (
await client.stats_db["diversity_index"]
.find({"localId": {"$in": localIds_of_same_metroId}})
.to_list(500)
)
all_indices.sort(key=lambda x: x["ageDiversityRank"])

# ============================
# ageHistogramParagraph
# ============================
age_stat_elected = (
await client.stats_db["age_stat"]
.aggregate(
[
{
"$match": {
"level": 2,
"councilorType": "elected",
"metroId": metroId,
"localId": localId,
}
},
{"$sort": {"year": -1}},
{"$limit": 1},
]
)
.to_list(500)
)[0]
most_recent_year = age_stat_elected["year"]
age_stat_candidate = await client.stats_db["age_stat"].find_one(
{
"level": 2,
"councilorType": "candidate",
"metroId": metroId,
"localId": localId,
"year": most_recent_year,
}
)

divArea_id = (
await client.stats_db["diversity_index"].find_one(
{"ageDiversityRank": 1}
)
)["localId"]
divArea = await client.stats_db["age_stat"].find_one(
{
"level": 2,
"councilorType": "elected",
"localId": divArea_id,
"year": most_recent_year,
}
)

uniArea_id = (
await client.stats_db["diversity_index"].find_one(
{"ageDiversityRank": 226}
)
)["localId"]
uniArea = await client.stats_db["age_stat"].find_one(
{
"level": 2,
"councilorType": "elected",
"localId": uniArea_id,
"year": most_recent_year,
}
)

return AgeTemplateData.model_validate(
{
"metroId": metroId,
"localId": localId,
"rankingParagraph": {
"ageDiversityIndex": age_diversity_index,
"allIndices": [
{"metroId": 3, "rank": 2, "ageDiversityIndex": 0.9},
{"metroId": 14, "rank": 7, "ageDiversityIndex": 0.4},
{"metroId": 15, "rank": 18, "ageDiversityIndex": 0.2},
{
"localId": doc["localId"],
"rank": idx + 1,
"ageDiversityIndex": doc["ageDiversityIndex"],
}
for idx, doc in enumerate(all_indices)
],
},
"indexHistoryParagraph": {
Expand All @@ -76,68 +164,65 @@ async def getLocalTemplateData(
],
},
"ageHistogramParagraph": {
"year": 2022,
"candidateCount": 80,
"electedCount": 16,
"firstQuintile": 66,
"lastQuintile": 29,
"year": most_recent_year,
"candidateCount": age_stat_candidate["data"][0]["population"],
"electedCount": age_stat_elected["data"][0]["population"],
"firstQuintile": age_stat_elected["data"][0]["firstquintile"],
"lastQuintile": age_stat_elected["data"][0]["lastquintile"],
"divArea": {
"localId": 172,
"firstQuintile": 43,
"lastQuintile": 21,
"localId": divArea_id,
"firstQuintile": divArea["data"][0]["firstquintile"],
"lastQuintile": divArea["data"][0]["lastquintile"],
},
"uniArea": {
"localId": 63,
"firstQuintile": 84,
"lastQuintile": 56,
"localId": uniArea_id,
"firstQuintile": uniArea["data"][0]["firstquintile"],
"lastQuintile": uniArea["data"][0]["lastquintile"],
},
},
}
)

case ScrapResult.FactorType.party:
party_list = [councilor["jdName"] async for councilor in councilors]
party_diversity_index = diversity.gini_simpson(party_list)
return ScrapResult.PartyTemplateData.model_validate(
case FactorType.party:
party_diversity_index = local_stat["partyDiversityIndex"]
return PartyTemplateData.model_validate(
{"partyDiversityIndex": party_diversity_index}
)


T = TypeVar(
"T",
ScrapResult.GenderChartDataPoint,
ScrapResult.AgeChartDataPoint,
ScrapResult.PartyChartDataPoint,
GenderChartDataPoint,
AgeChartDataPoint,
PartyChartDataPoint,
)


@router.get("/chart-data/{metroId}/{localId}")
async def getLocalChartData(
metroId: int, localId: int, factor: ScrapResult.FactorType
) -> BasicResponse.ErrorResponse | ScrapResult.ChartData[T]:
metroId: int, localId: int, factor: FactorType
) -> ErrorResponse | ChartData[T]:
if (
await MongoDB.client.district_db["local_district"].find_one(
await client.district_db["local_district"].find_one(
{"localId": localId, "metroId": metroId}
)
is None
):
return BasicResponse.ErrorResponse.model_validate(
return ErrorResponse.model_validate(
{
"error": "RegionCodeError",
"code": BasicResponse.REGION_CODE_ERR,
"code": REGION_CODE_ERR,
"message": f"No local district with metroId {metroId} and localId {localId}.",
}
)

councilors = MongoDB.client.council_db["local_councilor"].find({"localId": localId})
councilors = client.council_db["local_councilor"].find({"localId": localId})

match factor:
case ScrapResult.FactorType.gender:
case FactorType.gender:
gender_list = [councilor["gender"] async for councilor in councilors]
gender_count = diversity.count(gender_list)
return ScrapResult.ChartData[
ScrapResult.GenderChartDataPoint
].model_validate(
return ChartData[GenderChartDataPoint].model_validate(
{
"data": [
{"gender": gender, "count": gender_count[gender]}
Expand All @@ -146,10 +231,10 @@ async def getLocalChartData(
}
)

case ScrapResult.FactorType.age:
case FactorType.age:
age_list = [councilor["age"] async for councilor in councilors]
age_count = diversity.count(age_list, stair=AGE_STAIR)
return ScrapResult.ChartData[ScrapResult.AgeChartDataPoint].model_validate(
return ChartData[AgeChartDataPoint].model_validate(
{
"data": [
{
Expand All @@ -162,12 +247,10 @@ async def getLocalChartData(
}
)

case ScrapResult.FactorType.party:
case FactorType.party:
party_list = [councilor["jdName"] async for councilor in councilors]
party_count = diversity.count(party_list)
return ScrapResult.ChartData[
ScrapResult.PartyChartDataPoint
].model_validate(
return ChartData[PartyChartDataPoint].model_validate(
{
"data": [
{"party": party, "count": party_count[party]}
Expand Down
2 changes: 1 addition & 1 deletion utils/diversity.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def gini_simpson(data, stair=0, opts=True):
"""
counts = count(data, stair)
total = sum(counts.values())
gs_idx = 1 - sum((n / total) ** 2 for n in counts.values())
gs_idx = 1 - sum((n / total) * ((n - 1) / (total - 1)) for n in counts.values())

if opts:
num_cats = len([c for c in counts.values() if c > 0])
Expand Down

0 comments on commit a6a4123

Please sign in to comment.