mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-21 04:39:06 +00:00
fix: browse page timestamps show UTC instead of user local time (#482)
* fix: browse page timestamps show UTC instead of user local time Closes #474 * fix: tests
This commit is contained in:
@@ -3,13 +3,13 @@ import json
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Literal
|
from typing import Any, Literal
|
||||||
|
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field, field_serializer
|
||||||
from reflector.db import database, metadata
|
from reflector.db import database, metadata
|
||||||
from reflector.processors.types import Word as ProcessorWord
|
from reflector.processors.types import Word as ProcessorWord
|
||||||
from reflector.settings import settings
|
from reflector.settings import settings
|
||||||
@@ -82,7 +82,7 @@ transcripts = sqlalchemy.Table(
|
|||||||
|
|
||||||
|
|
||||||
def generate_transcript_name() -> str:
|
def generate_transcript_name() -> str:
|
||||||
now = datetime.utcnow()
|
now = datetime.now(timezone.utc)
|
||||||
return f"Transcript {now.strftime('%Y-%m-%d %H:%M:%S')}"
|
return f"Transcript {now.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||||
|
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ class Transcript(BaseModel):
|
|||||||
status: str = "idle"
|
status: str = "idle"
|
||||||
locked: bool = False
|
locked: bool = False
|
||||||
duration: float = 0
|
duration: float = 0
|
||||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||||
title: str | None = None
|
title: str | None = None
|
||||||
short_summary: str | None = None
|
short_summary: str | None = None
|
||||||
long_summary: str | None = None
|
long_summary: str | None = None
|
||||||
@@ -168,6 +168,12 @@ class Transcript(BaseModel):
|
|||||||
source_kind: SourceKind
|
source_kind: SourceKind
|
||||||
audio_deleted: bool | None = None
|
audio_deleted: bool | None = None
|
||||||
|
|
||||||
|
@field_serializer("created_at", when_used="json")
|
||||||
|
def serialize_datetime(self, dt: datetime) -> str:
|
||||||
|
if dt.tzinfo is None:
|
||||||
|
dt = dt.replace(tzinfo=timezone.utc)
|
||||||
|
return dt.isoformat()
|
||||||
|
|
||||||
def add_event(self, event: str, data: BaseModel) -> TranscriptEvent:
|
def add_event(self, event: str, data: BaseModel) -> TranscriptEvent:
|
||||||
ev = TranscriptEvent(event=event, data=data.model_dump())
|
ev = TranscriptEvent(event=event, data=data.model_dump())
|
||||||
self.events.append(ev)
|
self.events.append(ev)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Annotated, Literal, Optional
|
from typing import Annotated, Literal, Optional
|
||||||
|
|
||||||
import reflector.auth as auth
|
import reflector.auth as auth
|
||||||
@@ -6,7 +6,7 @@ from fastapi import APIRouter, Depends, HTTPException
|
|||||||
from fastapi_pagination import Page
|
from fastapi_pagination import Page
|
||||||
from fastapi_pagination.ext.databases import paginate
|
from fastapi_pagination.ext.databases import paginate
|
||||||
from jose import jwt
|
from jose import jwt
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field, field_serializer
|
||||||
from reflector.db.meetings import meetings_controller
|
from reflector.db.meetings import meetings_controller
|
||||||
from reflector.db.migrate_user import migrate_user
|
from reflector.db.migrate_user import migrate_user
|
||||||
from reflector.db.rooms import rooms_controller
|
from reflector.db.rooms import rooms_controller
|
||||||
@@ -61,6 +61,13 @@ class GetTranscriptMinimal(BaseModel):
|
|||||||
target_language: str | None
|
target_language: str | None
|
||||||
reviewed: bool
|
reviewed: bool
|
||||||
meeting_id: str | None
|
meeting_id: str | None
|
||||||
|
|
||||||
|
@field_serializer("created_at", when_used="json")
|
||||||
|
def serialize_datetime(self, dt: datetime) -> str:
|
||||||
|
if dt.tzinfo is None:
|
||||||
|
dt = dt.replace(tzinfo=timezone.utc)
|
||||||
|
return dt.isoformat()
|
||||||
|
|
||||||
source_kind: SourceKind
|
source_kind: SourceKind
|
||||||
room_id: str | None = None
|
room_id: str | None = None
|
||||||
room_name: str | None = None
|
room_name: str | None = None
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import useSessionUser from "../../lib/useSessionUser";
|
|||||||
import NextLink from "next/link";
|
import NextLink from "next/link";
|
||||||
import { Room, GetTranscriptMinimal } from "../../api";
|
import { Room, GetTranscriptMinimal } from "../../api";
|
||||||
import Pagination from "./pagination";
|
import Pagination from "./pagination";
|
||||||
import { formatTimeMs } from "../../lib/time";
|
import { formatTimeMs, formatLocalDate } from "../../lib/time";
|
||||||
import useApi from "../../lib/useApi";
|
import useApi from "../../lib/useApi";
|
||||||
import { useError } from "../../(errors)/errorContext";
|
import { useError } from "../../(errors)/errorContext";
|
||||||
import { SourceKind } from "../../api";
|
import { SourceKind } from "../../api";
|
||||||
@@ -381,15 +381,7 @@ export default function TranscriptBrowser() {
|
|||||||
? item.room_name
|
? item.room_name
|
||||||
: item.source_kind}
|
: item.source_kind}
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>{formatLocalDate(item.created_at)}</Td>
|
||||||
{new Date(item.created_at).toLocaleString("en-US", {
|
|
||||||
year: "numeric",
|
|
||||||
month: "long",
|
|
||||||
day: "numeric",
|
|
||||||
hour: "numeric",
|
|
||||||
minute: "numeric",
|
|
||||||
})}
|
|
||||||
</Td>
|
|
||||||
<Td>{formatTimeMs(item.duration)}</Td>
|
<Td>{formatTimeMs(item.duration)}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<Menu closeOnSelect={true}>
|
<Menu closeOnSelect={true}>
|
||||||
@@ -466,9 +458,7 @@ export default function TranscriptBrowser() {
|
|||||||
? item.room_name
|
? item.room_name
|
||||||
: item.source_kind}
|
: item.source_kind}
|
||||||
</Text>
|
</Text>
|
||||||
<Text>
|
<Text>Date: {formatLocalDate(item.created_at)}</Text>
|
||||||
Date: {new Date(item.created_at).toLocaleString()}
|
|
||||||
</Text>
|
|
||||||
<Text>Duration: {formatTimeMs(item.duration)}</Text>
|
<Text>Duration: {formatTimeMs(item.duration)}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Menu>
|
<Menu>
|
||||||
|
|||||||
@@ -28,3 +28,29 @@ export const formatTimeDifference = (seconds: number): string => {
|
|||||||
|
|
||||||
return timeString;
|
return timeString;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const formatRelativeTime = (dateString: string): string => {
|
||||||
|
const now = new Date();
|
||||||
|
const past = new Date(dateString);
|
||||||
|
const diffMs = now.getTime() - past.getTime();
|
||||||
|
|
||||||
|
const diffSeconds = Math.floor(diffMs / 1000);
|
||||||
|
const diffMinutes = Math.floor(diffSeconds / 60);
|
||||||
|
const diffHours = Math.floor(diffMinutes / 60);
|
||||||
|
const diffDays = Math.floor(diffHours / 24);
|
||||||
|
|
||||||
|
if (diffSeconds < 60) return `${diffSeconds}s ago`;
|
||||||
|
if (diffMinutes < 60) return `${diffMinutes}m ago`;
|
||||||
|
if (diffHours < 24) return `${diffHours}h ago`;
|
||||||
|
return `${diffDays}d ago`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatLocalDate = (dateString: string): string => {
|
||||||
|
return new Date(dateString).toLocaleString(navigator.language || "en-US", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "numeric",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user