Skip to content

Commit

Permalink
Merge pull request #9 from extrawest/release/2023-09-28
Browse files Browse the repository at this point in the history
[release] 2023-09-28
  • Loading branch information
andrewdubyniak authored Sep 29, 2023
2 parents de4218e + f3d795c commit 592a9be
Show file tree
Hide file tree
Showing 16 changed files with 456 additions and 37 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ Example: `http://127.0.0.1:8000/ocpi/docs/`
- Add support for initializing v2.1.1;
- It's now possible to initialize a few versions of ocpi for one project;
- Minimal required python version is 3.10;
- Add cdrs module;


## Related
Expand Down
2 changes: 1 addition & 1 deletion py_ocpi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Python Implementation of OCPI"""

__version__ = "2023.09.22"
__version__ = "2023.09.28"

from .core import enums, data_types
from .main import get_application
21 changes: 12 additions & 9 deletions py_ocpi/core/endpoints/v_2_1_1/cpo.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,29 @@
VersionNumber,
)

URL_BASE = (
f"https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/"
f"cpo/{VersionNumber.v_2_1_1.value}/"
)


CREDENTIALS_AND_REGISTRATION = Endpoint(
identifier=ModuleID.credentials_and_registration,
url=URL(
f"https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo"
f"/{VersionNumber.v_2_1_1.value}/"
f"{ModuleID.credentials_and_registration.value}"
),
url=URL(f"{URL_BASE}/{ModuleID.credentials_and_registration.value}"),
)

LOCATIONS = Endpoint(
identifier=ModuleID.locations,
url=URL(
f"https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/cpo"
f"/{VersionNumber.v_2_1_1.value}/{ModuleID.locations.value}"
),
url=URL(f"{URL_BASE}/{ModuleID.locations.value}"),
)

CDRS = Endpoint(
identifier=ModuleID.cdrs,
url=URL(f"{URL_BASE}/{ModuleID.cdrs.value}"),
)

ENDPOINTS_LIST = {
ModuleID.credentials_and_registration: CREDENTIALS_AND_REGISTRATION,
ModuleID.locations: LOCATIONS,
ModuleID.cdrs: CDRS,
}
22 changes: 13 additions & 9 deletions py_ocpi/core/endpoints/v_2_1_1/emsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,29 @@
VersionNumber,
)

URL_BASE = (
f"https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/"
f"emsp/{VersionNumber.v_2_1_1.value}"
)


CREDENTIALS_AND_REGISTRATION = Endpoint(
identifier=ModuleID.credentials_and_registration,
url=URL(
f"https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp"
f"/{VersionNumber.v_2_1_1.value}/"
f"{ModuleID.credentials_and_registration.value}"
),
url=URL(f"{URL_BASE}/{ModuleID.credentials_and_registration.value}"),
)

LOCATIONS = Endpoint(
identifier=ModuleID.locations,
url=URL(
f"https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp"
f"/{VersionNumber.v_2_1_1.value}/{ModuleID.locations.value}"
),
url=URL(f"{URL_BASE}/{ModuleID.locations.value}"),
)

CDRS = Endpoint(
identifier=ModuleID.cdrs,
url=URL(f"{URL_BASE}/{ModuleID.cdrs.value}"),
)

ENDPOINTS_LIST = {
ModuleID.credentials_and_registration: CREDENTIALS_AND_REGISTRATION,
ModuleID.locations: LOCATIONS,
ModuleID.cdrs: CDRS,
}
2 changes: 2 additions & 0 deletions py_ocpi/modules/cdrs/v_2_1_1/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .cpo import router as cpo_router
from .emsp import router as emsp_router
43 changes: 43 additions & 0 deletions py_ocpi/modules/cdrs/v_2_1_1/api/cpo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from fastapi import APIRouter, Depends, Response, Request

from py_ocpi.modules.versions.enums import VersionNumber
from py_ocpi.core import status
from py_ocpi.core.adapter import Adapter
from py_ocpi.core.crud import Crud
from py_ocpi.core.dependencies import get_crud, get_adapter, pagination_filters
from py_ocpi.core.enums import ModuleID, RoleEnum
from py_ocpi.core.schemas import OCPIResponse
from py_ocpi.core.utils import get_auth_token_from_header, get_list

router = APIRouter(
prefix="/cdrs",
)


@router.get("/", response_model=OCPIResponse)
async def get_cdrs(
response: Response,
request: Request,
crud: Crud = Depends(get_crud),
adapter: Adapter = Depends(get_adapter),
filters: dict = Depends(pagination_filters),
):
auth_token = get_auth_token_from_header(request)

data_list = await get_list(
response,
filters,
ModuleID.cdrs,
RoleEnum.cpo,
VersionNumber.v_2_1_1,
crud,
auth_token=auth_token,
)

cdrs = []
for data in data_list:
cdrs.append(adapter.cdr_adapter(data, VersionNumber.v_2_1_1).dict())
return OCPIResponse(
data=cdrs,
**status.OCPI_1000_GENERIC_SUCESS_CODE,
)
70 changes: 70 additions & 0 deletions py_ocpi/modules/cdrs/v_2_1_1/api/emsp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from fastapi import APIRouter, Depends, Request, Response

from py_ocpi.modules.cdrs.v_2_1_1.schemas import Cdr
from py_ocpi.modules.versions.enums import VersionNumber
from py_ocpi.core.utils import get_auth_token_from_header
from py_ocpi.core import status
from py_ocpi.core.schemas import OCPIResponse
from py_ocpi.core.adapter import Adapter
from py_ocpi.core.crud import Crud
from py_ocpi.core.data_types import CiString
from py_ocpi.core.enums import ModuleID, RoleEnum
from py_ocpi.core.config import settings
from py_ocpi.core.dependencies import get_crud, get_adapter

router = APIRouter(
prefix="/cdrs",
)


@router.get("/{cdr_id}", response_model=OCPIResponse)
async def get_cdr(
request: Request,
cdr_id: CiString(36), # type: ignore
crud: Crud = Depends(get_crud),
adapter: Adapter = Depends(get_adapter),
):
auth_token = get_auth_token_from_header(request)

data = await crud.get(
ModuleID.cdrs,
RoleEnum.emsp,
cdr_id,
auth_token=auth_token,
version=VersionNumber.v_2_1_1,
)
return OCPIResponse(
data=[adapter.cdr_adapter(data, VersionNumber.v_2_1_1).dict()],
**status.OCPI_1000_GENERIC_SUCESS_CODE,
)


@router.post("/", response_model=OCPIResponse)
async def add_cdr(
request: Request,
response: Response,
cdr: Cdr,
crud: Crud = Depends(get_crud),
adapter: Adapter = Depends(get_adapter),
):
auth_token = get_auth_token_from_header(request)

data = await crud.create(
ModuleID.cdrs,
RoleEnum.emsp,
cdr.dict(),
auth_token=auth_token,
version=VersionNumber.v_2_1_1,
)

cdr_data = adapter.cdr_adapter(data, VersionNumber.v_2_1_1)
cdr_url = (
f"https://{settings.OCPI_HOST}/{settings.OCPI_PREFIX}/emsp"
f"/{VersionNumber.v_2_1_1}/{ModuleID.cdrs}/{cdr_data.id}"
)
response.headers.append("Location", cdr_url)

return OCPIResponse(
data=[cdr_data.dict()],
**status.OCPI_1000_GENERIC_SUCESS_CODE,
)
31 changes: 31 additions & 0 deletions py_ocpi/modules/cdrs/v_2_1_1/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from enum import Enum


class AuthMethod(str, Enum):
"""
https://github.com/ocpi/ocpi/blob/release-2.1.1-bugfixes/mod_cdrs.md#41-authmethod-enum
"""

# Authentication request from the eMSP
auth_request = "AUTH_REQUEST"
# Whitelist used to authenticate, no request done to the eMSP
whitelist = "WHITELIST"


class CdrDimensionType(str, Enum):
"""
https://github.com/ocpi/ocpi/blob/release-2.1.1-bugfixes/mod_cdrs.md#43-cdrdimensiontype-enum
"""

# defined in kWh, default step_size is 1 Wh
energy = "ENERGY"
# flat fee, no unit
flat = "FLAT"
# defined in A (Ampere), Maximum current reached during charging session
max_current = "MAX_CURRENT"
# defined in A (Ampere), Minimum current used during charging session
min_current = "MIN_CURRENT"
# time not charging: defined in hours, default step_size is 1 second
parking_time = "PARKING_TIME"
# time charging: defined in hours, default step_size is 1 second
time = "TIME"
52 changes: 52 additions & 0 deletions py_ocpi/modules/cdrs/v_2_1_1/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from typing import List, Optional

from pydantic import BaseModel
from py_ocpi.modules.cdrs.v_2_1_1.enums import (
AuthMethod,
CdrDimensionType,
)

from py_ocpi.core.data_types import CiString, Number, String, DateTime
from py_ocpi.modules.locations.v_2_1_1.schemas import Location
from py_ocpi.modules.tariffs.v_2_1_1.schemas import Tariff


class CdrDimension(BaseModel):
"""
https://github.com/ocpi/ocpi/blob/release-2.1.1-bugfixes/mod_cdrs.md#42-cdrdimension-class
"""

type: CdrDimensionType
volume: Number


class ChargingPeriod(BaseModel):
"""
https://github.com/ocpi/ocpi/blob/release-2.1.1-bugfixes/mod_cdrs.md#44-chargingperiod-class
"""

start_date_time: DateTime
dimensions: List[CdrDimension]


class Cdr(BaseModel):
"""
https://github.com/ocpi/ocpi/blob/release-2.1.1-bugfixes/mod_cdrs.md#31-cdr-object
"""

id: CiString(36) # type: ignore
start_date_time: DateTime
end_date_time: DateTime
auth_id: String(36) # type: ignore
auth_method: AuthMethod
location: Location
meter_id: Optional[String(255)] # type: ignore
currency: String(3) # type: ignore
tariffs: List[Tariff] = []
charging_periods: List[ChargingPeriod]
total_cost: Number
total_energy: Number
total_time: Number
total_parking_time: Optional[Number]
remark: Optional[String(255)] # type: ignore
last_updated: DateTime
15 changes: 15 additions & 0 deletions py_ocpi/modules/tariffs/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from enum import Enum


class DayOfWeek(str, Enum):
"""
https://github.com/ocpi/ocpi/blob/2.2.1/mod_tariffs.asciidoc#141-dayofweek-enum
"""

monday = "MONDAY"
tuesday = "TUESDAY"
wednesday = "WEDNESDAY"
thursday = "THURSDAY"
friday = "FRIDAY"
saturday = "SATURDAY"
sunday = "SUNDAY"
16 changes: 16 additions & 0 deletions py_ocpi/modules/tariffs/v_2_1_1/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from py_ocpi.modules.tariffs.enums import * # noqa


class TariffDimensionType(str, Enum): # noqa
"""
https://github.com/ocpi/ocpi/blob/release-2.1.1-bugfixes/mod_tariffs.md#44-tariffdimensiontype-enum
"""

# defined in kWh, step_size multiplier: 1 Wh
energy = "ENERGY"
# flat fee, no unit
flat = "FLAT"
# time not charging: defined in hours, step_size multiplier: 1 second
parking_time = "PARKING_TIME"
# time charging: defined in hours, step_size multiplier: 1 second
time = "TIME"
61 changes: 61 additions & 0 deletions py_ocpi/modules/tariffs/v_2_1_1/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from typing import List, Optional

from pydantic import BaseModel

from py_ocpi.core.data_types import URL, DisplayText, Number, String, DateTime
from py_ocpi.modules.locations.v_2_1_1.schemas import EnergyMix
from py_ocpi.modules.tariffs.v_2_1_1.enums import (
DayOfWeek,
TariffDimensionType,
)


class PriceComponent(BaseModel):
"""
https://github.com/ocpi/ocpi/blob/release-2.1.1-bugfixes/mod_tariffs.md#42-pricecomponent-class
"""

type: TariffDimensionType
price: Number
step_size: int


class TariffRestrictions(BaseModel):
"""
https://github.com/ocpi/ocpi/blob/release-2.1.1-bugfixes/mod_tariffs.md#45-tariffrestrictions-class
"""

start_time: Optional[String(5)] # type: ignore
end_time: Optional[String(5)] # type: ignore
start_date: Optional[String(10)] # type: ignore
end_date: Optional[String(10)] # type: ignore
min_kwh: Optional[Number]
max_kwh: Optional[Number]
min_power: Optional[Number]
max_power: Optional[Number]
min_duration: Optional[int]
max_duration: Optional[int]
day_of_week: List[DayOfWeek] = []


class TariffElement(BaseModel):
"""
https://github.com/ocpi/ocpi/blob/release-2.1.1-bugfixes/mod_tariffs.md#43-tariffelement-class
"""

price_components: List[PriceComponent]
restrictions: Optional[TariffRestrictions]


class Tariff(BaseModel):
"""
https://github.com/ocpi/ocpi/blob/release-2.1.1-bugfixes/mod_tariffs.md#31-tariff-object
"""

id: String(36) # type: ignore
currency: String(3) # type: ignore
tariff_alt_text: List[DisplayText] = []
tariff_alt_url: Optional[URL]
elements: List[TariffElement]
energy_mix: Optional[EnergyMix]
last_updated: DateTime
Loading

0 comments on commit 592a9be

Please sign in to comment.