Skip to content

Add the first production-ready version of the external project overview - #1185

Open
nabalma wants to merge 8 commits into
masterfrom
external-project-overview-first-release
Open

Add the first production-ready version of the external project overview#1185
nabalma wants to merge 8 commits into
masterfrom
external-project-overview-first-release

Conversation

@nabalma

@nabalma nabalma commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR introduces the first production-ready version of the External Project Overview,
Review comments from the previous stacked PRs have been addressed and incorporated in this PR.

This PR includes :

  • External project navigation, overview and details pages.
  • Submissions and readsets tabs with dashboards, filters and CSV exports.
  • Weighted quality, alignment and duplication metrics.
  • Reference genome assembly names and readable readset file exports.

Review comments from the previous stacked PRs have been addressed and incorporated.

@nafiz1001

nafiz1001 commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

It might be time to Close (with comment) PRs #1178, #1179, #1180 and #1181

Done (PRs Closed) ....

Comment on lines +32 to +33
def _get_readsets_queryset(self, parent_project):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a type annotation would be nice parent_project: ParentProject

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Fixed ...

Comment on lines +119 to +120
@action(detail=True, methods=["get"],url_path="readsets")
def overview_readsets(self, request, pk=None):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space after comma (methods=["get"],)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +621 to +660
class ParentProjectReadsetSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
readset_sample_name = serializers.CharField()
biosample_id = serializers.IntegerField(allow_null=True)
external_id = serializers.CharField()
run_name = serializers.CharField()
lane = serializers.IntegerField()
reference_genome_id = serializers.IntegerField(allow_null=True,)
reference_genome_assembly_name = serializers.CharField(allow_null=True,)
sequencing_index_name = serializers.CharField(allow_null=True)
run_start_date = serializers.DateField()
alias = serializers.CharField(allow_null=True)
cohort = serializers.CharField(allow_blank=True,allow_null=True,)
library_type = serializers.CharField(allow_null=True,)
container_barcodes = serializers.ListField(child=serializers.CharField(allow_null=True),allow_empty=True,)
number_of_reads = serializers.IntegerField(allow_null=True,)
number_of_bases = serializers.IntegerField(allow_null=True,)
average_quality = serializers.DecimalField(
max_digits=40,
decimal_places=20,
allow_null=True,
)

pf_reads_aligned = serializers.DecimalField(
max_digits=40,
decimal_places=20,
allow_null=True,
)

duplicate_aligned = serializers.DecimalField(
max_digits=40,
decimal_places=20,
allow_null=True,
)
readset_files = serializers.ListField(
child=serializers.DictField(),
required=False,
)
run_validation_status = serializers.IntegerField(allow_null=True,)

@nafiz1001 nafiz1001 Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@UlysseFG Personally I feel this serializer should be a composition of models rather than individual fields. Like we could have a field for parent_project, derived_sample, readset, etc. So that way, we know where each fields come from.

Comment on lines +33 to +39
const openCount = data.filter((project) => project.status === "Open").length

const uniquePIs = new Set(data.map((project) => project.principal_investigator).filter(Boolean))
.size

const uniqueRequestors = new Set(data.map((project) => project.requestor_name).filter(Boolean))
.size

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be memoized. Otherwise, they would get recomputed at each re-render of the component.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Especially if these values are passed to child components.

@nabalma nabalma Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed ...

const ExternalIDProjectsDashboard = ({ data }: ExternalIDProjectsDashboardProps) => {
  const total = data.length

  const openCount = useMemo(() => data.filter((project) => project.status === "Open").length, [data])

  const uniquePIs = useMemo(() => new Set(data.map((project) => project.principal_investigator).filter(Boolean)).size, [data])

  const uniqueRequestors = useMemo(() => new Set(data.map((project) => project.requestor_name).filter(Boolean)).size, [data])

Comment on lines +167 to +174
const iconStyle = (color: string, backgroundColor: string): React.CSSProperties => ({
color,
backgroundColor,
fontSize: 18,
padding: 6,
borderRadius: 8,
marginRight: 4,
})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be outside the component definition.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This style is now out of the component definition ...

const iconStyle = (color: string, backgroundColor: string): React.CSSProperties => ({
  color,
  backgroundColor,
  fontSize: 18,
  padding: 6,
  borderRadius: 8,
  marginRight: 4,
})

function ExternalIDReadSetDashboard({ readsets }: { readsets: ProjectOverviewReadset[] }) {

Comment on lines +469 to +484
<Route
path="/external-projects-overview/*"
element={
<PrivateNavigate>
<ExternalProjectsPage />
</PrivateNavigate>
}
/>
<Route
path="/external-projects-overview/:parentProjectId"
element={
<PrivateNavigate>
<ExternalProjectDetailsPage />
</PrivateNavigate>
}
/>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We normally have nested Routes for specific pages like with frontend/src/components/projects/ProjectsPage.js. But since it's just two pages for now, we can keep it like this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted ...

Comment on lines +242 to +263
expandedRowRender: (parentProject) => {
const internalProjects = (parentProject.projects ?? []).reduce<FMSProject[]>(
(projects, projectID) => {
const project = internalProjectsByID[projectID]
if (project) {
projects.push(project)
}
return projects
},
[],
)
return (
<Table
size="small"
rowKey="id"
dataSource={internalProjects}
columns={internalProjectColumns}
pagination={false}
/>
)
},
}}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes me a little uncomfortable because dataSource is getting generated on the fly without memoization. But, so far, the performance is fine, so it's not so important to address this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants