Compare commits
No commits in common. "40adda078ed95bec68258a71ea54d48e2ddcd01d" and "e7e7179cd98a42f36f128a5ff0260ac97af8fc5e" have entirely different histories.
40adda078e
...
e7e7179cd9
8 changed files with 2 additions and 138 deletions
|
@ -1,8 +1,6 @@
|
|||
from datetime import UTC, datetime, timedelta
|
||||
from uuid import UUID
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.params import Depends
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session as DbSession
|
||||
|
||||
from app.database import get_db
|
||||
|
@ -12,9 +10,6 @@ from app.schemas.user import PasswordUpdate, UserUpdate, User
|
|||
from app.security.jwt_bearer import JWTBearer
|
||||
from app.services import session_service, user_service
|
||||
from app.util.errors import InvalidStateError, NotFoundError
|
||||
from app.schemas.dashboard import DashboardResponse, DashboardStats
|
||||
from app.models.transaction import Transaction as DbTransaction
|
||||
from app.schemas.transaction import TransactionStatus
|
||||
|
||||
router = APIRouter(prefix="/me", tags=["Me (v1)"])
|
||||
|
||||
|
@ -113,75 +108,4 @@ async def delete_user_session(
|
|||
)
|
||||
except NotFoundError:
|
||||
raise HTTPException(status_code=404, detail="session_not_found")
|
||||
return list()
|
||||
|
||||
@router.get(path="/dashboard", response_model=DashboardResponse)
|
||||
async def get_dashboard(
|
||||
db: DbSession = Depends(get_db),
|
||||
token: AccessToken = Depends(JWTBearer()),
|
||||
):
|
||||
"""
|
||||
Get dashboard information for the currently authenticated user.
|
||||
"""
|
||||
# Currently ongoing transaction
|
||||
stmt_current_transaction = select(DbTransaction).where(DbTransaction.user_id == token.subject).where(DbTransaction.status == TransactionStatus.ONGOING)
|
||||
current_transaction = db.execute(stmt_current_transaction).scalars().first()
|
||||
|
||||
# Common base query
|
||||
stmt_base = select(DbTransaction).where(DbTransaction.user_id == token.subject).where( DbTransaction.status == TransactionStatus.ENDED).order_by(DbTransaction.ended_at.desc())
|
||||
|
||||
# 5 most recent transactions
|
||||
stmt_transactions_recent = stmt_base.limit(5)
|
||||
recent_transactions = db.execute(stmt_transactions_recent).scalars().all()
|
||||
|
||||
# Calculate beginning of the current and previous month
|
||||
current_date = datetime.now(UTC).replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None)
|
||||
|
||||
beginning_current_month = current_date.replace(day=1)
|
||||
|
||||
beginning_previous_month = beginning_current_month.replace(
|
||||
year=(beginning_current_month.year - 1 if beginning_current_month.month == 1 else beginning_current_month.year),
|
||||
month=(12 if beginning_current_month.month == 1 else beginning_current_month.month - 1)
|
||||
)
|
||||
|
||||
# Transactions for total calculations
|
||||
stmt_transactions = stmt_base.where(DbTransaction.ended_at >= beginning_previous_month)
|
||||
transactions = db.execute(stmt_transactions).scalars().all()
|
||||
|
||||
# Current month totals
|
||||
count = 0
|
||||
energy_total = 0
|
||||
cost_total = 0
|
||||
|
||||
# Previous month totals
|
||||
count_previous = 0
|
||||
energy_total_previous = 0
|
||||
cost_total_previous = 0
|
||||
|
||||
# Calculate totals
|
||||
for trans in transactions:
|
||||
trans_energy = trans.meter_end - trans.meter_start
|
||||
trans_cost = trans_energy * trans.price
|
||||
if trans.ended_at >= beginning_current_month:
|
||||
# Current month
|
||||
energy_total += trans_energy
|
||||
cost_total += trans_cost
|
||||
count += 1
|
||||
else:
|
||||
# Previous month
|
||||
energy_total_previous += trans_energy
|
||||
cost_total_previous += trans_cost
|
||||
count_previous += 1
|
||||
|
||||
return DashboardResponse(
|
||||
stats=DashboardStats(
|
||||
transaction_count=count,
|
||||
transaction_count_previous=count_previous,
|
||||
transaction_energy_total=energy_total,
|
||||
transaction_energy_total_previous=energy_total_previous,
|
||||
transaction_cost_total=cost_total,
|
||||
transaction_cost_total_previous=cost_total_previous
|
||||
),
|
||||
current_transaction=current_transaction,
|
||||
recent_transactions=recent_transactions
|
||||
)
|
||||
return list()
|
|
@ -8,8 +8,6 @@ from app.schemas.connector import Connector
|
|||
|
||||
from ocpp.v201.enums import ResetEnumType, ResetStatusEnumType
|
||||
|
||||
from app.util.encoders import decimal_encoder
|
||||
|
||||
class ChargePointBase(BaseModel):
|
||||
identity: str
|
||||
is_active: bool
|
||||
|
@ -34,7 +32,6 @@ class ChargePoint(ChargePointBase):
|
|||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
json_encoders = {Decimal: decimal_encoder}
|
||||
|
||||
class ChargePointPassword(BaseModel):
|
||||
password: str
|
||||
|
|
|
@ -4,8 +4,6 @@ from uuid import UUID
|
|||
from pydantic import BaseModel
|
||||
import enum
|
||||
|
||||
from app.util.encoders import decimal_encoder
|
||||
|
||||
class AttributeType(enum.Enum):
|
||||
ACTUAL = "Actual"
|
||||
TARGET = "Target"
|
||||
|
@ -54,7 +52,6 @@ class ChargepointVariable(BaseModel):
|
|||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
json_encoders = {Decimal: decimal_encoder}
|
||||
|
||||
class ChargepointVariableUpdate(BaseModel):
|
||||
value: str
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
from decimal import Decimal
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.schemas.transaction import Transaction
|
||||
from app.util.encoders import decimal_encoder
|
||||
|
||||
class DashboardStats(BaseModel):
|
||||
transaction_count: int
|
||||
transaction_count_previous: int
|
||||
transaction_energy_total: Decimal
|
||||
transaction_energy_total_previous: Decimal
|
||||
transaction_cost_total: Decimal
|
||||
transaction_cost_total_previous: Decimal
|
||||
|
||||
class Config:
|
||||
json_encoders = {Decimal: decimal_encoder}
|
||||
|
||||
class DashboardResponse(BaseModel):
|
||||
stats: DashboardStats
|
||||
current_transaction: Optional[Transaction] = None
|
||||
recent_transactions: list[Transaction]
|
||||
|
||||
class Config:
|
||||
json_encoders = {Decimal: decimal_encoder}
|
|
@ -5,8 +5,6 @@ from typing import Optional
|
|||
from uuid import UUID
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.util.encoders import decimal_encoder
|
||||
|
||||
class PhaseType(enum.Enum):
|
||||
L1 = "L1"
|
||||
L2 = "L2"
|
||||
|
@ -57,4 +55,3 @@ class MeterValue(BaseModel):
|
|||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
json_encoders = {Decimal: decimal_encoder}
|
||||
|
|
|
@ -8,5 +8,4 @@ class Session(BaseModel):
|
|||
name: str
|
||||
last_used: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
model_config = {"from_attributes": True}
|
|
@ -5,8 +5,6 @@ from typing import Optional
|
|||
from uuid import UUID
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.util.encoders import decimal_encoder
|
||||
|
||||
class TransactionStatus(enum.Enum):
|
||||
ONGOING = "ongoing"
|
||||
ENDED = "ended"
|
||||
|
@ -52,7 +50,6 @@ class Transaction(BaseModel):
|
|||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
json_encoders = {Decimal: decimal_encoder}
|
||||
|
||||
class RemoteTransactionStartStopResponse(BaseModel):
|
||||
status: RemoteTransactionStartStopStatus
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
from decimal import Decimal
|
||||
from typing import Union
|
||||
|
||||
def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
|
||||
"""Encodes a Decimal as int of there's no exponent, otherwise float.
|
||||
|
||||
This is useful when we use ConstrainedDecimal to represent Numeric(x,0)
|
||||
where a integer (but not int typed) is used. Encoding this as a float
|
||||
results in failed round-tripping between encode and parse.
|
||||
Our Id type is a prime example of this.
|
||||
|
||||
>>> decimal_encoder(Decimal("1.0"))
|
||||
1.0
|
||||
|
||||
>>> decimal_encoder(Decimal("1"))
|
||||
1
|
||||
"""
|
||||
exponent = dec_value.as_tuple().exponent
|
||||
if isinstance(exponent, int) and exponent >= 0:
|
||||
return int(dec_value)
|
||||
else:
|
||||
return float(dec_value)
|
Loading…
Add table
Add a link
Reference in a new issue