Skip to content

Commit

Permalink
οΈπŸ›ŸπŸ‘ ↝ [SSM-135 SSM-130 SSM-138]: Cloud classification aggregator added
Browse files Browse the repository at this point in the history
  • Loading branch information
Gizmotronn committed Mar 2, 2025
1 parent a0b5ab7 commit cc1d570
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 38 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ down-full:

deploy-test:
docker-compose build && yarn build && vercel

up-light:
docker-compose up -d
78 changes: 41 additions & 37 deletions app/planets/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"use client";
'use client';

import React, { useEffect, useState } from "react";
import { useSession, useSupabaseClient } from "@supabase/auth-helpers-react";
import Navbar from "@/components/Layout/Navbar";
import { PostCardSingleWithGenerator } from "@/content/Posts/PostWithGen";
import ClassificationComments from "@/content/Classifications/ClassificationStats";
import CloudClassificationSummary from "@/components/Structures/Missions/Meteorologists/Cloudspotting/CloudAggregator";

interface Classification {
id: number;
Expand All @@ -21,7 +22,7 @@ interface Classification {
tags?: string[];
images?: string[];
relatedClassifications?: Classification[];
};
}

type Anomaly = {
id: number;
Expand Down Expand Up @@ -67,7 +68,6 @@ export default function ClassificationDetail({ params }: { params: { id: string
setClassification(data);
setAnomaly(data.anomaly);

// Debugging step: Log the fetched classification
console.log("Fetched classification:", data);

const parentPlanetLocation = data.anomaly?.id;
Expand All @@ -84,7 +84,6 @@ export default function ClassificationDetail({ params }: { params: { id: string
return;
}

// Debugging step: Log the related classifications
console.log("Related classifications:", relatedData);

if (relatedData) {
Expand All @@ -110,6 +109,11 @@ export default function ClassificationDetail({ params }: { params: { id: string
if (error) return <p className="text-red-500">{error}</p>;
if (!classification) return <p>Classification not found.</p>;

// Filter out only the classifications with classificationtype = "cloud"
const cloudClassifications = classification.relatedClassifications?.filter(
(related) => related.classificationtype === "cloud"
);

return (
<div className="p-6 bg-black text-white border border-gray-200 rounded-md shadow-md">
<Navbar />
Expand All @@ -131,42 +135,42 @@ export default function ClassificationDetail({ params }: { params: { id: string
classificationType={classification.classificationtype || "Unknown"}
/>
)}

{classification.relatedClassifications && classification.relatedClassifications.length > 0 && (

{/* Pass only cloud classifications to the CloudClassificationSummary component */}
{cloudClassifications && cloudClassifications.length > 0 && (
<CloudClassificationSummary
classifications={cloudClassifications}
/>
)}

{/* Other related classifications section */}
{classification.relatedClassifications && (
<div className="mt-6">
<h3 className="text-xl font-bold">Related Classifications</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mt-4">
{classification.relatedClassifications.map((related: Classification) => (
<div
key={related.id}
className="p-4 border border-gray-200 rounded-md shadow-md bg-[#2C4F64]"
>
<h4 className="font-bold text-lg">Classification #{related.id}</h4>
<p className="mt-2 text-sm">{related.anomaly?.content || "No anomaly content"}</p>

{/* Displaying media */}
{related.media && related.media.length > 0 && (
<div className="mt-2">
{related.media.map((media, index) => (
<img
key={index}
src={typeof media === "string" ? media : media.uploadUrl}
alt={`Related Classification #${related.id} - Image ${index + 1}`}
className="w-full h-auto rounded-md"
/>
))}
</div>
)}

{/* Displaying classification configuration */}
{related.classificationConfiguration && (
<div className="mt-2 p-2 bg-gray-800 text-white rounded-md">
<pre>{JSON.stringify(related.classificationConfiguration, null, 2)}</pre>
</div>
)}
{classification.relatedClassifications.map((related) => (
<div key={related.id} className="mt-4">
<h4 className="text-lg font-semibold">{related.classificationtype}</h4>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mt-2">
<div className="p-4 border border-gray-200 rounded-md shadow-md bg-[#2C4F64]">
<h4 className="font-bold text-lg">Classification #{related.id}</h4>
<p className="mt-2 text-sm">{related.anomaly?.content || "No anomaly content"}</p>

{related.media && related.media.length > 0 && (
<div className="mt-2">
{related.media.map((media, index) => (
<img
key={index}
src={typeof media === "string" ? media : media.uploadUrl}
alt={`Related Classification #${related.id} - Image ${index + 1}`}
className="w-full h-auto rounded-md"
/>
))}
</div>
)}
</div>
</div>
))}
</div>
</div>
))}
</div>
)}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from "react";

type Anomaly = {
id: number;
content: string | null;
anomalytype: string | null;
mass: number | null;
radius: number | null;
density: number | null;
gravity: number | null;
temperature: number | null;
orbital_period: number | null;
avatar_url: string | null;
created_at: string;
};

interface Classification {
id: number;
content: string | null;
author: string | null;
anomaly: Anomaly | null;
media: (string | { uploadUrl?: string })[] | null;
classificationtype: string | null;
classificationConfiguration?: any;
created_at: string;
title?: string;
votes?: number;
category?: string;
tags?: string[];
images?: string[];
relatedClassifications?: Classification[];
annotationOptions?: any[]; // Add annotationOptions to the Classification type to avoid errors
};

export interface SatellitePlanetFourClassification extends Classification {
annotationOptions: any[];
classificationConfiguration?: any; // Ensure it's optional here if required
}


interface SatellitePlanetFourAggregatorProps {
classifications: SatellitePlanetFourClassification[];
}

const SatellitePlanetFourAggregator: React.FC<SatellitePlanetFourAggregatorProps> = ({ classifications }) => {
return (
<div className="mt-6">
<h3 className="text-xl font-bold">Satellite Planet Four Classifications</h3>
{classifications.length === 0 ? (
<p>No satellite classifications available.</p>
) : (
classifications.map((classification) => (
<div key={classification.id} className="mt-4 p-4 border border-gray-200 rounded-md shadow-md bg-[#2C4F64]">
<h4 className="font-bold text-lg">Classification #{classification.id}</h4>
<p className="mt-2 text-sm">Annotation Options: {classification.annotationOptions.join(", ")}</p>
{classification.classificationConfiguration && (
<pre className="bg-gray-800 text-white p-2 rounded-md">
{JSON.stringify(classification.classificationConfiguration, null, 2)}
</pre>
)}
</div>
))
)}
</div>
);
};

export default SatellitePlanetFourAggregator;
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
"use client";

import React, { useState } from "react";

interface Classification {
id: number;
classificationConfiguration?: ClassificationConfiguration;
}

interface ClassificationConfiguration {
createdBy?: number | null;
activePlanet?: number;
additionalFields?: Record<string, any>;
annotationOptions?: string[];
parentPlanetLocation?: string;
classificationOptions?: Record<string, Record<string, boolean>>;
}

interface AggregatedCloud {
annotationOptions: Record<string, number>;
classificationOptions: Record<string, Record<string, number>>;
additionalFields: Record<string, Set<string>>;
}

const aggregateCloudClassifications = (
classifications: Classification[]
): AggregatedCloud => {
const aggregated: AggregatedCloud = {
annotationOptions: {},
classificationOptions: {},
additionalFields: {},
};

classifications.forEach(({ classificationConfiguration }) => {
if (!classificationConfiguration) return;

classificationConfiguration.annotationOptions?.forEach((option) => {
const match = option.match(/(.+?)\s*\(x(\d+)\)/);
if (match) {
const [, name, count] = match;
aggregated.annotationOptions[name] =
(aggregated.annotationOptions[name] || 0) + parseInt(count);
} else {
aggregated.annotationOptions[option] =
(aggregated.annotationOptions[option] || 0) + 1;
}
});

Object.entries(classificationConfiguration.classificationOptions || {}).forEach(
([category, values]) => {
if (!aggregated.classificationOptions[category]) {
aggregated.classificationOptions[category] = {};
}
Object.entries(values).forEach(([key, value]) => {
if (value) {
aggregated.classificationOptions[category][key] =
(aggregated.classificationOptions[category][key] || 0) + 1;
}
});
}
);

Object.entries(classificationConfiguration.additionalFields || {}).forEach(
([key, value]) => {
if (!aggregated.additionalFields[key]) {
aggregated.additionalFields[key] = new Set();
}
aggregated.additionalFields[key].add(value);
}
);
});

return aggregated;
};

const CloudClassificationSummary: React.FC<{ classifications: Classification[] }> = ({
classifications,
}) => {
const aggregatedData = aggregateCloudClassifications(classifications);
const [showAggregated, setShowAggregated] = useState(true);

return (
<div className="p-4 border border-gray-200 rounded-md shadow-md bg-[#2C4F64] text-white">
<div className="flex justify-between items-center">
<h3 className="text-xl font-bold">Cloud Classification Summary</h3>
<button
onClick={() => setShowAggregated(!showAggregated)}
className="px-3 py-1 bg-gray-700 hover:bg-gray-600 rounded-md"
>
{showAggregated ? "Show Individual" : "Show Aggregated"}
</button>
</div>

{showAggregated ? (
<div>
<div className="mt-4">
<h4 className="font-bold">Annotation Options</h4>
<ul>
{Object.entries(aggregatedData.annotationOptions).map(([option, count]) => (
<li key={option}>
{option} (x{count})
</li>
))}
</ul>
</div>

<div className="mt-4">
<h4 className="font-bold">Classification Options</h4>
<ul>
{Object.entries(aggregatedData.classificationOptions).map(([category, values]) => (
<li key={category}>
<strong>{category || "General"}</strong>:{" "}
{Object.entries(values)
.map(([key, count]) => `${key} (x${count})`)
.join(", ")}
</li>
))}
</ul>
</div>

<div className="mt-4">
<h4 className="font-bold">Additional Fields</h4>
<ul>
{Object.entries(aggregatedData.additionalFields).map(([key, values]) => (
<li key={key}>
<strong>{key}</strong>: {[...values].join(", ")}
</li>
))}
</ul>
</div>
</div>
) : (
<div className="mt-4">
<h4 className="font-bold">Individual Classifications</h4>
{classifications.map((classification) => (
<div key={classification.id} className="p-3 border border-gray-300 rounded-md mt-2">
<strong>Classification #{classification.id}</strong>
<pre className="mt-2 bg-gray-800 p-2 rounded-md text-xs">
{JSON.stringify(classification.classificationConfiguration, null, 2)}
</pre>
</div>
))}
</div>
)}
</div>
);
};

export default CloudClassificationSummary;
2 changes: 1 addition & 1 deletion components/Structures/Missions/PickVehicle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const automatons: Vehicle[] = [
id: "1",
name: "Visual Satellite",
description: "Use this to find landmarks and sites on the planet you chose. Recommended for Planet Four & Cloudspotting",
image: '/assets/Automatons/ExploreRover1.png', // Need to find a new asset for satellites; link to automaton entry in db table and api route
image: '/assets/Automatons/Sat.png', // Need to find a new asset for satellites; link to automaton entry in db table and api route
stats: {
speed: 65,
armor: 30,
Expand Down
Binary file modified public/assets/Automatons/ExploreRover1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/assets/Automatons/Sat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit cc1d570

Please sign in to comment.