diff --git a/backend/app/models/chargepoint.py b/backend/app/models/chargepoint.py index c41d0d3..e24e180 100644 --- a/backend/app/models/chargepoint.py +++ b/backend/app/models/chargepoint.py @@ -25,4 +25,3 @@ class ChargePoint(Base): connectors = relationship("Connector", cascade="delete, delete-orphan") transactions = relationship("Transaction", cascade="delete, delete-orphan") variables = relationship("ChargepointVariable", cascade="delete, delete-orphan") - firmware_updates = relationship("FirmwareUpdate", cascade="delete, delete-orphan") diff --git a/backend/app/models/firmware_update.py b/backend/app/models/firmware_update.py index 5d0d9f7..9e4fc4b 100644 --- a/backend/app/models/firmware_update.py +++ b/backend/app/models/firmware_update.py @@ -1,6 +1,5 @@ import uuid from sqlalchemy import Column, DateTime, Enum, ForeignKey, Integer, String, Text, Uuid -from sqlalchemy.orm import relationship from app.database import Base from app.schemas.firmware_update import FirmwareUpdateStatus @@ -21,4 +20,3 @@ class FirmwareUpdate(Base): signature = Column(String, nullable=True) chargepoint_id = Column(Uuid, ForeignKey("chargepoints.id"), index=True) - chargepoint = relationship("ChargePoint", back_populates="firmware_updates") diff --git a/backend/app/models/meter_value.py b/backend/app/models/meter_value.py index 5adf6a1..e2528ed 100644 --- a/backend/app/models/meter_value.py +++ b/backend/app/models/meter_value.py @@ -1,6 +1,5 @@ import uuid from sqlalchemy import Uuid, Column, DateTime, Enum, Float, ForeignKey, String -from sqlalchemy.orm import relationship from app.database import Base from app.schemas.meter_value import Measurand, PhaseType @@ -15,5 +14,4 @@ class MeterValue(Base): unit = Column(String, nullable=True) value = Column(Float) - transaction_id = Column(String, ForeignKey("transactions.id"), index=True) - transaction = relationship("Transaction", back_populates="meter_values") \ No newline at end of file + transaction_id = Column(String, ForeignKey("transactions.id"), index=True) \ No newline at end of file diff --git a/backend/app/models/session.py b/backend/app/models/session.py index d9c0ce2..cd0c5ba 100644 --- a/backend/app/models/session.py +++ b/backend/app/models/session.py @@ -1,6 +1,5 @@ import uuid from sqlalchemy import Column, DateTime, ForeignKey, String, Uuid -from sqlalchemy.orm import relationship from app.database import Base @@ -12,5 +11,4 @@ class Session(Base): refresh_token = Column(String, nullable=False, unique=True, index=True) last_used = Column(DateTime(timezone=True)) - user_id = Column(Uuid, ForeignKey("users.id"), nullable=False, index=True) - user = relationship("User", back_populates="sessions") \ No newline at end of file + user_id = Column(Uuid, ForeignKey("users.id"), nullable=False, index=True) \ No newline at end of file diff --git a/backend/app/models/transaction.py b/backend/app/models/transaction.py index a9d4452..a918430 100644 --- a/backend/app/models/transaction.py +++ b/backend/app/models/transaction.py @@ -1,5 +1,4 @@ from sqlalchemy import String, Uuid, Column, DateTime, Enum, Numeric, ForeignKey -from sqlalchemy.orm import relationship from app.schemas.transaction import TransactionEventTriggerReason, TransactionStatus from app.database import Base @@ -12,14 +11,9 @@ class Transaction(Base): started_at = Column(DateTime, index=True) ended_at = Column(DateTime, nullable=True, index=True) meter_start = Column(Numeric(10,2)) - meter_end = Column(Numeric(10,2)) + meter_end = Column(Numeric(10,2), nullable=True) end_reason = Column(Enum(TransactionEventTriggerReason), nullable=True) price = Column(Numeric(10,2)) user_id = Column(Uuid, ForeignKey("users.id"), nullable=True, index=True) - user = relationship("User", back_populates="transactions") - chargepoint_id = Column(Uuid, ForeignKey("chargepoints.id"), index=True) - chargepoint = relationship("ChargePoint", back_populates="transactions") - - meter_values = relationship("MeterValue", cascade="delete, delete-orphan") diff --git a/backend/app/models/user.py b/backend/app/models/user.py index bfdefb9..75cf5da 100644 --- a/backend/app/models/user.py +++ b/backend/app/models/user.py @@ -17,4 +17,3 @@ class User(Base): id_tokens = relationship("IdToken", back_populates="owner", cascade="delete, delete-orphan") transactions = relationship("Transaction", cascade="delete, delete-orphan") - sessions = relationship("Session", cascade="delete, delete-orphan") diff --git a/backend/app/schemas/auth_token.py b/backend/app/schemas/auth_token.py index 9ae5534..bb9ee60 100644 --- a/backend/app/schemas/auth_token.py +++ b/backend/app/schemas/auth_token.py @@ -3,7 +3,6 @@ from datetime import datetime from pydantic import BaseModel from app.schemas.user import Role -from app.util.encoders import force_utc_datetime @dataclass @@ -20,6 +19,3 @@ class TokenResponse(BaseModel): access_token: str refresh_token: str not_after: datetime - - class Config: - json_encoders = {datetime: force_utc_datetime} diff --git a/backend/app/schemas/chargepoint.py b/backend/app/schemas/chargepoint.py index 878b20a..2493315 100644 --- a/backend/app/schemas/chargepoint.py +++ b/backend/app/schemas/chargepoint.py @@ -8,7 +8,7 @@ from app.schemas.connector import Connector from ocpp.v201.enums import ResetEnumType, ResetStatusEnumType -from app.util.encoders import decimal_encoder, force_utc_datetime +from app.util.encoders import decimal_encoder class ChargePointBase(BaseModel): identity: str @@ -32,15 +32,6 @@ class ChargePoint(ChargePointBase): firmware_version: str | None connectors: list[Connector] = [] - class Config: - from_attributes = True - json_encoders = {Decimal: decimal_encoder, datetime: force_utc_datetime} - -class ChargePointThumb(BaseModel): - id: UUID - identity: str - price: Decimal - class Config: from_attributes = True json_encoders = {Decimal: decimal_encoder} diff --git a/backend/app/schemas/meter_value.py b/backend/app/schemas/meter_value.py index abc08f1..99f6945 100644 --- a/backend/app/schemas/meter_value.py +++ b/backend/app/schemas/meter_value.py @@ -5,7 +5,7 @@ from typing import Optional from uuid import UUID from pydantic import BaseModel -from app.util.encoders import decimal_encoder, force_utc_datetime +from app.util.encoders import decimal_encoder class PhaseType(enum.Enum): L1 = "L1" @@ -57,4 +57,4 @@ class MeterValue(BaseModel): class Config: from_attributes = True - json_encoders = {Decimal: decimal_encoder, datetime: force_utc_datetime} + json_encoders = {Decimal: decimal_encoder} diff --git a/backend/app/schemas/session.py b/backend/app/schemas/session.py index d9bbf35..07b98a1 100644 --- a/backend/app/schemas/session.py +++ b/backend/app/schemas/session.py @@ -2,8 +2,6 @@ from datetime import datetime from uuid import UUID from pydantic import BaseModel -from app.util.encoders import force_utc_datetime - class Session(BaseModel): id: UUID @@ -11,5 +9,4 @@ class Session(BaseModel): last_used: datetime class Config: - from_attributes = True - json_encoders = {datetime: force_utc_datetime} \ No newline at end of file + from_attributes = True \ No newline at end of file diff --git a/backend/app/schemas/transaction.py b/backend/app/schemas/transaction.py index ca242ee..0188f91 100644 --- a/backend/app/schemas/transaction.py +++ b/backend/app/schemas/transaction.py @@ -1,12 +1,11 @@ -import enum from datetime import datetime from decimal import Decimal +import enum from typing import Optional +from uuid import UUID from pydantic import BaseModel -from app.schemas.chargepoint import ChargePointThumb -from app.schemas.user import UserThumb -from app.util.encoders import decimal_encoder, force_utc_datetime +from app.util.encoders import decimal_encoder class TransactionStatus(enum.Enum): ONGOING = "ongoing" @@ -45,15 +44,15 @@ class Transaction(BaseModel): started_at: datetime ended_at: Optional[datetime] = None meter_start: Decimal - meter_end: Decimal + meter_end: Optional[Decimal] = None end_reason: Optional[TransactionEventTriggerReason] = None price: Decimal - user: UserThumb - chargepoint: ChargePointThumb + user_id: Optional[UUID] = None + chargepoint_id: UUID class Config: from_attributes = True - json_encoders = {Decimal: decimal_encoder, datetime: force_utc_datetime} + json_encoders = {Decimal: decimal_encoder} class RemoteTransactionStartStopResponse(BaseModel): status: RemoteTransactionStartStopStatus diff --git a/backend/app/schemas/user.py b/backend/app/schemas/user.py index c113e88..8737aa4 100644 --- a/backend/app/schemas/user.py +++ b/backend/app/schemas/user.py @@ -30,13 +30,6 @@ class User(UserBase): class Config: from_attributes = True -class UserThumb(BaseModel): - id: UUID - friendly_name: str - - class Config: - from_attributes = True - class PasswordUpdate(BaseModel): old_password: str = Field(max_length=100) new_password: str = Field(max_length=100) diff --git a/backend/app/services/transaction_service.py b/backend/app/services/transaction_service.py index 9d3c992..d8719b4 100644 --- a/backend/app/services/transaction_service.py +++ b/backend/app/services/transaction_service.py @@ -21,23 +21,19 @@ async def create_transaction( with SessionLocal() as db: chargepoint = db.query(ChargePoint).filter(ChargePoint.identity == chargepoint_identity).first() meter_start=0 - meter_end=0 if "meter_value" in transaction_data.keys(): for meter_value_entry in transaction_data['meter_value']: for sampled_value in meter_value_entry['sampled_value']: if "measurand" in sampled_value.keys(): if sampled_value['measurand'] == str(Measurand.ENERGY_ACTIVE_IMPORT_REGISTER): meter_start = sampled_value['value'] - meter_end = sampled_value['value'] else: meter_start = sampled_value['value'] - meter_end = sampled_value['value'] transaction = Transaction( id=transaction_info["transaction_id"], status=TransactionStatus.ONGOING, started_at=timestamp, meter_start=meter_start, - meter_end=meter_end, price=chargepoint.price, chargepoint_id=chargepoint.id, user_id=user_id @@ -59,12 +55,6 @@ async def update_transaction( transaction_id=transaction.id, meter_value_data=meter_value_entry ) - # Update current meter_end value - for sampled_value in meter_value_entry['sampled_value']: - if "measurand" in sampled_value.keys(): - if sampled_value['measurand'] == str(Measurand.ENERGY_ACTIVE_IMPORT_REGISTER): - transaction.meter_end = sampled_value['value'] - db.commit() async def end_transaction( transaction_id: str, diff --git a/backend/app/util/encoders.py b/backend/app/util/encoders.py index 0f0d21f..fbdec8d 100644 --- a/backend/app/util/encoders.py +++ b/backend/app/util/encoders.py @@ -1,4 +1,3 @@ -from datetime import datetime, timezone from decimal import Decimal from typing import Union @@ -20,8 +19,4 @@ def decimal_encoder(dec_value: Decimal) -> Union[int, float]: if isinstance(exponent, int) and exponent >= 0: return int(dec_value) else: - return float(dec_value) - -def force_utc_datetime(datetime_value: datetime) -> datetime: - """Force a datetime to be in the UTC timzone""" - return datetime_value.replace(tzinfo=timezone.utc) \ No newline at end of file + return float(dec_value) \ No newline at end of file diff --git a/frontend/src/lib/component/DashboardCard.svelte b/frontend/src/lib/component/DashboardCard.svelte index c476707..2b77f43 100644 --- a/frontend/src/lib/component/DashboardCard.svelte +++ b/frontend/src/lib/component/DashboardCard.svelte @@ -25,20 +25,18 @@

{props.value}{#if props.unit}{' ' + props.unit}{/if}

- {#if !Number.isNaN(diff)} - {#if diff > 0} -
- {diff}% -
- {:else if diff < 0} -
- {diff}% -
- {:else} -
- {diff}% -
- {/if} + {#if diff > 0} +
+ {diff}% +
+ {:else if diff < 0} +
+ {diff}% +
+ {:else} +
+ {diff}% +
{/if} diff --git a/frontend/src/lib/component/TransactionTable.svelte b/frontend/src/lib/component/TransactionTable.svelte index 6b5a73e..0940a37 100644 --- a/frontend/src/lib/component/TransactionTable.svelte +++ b/frontend/src/lib/component/TransactionTable.svelte @@ -1,9 +1,18 @@ @@ -27,7 +36,7 @@

{$i18n.t('common:transactionTable.startTime', { - time: new Date(transaction.started_at), + time: transaction.begin, formatParams: { time: { year: 'numeric', @@ -42,7 +51,7 @@

{$i18n.t('common:transactionTable.endTime', { - time: new Date(transaction.ended_at!), + time: transaction.end, formatParams: { time: { year: 'numeric', @@ -61,15 +70,11 @@ - {transaction.chargepoint.identity} + {transaction.chargepoint.name} - {(transaction.meter_end - transaction.meter_start).toFixed(2)} kWh - {((transaction.meter_end - transaction.meter_start) * transaction.price).toFixed(2)} € + {transaction.energyAmmount} kWh + {transaction.cost} € {$i18n.t('common:transactionTable.detailButton')} diff --git a/frontend/src/lib/types/chargepoint.ts b/frontend/src/lib/types/chargepoint.ts index 4e4574e..ade067b 100644 --- a/frontend/src/lib/types/chargepoint.ts +++ b/frontend/src/lib/types/chargepoint.ts @@ -16,10 +16,4 @@ export type ChargePoint = { status: 'Available' | 'Occupied' | 'Reserved' | 'Unavailable' | 'Faulted' } ] -} - -export type ChargePointThumb = { - id: string; - identity: string; - price: number; } \ No newline at end of file diff --git a/frontend/src/lib/types/dashboard.ts b/frontend/src/lib/types/dashboard.ts deleted file mode 100644 index 51713ff..0000000 --- a/frontend/src/lib/types/dashboard.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { Transaction } from "./transaction"; - -export type Dashboard = { - stats: { - transaction_count: number; - transaction_count_previous: number; - transaction_energy_total: number; - transaction_energy_total_previous: number; - transaction_cost_total: number; - transaction_cost_total_previous: number; - }; - current_transaction: Transaction | undefined; - recent_transactions: Transaction[]; -} \ No newline at end of file diff --git a/frontend/src/lib/types/transaction.ts b/frontend/src/lib/types/transaction.ts deleted file mode 100644 index 3fc6501..0000000 --- a/frontend/src/lib/types/transaction.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { ChargePointThumb } from "./chargepoint"; -import type { UserThumb } from "./user"; - -export type TransactionEventTriggerReason = 'Authorized' | 'CablePluggedIn' | 'ChargingRateChanged' | 'ChargingStateChanged' | 'Deauthorized' | 'EnergyLimitReached' | 'EVCommunicationLost' | 'EVConnectTimeout' | 'MeterValueClock' | 'MeterValuePeriodic' | 'TimeLimitReached' | 'Trigger' | 'UnlockCommand' | 'StopAuthorized' | 'EVDeparted' | 'EVDetected' | 'RemoteStop' | 'RemoteStart' | 'AbnormalCondition' | 'SignedDataReceived' | 'ResetCommand'; - -export type Transaction = { - id: string; - status: 'ongoing' | 'ended'; - started_at: string; - ended_at: string | undefined; - meter_start: number; - meter_end: number; - end_reason: TransactionEventTriggerReason; - price: number; - user: UserThumb; - chargepoint: ChargePointThumb; -} \ No newline at end of file diff --git a/frontend/src/lib/types/user.ts b/frontend/src/lib/types/user.ts deleted file mode 100644 index aa1d184..0000000 --- a/frontend/src/lib/types/user.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type UserThumb = { - id: string; - friendly_name: string; -} \ No newline at end of file diff --git a/frontend/src/lib/util.ts b/frontend/src/lib/util.ts index bfabf44..9d3747c 100644 --- a/frontend/src/lib/util.ts +++ b/frontend/src/lib/util.ts @@ -7,23 +7,4 @@ export const currentDaytime = function(): string { return 'day' } return 'evening' -} - -export function relativeTimeFromDate(date: Date): {val: number, range: Intl.RelativeTimeFormatUnit} | undefined { - const units: {unit: Intl.RelativeTimeFormatUnit; ms: number}[] = [ - {unit: "year", ms: 31536000000}, - {unit: "month", ms: 2628000000}, - {unit: "day", ms: 86400000}, - {unit: "hour", ms: 3600000}, - {unit: "minute", ms: 60000}, - {unit: "second", ms: 1000}, - ]; - - const elapsed = date.getTime() - new Date().getTime(); - for (const {unit, ms} of units) { - if (Math.abs(elapsed) >= ms || unit === "second") { - return {val: Math.round(elapsed / ms), range: unit}; - } - } - return undefined; } \ No newline at end of file diff --git a/frontend/src/routes/(navbar)/+page.svelte b/frontend/src/routes/(navbar)/+page.svelte index de8c463..e96407d 100644 --- a/frontend/src/routes/(navbar)/+page.svelte +++ b/frontend/src/routes/(navbar)/+page.svelte @@ -4,39 +4,10 @@ import i18n from '$lib/i18n' import DashboardCard from '$lib/component/DashboardCard.svelte' import TransactionTable from '$lib/component/TransactionTable.svelte' - import type { Dashboard } from '$lib/types/dashboard' - import axios from '$lib/axios.svelte' - import { onMount } from 'svelte' $i18n.loadNamespaces('dashboard') - let dashboardData: Dashboard = $state({ - stats: { - transaction_count: 0, - transaction_count_previous: 0, - transaction_energy_total: 0, - transaction_energy_total_previous: 0, - transaction_cost_total: 0, - transaction_cost_total_previous: 0, - }, - current_transaction: undefined, - recent_transactions: [], - }) - - function refreshDashboard() { - axios.get('/me/dashboard').then((res) => { - dashboardData = res.data - }) - } - - onMount(() => { - refreshDashboard() - const interval = setInterval(() => { - refreshDashboard() - }, 15000) - - return () => clearInterval(interval) - }) + let hasActiveTransaction = $state(true)

@@ -52,21 +23,18 @@ {$i18n.t('dashboard:cards.currentTransaction')}

- {#if dashboardData.current_transaction} + {#if hasActiveTransaction}
{/if}

- {#if dashboardData.current_transaction}{dashboardData.current_transaction - .meter_end - dashboardData.current_transaction.meter_start} kWh{:else}-{/if} + {#if hasActiveTransaction}0,25 kWh{:else}-{/if}

- {#if dashboardData.current_transaction} + {#if hasActiveTransaction}
- {(dashboardData.current_transaction.meter_end - - dashboardData.current_transaction.meter_start) * - dashboardData.current_transaction.price} € + 2,30 €
{/if}
@@ -75,15 +43,13 @@

- {#if dashboardData.current_transaction} - {$i18n.t('dashboard:cards.chargepoint', { - name: dashboardData.current_transaction.chargepoint.identity, - })} + {#if hasActiveTransaction} + {$i18n.t('dashboard:cards.chargepoint', { name: 'DE-EXMPL-0001' })} {:else} {$i18n.t('dashboard:cards.noCurrentTransaction')} {/if}

- {#if dashboardData.current_transaction} + {#if hasActiveTransaction}

{$i18n.t('dashboard:table.title')}

- +
diff --git a/frontend/src/routes/(navbar)/chargepoint/+page.svelte b/frontend/src/routes/(navbar)/chargepoint/+page.svelte index 0d65da3..f12908f 100644 --- a/frontend/src/routes/(navbar)/chargepoint/+page.svelte +++ b/frontend/src/routes/(navbar)/chargepoint/+page.svelte @@ -1,42 +1,13 @@
@@ -59,22 +30,49 @@ {#each chargepoints as chargepoint} - {chargepoint.identity} +
+
+

+ {$i18n.t('common:transactionTable.startTime', { + time: transaction.begin, + formatParams: { + time: { + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + }, + }, + })} +

+

+ {$i18n.t('common:transactionTable.endTime', { + time: transaction.end, + formatParams: { + time: { + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + }, + }, + })} +

+
+
- {#if chargepoint.is_active} - {$i18n.t('chargepoint:state.enabled')} - {:else} - {$i18n.t('chargepoint:state.disabled')} - {/if} +
+ + {transaction.chargepoint.name} + - - {$i18n.t( - 'chargepoint:tableFormatting.lastSeen', - relativeTimeFromDate(new Date(chargepoint.last_seen))! - )} - - {chargepoint.price} € + {transaction.energyAmmount} kWh + {transaction.cost} € {$i18n.t('common:transactionTable.detailButton')} @@ -93,14 +91,8 @@ {/if}
- + - +
diff --git a/frontend/static/locales/de/chargepoint.json b/frontend/static/locales/de/chargepoint.json index 25d2220..4f76841 100644 --- a/frontend/static/locales/de/chargepoint.json +++ b/frontend/static/locales/de/chargepoint.json @@ -9,9 +9,5 @@ "tableFormatting": { "lastSeen": "{{val, relativetime}}" }, - "state": { - "enabled": "Aktiv", - "disabled": "Deaktiviert" - }, "noChargepoints": "Noch keine Ladepunkte vorhanden." } \ No newline at end of file diff --git a/frontend/static/locales/en/chargepoint.json b/frontend/static/locales/en/chargepoint.json index e485e10..0bcbf00 100644 --- a/frontend/static/locales/en/chargepoint.json +++ b/frontend/static/locales/en/chargepoint.json @@ -9,9 +9,5 @@ "tableFormatting": { "lastSeen": "{{val, relativetime}}" }, - "state": { - "enabled": "Active", - "disabled": "Disabled" - }, "noChargepoints": "There are no chargepoints yet." } \ No newline at end of file