This commit is contained in:
parent
199315327b
commit
62b82788b8
|
|
@ -182,19 +182,33 @@ async def get_reports(
|
||||||
|
|
||||||
result = await db.execute(query)
|
result = await db.execute(query)
|
||||||
reports = result.scalars().all()
|
reports = result.scalars().all()
|
||||||
|
|
||||||
return [
|
# Process reports to strip large payloads for the list view
|
||||||
ReportResponse(
|
optimized_reports = []
|
||||||
id=str(r.id),
|
for r in reports:
|
||||||
ticker=r.ticker,
|
# Create a copy of the result to avoid modifying SQLAlchemy objects directly
|
||||||
market_type=r.market_type,
|
if r.result and isinstance(r.result, dict):
|
||||||
analysis_date=r.analysis_date,
|
# Shallow copy the dictionary
|
||||||
result=r.result,
|
optimized_result = dict(r.result)
|
||||||
language=r.language,
|
# Remove the massive reports field if it exists
|
||||||
created_at=r.created_at.isoformat() + "Z"
|
if "reports" in optimized_result:
|
||||||
|
optimized_result["reports"] = None
|
||||||
|
else:
|
||||||
|
optimized_result = r.result
|
||||||
|
|
||||||
|
optimized_reports.append(
|
||||||
|
ReportResponse(
|
||||||
|
id=str(r.id),
|
||||||
|
ticker=r.ticker,
|
||||||
|
market_type=r.market_type,
|
||||||
|
analysis_date=r.analysis_date,
|
||||||
|
result=optimized_result,
|
||||||
|
language=r.language,
|
||||||
|
created_at=r.created_at.isoformat() + "Z"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
for r in reports
|
|
||||||
]
|
return optimized_reports
|
||||||
|
|
||||||
|
|
||||||
@router.post("/reports")
|
@router.post("/reports")
|
||||||
|
|
|
||||||
|
|
@ -401,13 +401,13 @@ export default function HistoryPage() {
|
||||||
|
|
||||||
// Auto-sync tracking ref
|
// Auto-sync tracking ref
|
||||||
const hasAutoSyncedRef = useRef(false);
|
const hasAutoSyncedRef = useRef(false);
|
||||||
const cloudReportsPromiseRef = useRef<Promise<any[]> | null>(null);
|
const cloudReportsPromiseRef = useRef<Promise<any[] | null> | null>(null);
|
||||||
|
|
||||||
const fetchCloudReportsCached = async (forceRefresh = false) => {
|
const fetchCloudReportsCached = async (forceRefresh = false) => {
|
||||||
if (forceRefresh || !cloudReportsPromiseRef.current) {
|
if (forceRefresh || !cloudReportsPromiseRef.current) {
|
||||||
cloudReportsPromiseRef.current = getCloudReports().catch(() => {
|
cloudReportsPromiseRef.current = getCloudReports().catch(() => {
|
||||||
cloudReportsPromiseRef.current = null;
|
cloudReportsPromiseRef.current = null;
|
||||||
return [];
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return cloudReportsPromiseRef.current;
|
return cloudReportsPromiseRef.current;
|
||||||
|
|
@ -483,6 +483,10 @@ export default function HistoryPage() {
|
||||||
|
|
||||||
// Get cloud reports
|
// Get cloud reports
|
||||||
const cloudReports = await fetchCloudReportsCached(true); // Force refresh
|
const cloudReports = await fetchCloudReportsCached(true); // Force refresh
|
||||||
|
if (!cloudReports) {
|
||||||
|
console.warn("☁️ Sync: Failed to fetch cloud reports. Aborting sync to prevent data loss.");
|
||||||
|
return; // Abort sync if fetching fails
|
||||||
|
}
|
||||||
const cloudKeys = new Set(cloudReports.map((r) => getReportSignature(r)));
|
const cloudKeys = new Set(cloudReports.map((r) => getReportSignature(r)));
|
||||||
const localKeys = new Set(allLocal.map((r) => getReportSignature(r)));
|
const localKeys = new Set(allLocal.map((r) => getReportSignature(r)));
|
||||||
|
|
||||||
|
|
@ -609,9 +613,10 @@ export default function HistoryPage() {
|
||||||
if (isAuthenticated && isCloudSyncEnabled()) {
|
if (isAuthenticated && isCloudSyncEnabled()) {
|
||||||
const cloudReports = await fetchCloudReportsCached();
|
const cloudReports = await fetchCloudReportsCached();
|
||||||
|
|
||||||
// Convert cloud reports to SavedReport format and filter by market type
|
if (cloudReports) {
|
||||||
const cloudFiltered = cloudReports
|
// Convert cloud reports to SavedReport format and filter by market type
|
||||||
.filter((r) => r.market_type === activeTab)
|
const cloudFiltered = cloudReports
|
||||||
|
.filter((r) => r.market_type === activeTab)
|
||||||
.map((r) => ({
|
.map((r) => ({
|
||||||
id: parseInt(r.id.replace(/-/g, "").slice(0, 8), 16), // Convert UUID to number
|
id: parseInt(r.id.replace(/-/g, "").slice(0, 8), 16), // Convert UUID to number
|
||||||
cloudId: r.id, // Keep cloud ID for deletion
|
cloudId: r.id, // Keep cloud ID for deletion
|
||||||
|
|
@ -649,6 +654,7 @@ export default function HistoryPage() {
|
||||||
setIsCloudData(true);
|
setIsCloudData(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} // Added missing closing brace for if (cloudReports)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter local data by language before display
|
// Filter local data by language before display
|
||||||
|
|
@ -684,7 +690,7 @@ export default function HistoryPage() {
|
||||||
if (isAuthenticated && isCloudSyncEnabled()) {
|
if (isAuthenticated && isCloudSyncEnabled()) {
|
||||||
const cloudReports = await fetchCloudReportsCached();
|
const cloudReports = await fetchCloudReportsCached();
|
||||||
|
|
||||||
if (cloudReports.length > 0) {
|
if (cloudReports && cloudReports.length > 0) {
|
||||||
// Get local reports to check for duplicates
|
// Get local reports to check for duplicates
|
||||||
const [usLocal, twseLocal, tpexLocal] = await Promise.all([
|
const [usLocal, twseLocal, tpexLocal] = await Promise.all([
|
||||||
getReportsByMarketType("us"),
|
getReportsByMarketType("us"),
|
||||||
|
|
@ -794,24 +800,29 @@ export default function HistoryPage() {
|
||||||
// 1. Delete from cloud: delete the specific report AND any other duplicates with the same key
|
// 1. Delete from cloud: delete the specific report AND any other duplicates with the same key
|
||||||
try {
|
try {
|
||||||
const allCloudReports = await fetchCloudReportsCached(true);
|
const allCloudReports = await fetchCloudReportsCached(true);
|
||||||
const matchingCloudIds = allCloudReports
|
if (allCloudReports) {
|
||||||
.filter((r) => {
|
const matchingCloudIds = allCloudReports
|
||||||
const lang = r.language || "zh-TW";
|
.filter((r) => {
|
||||||
return (
|
const lang = r.language || "zh-TW";
|
||||||
r.ticker === reportToDelete.ticker &&
|
return (
|
||||||
r.analysis_date === reportToDelete.analysis_date &&
|
r.ticker === reportToDelete.ticker &&
|
||||||
r.market_type === reportToDelete.market_type &&
|
r.analysis_date === reportToDelete.analysis_date &&
|
||||||
lang === (targetLang || "zh-TW")
|
r.market_type === reportToDelete.market_type &&
|
||||||
);
|
lang === (targetLang || "zh-TW")
|
||||||
})
|
);
|
||||||
.map((r) => r.id);
|
})
|
||||||
|
.map((r) => r.id);
|
||||||
|
|
||||||
if (matchingCloudIds.length > 0) {
|
if (matchingCloudIds.length > 0) {
|
||||||
console.log(`🗑️ Deleting ${matchingCloudIds.length} cloud report(s):`, matchingCloudIds);
|
console.log(`🗑️ Deleting ${matchingCloudIds.length} cloud report(s):`, matchingCloudIds);
|
||||||
await Promise.all(matchingCloudIds.map((id) => deleteCloudReport(id)));
|
await Promise.all(matchingCloudIds.map((id) => deleteCloudReport(id)));
|
||||||
|
} else if (cloudId) {
|
||||||
|
// Fallback: delete by cloudId if no match found by key
|
||||||
|
console.log("🗑️ Deleting from cloud by ID:", cloudId);
|
||||||
|
await deleteCloudReport(cloudId);
|
||||||
|
}
|
||||||
} else if (cloudId) {
|
} else if (cloudId) {
|
||||||
// Fallback: delete by cloudId if no match found by key
|
console.log("🗑️ Deleting from cloud by original ID because fetch failed:", cloudId);
|
||||||
console.log("🗑️ Deleting from cloud by ID:", cloudId);
|
|
||||||
await deleteCloudReport(cloudId);
|
await deleteCloudReport(cloudId);
|
||||||
}
|
}
|
||||||
} catch (cloudErr) {
|
} catch (cloudErr) {
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ interface GetCloudReportsOptions {
|
||||||
/**
|
/**
|
||||||
* Fetch reports from cloud with optional filtering and pagination
|
* Fetch reports from cloud with optional filtering and pagination
|
||||||
*/
|
*/
|
||||||
export async function getCloudReports(options?: GetCloudReportsOptions): Promise<CloudReport[]> {
|
export async function getCloudReports(options?: GetCloudReportsOptions): Promise<CloudReport[] | null> {
|
||||||
if (!isCloudSyncEnabled()) return [];
|
if (!isCloudSyncEnabled()) return [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -111,7 +111,7 @@ export async function getCloudReports(options?: GetCloudReportsOptions): Promise
|
||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch cloud reports:", error);
|
console.error("Failed to fetch cloud reports:", error);
|
||||||
return [];
|
return null; // Return null instead of [] to indicate fetch failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue