Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@
},
"options": {
"typeAware": true
},
"rules": {
"no-extra-boolean-cast": "off"
}
}
18 changes: 17 additions & 1 deletion precomputer/src/tasks/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,23 @@ export const getClusterPackage = async ({ api, clusterNumber }: Props): Promise<
rfcNumber,
disposition,
references: references.map((reference): ClusterDocumentCommon["references"][number] => {
return reference
const {
relationship,
draftName,
targetDraftName,
sourceRfcNumber,
targetRfcNumber,
targetDisposition
} = reference

return {
relationship,
draftName,
targetDraftName,
sourceRfcNumber,
targetRfcNumber,
targetDisposition
}
}),
isReceived,
isNormRef,
Expand Down
125 changes: 10 additions & 115 deletions website/app/components/DocumentDependenciesGraph.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,17 @@
<form class="text-right mt-1">
<label class="cursor-pointer font-bold inline-block" :data-checked="showLegend">
<input type="checkbox" name="showLegend" class="size-4 mr-1 align-middle" value="ok show the legend"
:v-model="showLegend" @change="handleChange" :aria-controls="GRAPH_DOM_ID" :aria-expanded="showLegend" />
:v-model="showLegend" @change="handleChangeLegend" :aria-controls="GRAPH_DOM_ID" :aria-expanded="showLegend" />
Show legend?
</label>
</form>

<details>
<summary>show debug</summary>
<pre v-if="clusterGraphData">
{{ JSON.stringify(clusterGraphData, null, 2) }}
</pre>
</details>
</template>

<script setup lang="ts">
Expand Down Expand Up @@ -103,7 +110,7 @@ const setTooltip: SetTooltip = (props) => {
/**
* This will be shown visually, but it's mostly for screenreaders
*/
const handleChange = (event: Event) => {
const handleChangeLegend = (event: Event) => {
const { target } = event
if (!(target instanceof HTMLInputElement)) {
const errorTitle = 'Expected native input[type=checkbox] but was'
Expand All @@ -124,119 +131,7 @@ const showLegend = ref(false)

const hasMounted = ref(false)

const clusterGraphData = computed(() => {
const newClusterGraphData: DataParam = {
links: [],
nodes: []
}

const isNodeParam = (data: unknown): data is NodeParam => {
const isANode = Boolean((data && typeof data === 'object' && 'id' in data))
if (!isANode) {
console.log("!IS A NODE", isANode, data)
}
return isANode
}

const isLinkParam = (data: unknown): data is LinkParam => {
return Boolean((data && typeof data === 'object' && 'source' in data && 'target' in data && 'rel' in data))
}

let referenceNodes: NodeParam[] = []

newClusterGraphData.nodes.push(
...(clusterToUse.value.documents ?? []).flatMap((clusterMember): NodeParam[] | null => {
const { name, rfcNumber, disposition, references, isReceived, isBlocked } = clusterMember

const hasNormRef = references ? references.length > 0 : undefined
const hasNormRefInQueue = references ? references.some(reference => reference.relationship === 'refqueue') : undefined
const hasNormRefBlocked = references ? references.some(reference => {
if (!reference.targetDraftName || !clusterToUse.value.documents) {
return
}
const targetDocument = clusterToUse.value.documents.find(doc => doc.name === reference.targetDraftName)
if (!targetDocument) {
referenceNodes.push({
id: reference.targetDraftName,
url: datatrackerDraftUrlBuilder(reference.targetDraftName),
isNormRef: true,
})
return true
}
return Boolean(targetDocument.isBlocked)
}) : undefined

const rfcToBeToNodeParam = (rfcToBe: ClusterDocumentCommon, partialNodeParam: Partial<NodeParam>): NodeParam | undefined => {
const { name, disposition } = rfcToBe
if (!name) {
console.warn("rfcToBe had no name?", rfcToBe)
return
}

return {
id: name,
rfcNumber: rfcToBe.rfcNumber ?? undefined,
url: datatrackerDraftUrlBuilder(name),
disposition: parseDisposition(disposition),
...partialNodeParam,
}
}

referenceNodes.push(...(references ?? []).flatMap(reference => {
const { draftName, targetDraftName } = reference
const draft = draftName ? clusterToUse.value.documents.find(doc => doc.name === draftName) : undefined
const target = targetDraftName ? clusterToUse.value.documents.find(doc => doc.name === targetDraftName) : undefined

return [
draft ? rfcToBeToNodeParam(draft, { isNormRef: false }) : draftName ? { id: draftName, url: datatrackerDraftUrlBuilder(draftName), isNormRef: false } : undefined,
target ? rfcToBeToNodeParam(target, {
isNormRef: true, // all targets are norm refs
}) : targetDraftName ? { id: targetDraftName, url: datatrackerDraftUrlBuilder(targetDraftName), isNormRef: true } : undefined,
].filter(isNodeParam)
}))

return [{
id: name,
url: datatrackerDraftUrlBuilder(name),
rfcNumber,
isReceived,
disposition,
isBlocked,
isNormRef: false,
hasNormRef,
hasNormRefInQueue,
hasNormRefBlocked
}]
}).filter(isNodeParam)
)

referenceNodes = referenceNodes.filter(
// only include reference nodes if they weren't already mentioned
referenceNode => !newClusterGraphData.nodes.some(graphDataNode => graphDataNode.id === referenceNode.id)
)
newClusterGraphData.nodes.push(...referenceNodes)

newClusterGraphData.links.push(
...(clusterToUse.value.documents ?? []).flatMap((clusterMember): LinkParam[] | null => {
const { references } = clusterMember

return references.map((reference): LinkParam => {
const { draftName, targetDraftName, relationship } = reference

return {
source: draftName,
target: targetDraftName,
rel: relationship,
}
})
}).filter(isLinkParam)
)

newClusterGraphData.nodes = uniqBy(newClusterGraphData.nodes, (node) => node.id)
newClusterGraphData.links = uniqBy(newClusterGraphData.links, (link) => JSON.stringify([link.source, link.target, link.rel]))

return newClusterGraphData
})
const clusterGraphData = computed(() => getClusterGraphData(clusterGraphData.value))

const attemptToRenderGraph = () => {
const { value: container } = containerDOMRef
Expand Down
105 changes: 103 additions & 2 deletions website/app/utils/document_relations-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { startCase } from "es-toolkit";
import { startCase, uniqBy } from 'es-toolkit'
import { type ClusterPackageCommon } from './validators'

/**
* These constants were calculated from DOM Bootstrap CSS variables
Expand All @@ -7,7 +8,7 @@ import { startCase } from "es-toolkit";
*/
export const font_size = 14
export const line_height = font_size + 2
export const font_family = 'Arial,sans-serif';
export const font_family = 'Arial,sans-serif'
export const font = `${font_size}px ${font_family}`

export const green = "#198754"
Expand Down Expand Up @@ -203,7 +204,14 @@ const makeTooltip = (node: NodeParam): string[] | undefined => {
* based on https://docs.google.com/spreadsheets/d/1WoPNZiFf9Hx4Qc6N5UE1-RKhYYNybBeCYZM72wL5TSM/edit?gid=0#gid=0
*/
export const getCircleTheme = (node: NodeParam): CircleTheme => {
if (node.rfcNumber === 9846) {
console.log(9846,
Boolean(node.isReceived) && Boolean(node.isNormRef) && !Boolean(node.hasNormRef) && !Boolean(node.isBlocked),
node)
}

if (Boolean(node.isReceived) && Boolean(node.isNormRef) && !Boolean(node.hasNormRef) && !Boolean(node.isBlocked) && node.disposition === 'in_progress') {
console.log('blue?')
return {
fill: blue,
textColor: black,
Expand Down Expand Up @@ -261,6 +269,7 @@ export const getCircleTheme = (node: NodeParam): CircleTheme => {
}
}


return {
fill: black,
textColor: white,
Expand Down Expand Up @@ -338,3 +347,95 @@ export const legendData: DataParam = {
{ id: 'draft-not-received-3g', isReceived: false, isNormRef: true }, // Not received, 3g
],
};


export const getClusterGraphData = (cluster: ClusterPackageCommon['cluster']) => {
const newClusterGraphData: DataParam = {
links: [],
nodes: []
}

const isNodeParam = (data: unknown): data is NodeParam => {
const isANode = Boolean((data && typeof data === 'object' && 'id' in data))
if (!isANode) {
console.log("!IS A NODE", isANode, data)
}
return isANode
}

const isLinkParam = (data: unknown): data is LinkParam => {
return Boolean((data && typeof data === 'object' && 'source' in data && 'target' in data && 'rel' in data))
}

let referenceNodes: NodeParam[] = []

newClusterGraphData.nodes.push(
...(cluster.documents ?? []).flatMap((clusterMember): NodeParam[] | null => {
const { name, rfcNumber, disposition, references, isReceived, isBlocked, isNormref } = clusterMember

const hasNormRef = references ? references.length > 0 : undefined
const hasNormRefInQueue = references ? references.every(reference => reference.relationship === 'refqueue') : undefined
const hasNormRefBlocked = references ? references.some(reference => reference.targetIsBlocked || !reference.targetIsReceived) : undefined

referenceNodes.push(...(references ?? []).flatMap((reference): NodeParam[] => {
const { targetDraftName, targetIsReceived, targetRfcNumber, targetDisposition } = reference
// source node is always a cluster member already in the graph; only create target nodes
if (!targetDraftName) return []
return [{
id: targetDraftName,
url: `/docs/${targetDraftName}`,
isNormRef: true,
isReceived: targetIsReceived ?? false,
rfcNumber: targetRfcNumber ?? undefined,
disposition: parseDisposition(targetDisposition),
}]
}))

return [{
id: name,
url: `/docs/${name}`,
rfcNumber: rfcNumber ?? undefined,
isReceived: isReceived ?? undefined,
disposition: parseDisposition(disposition),
isBlocked,
isNormRef: isNormref ?? false,
hasNormRef,
hasNormRefInQueue,
hasNormRefBlocked
}]
}).filter(isNodeParam)
)

referenceNodes = referenceNodes.filter(
// only include reference nodes if they weren't already mentioned
referenceNode => !newClusterGraphData.nodes.some(graphDataNode => graphDataNode.id === referenceNode.id)
)
newClusterGraphData.nodes.push(...referenceNodes)

newClusterGraphData.links.push(
...(cluster.documents ?? []).flatMap((clusterMember): LinkParam[] | null => {
const { references } = clusterMember

return references ? references.map((reference): LinkParam | null => {
const { draftName, targetDraftName, relationship } = reference

if (draftName === undefined || targetDraftName === undefined) {
console.warn("Graph: cluster reference", reference, " has undefined name(s)")
return null
}

return {
source: draftName,
target: targetDraftName,
rel: parseRelationship(relationship),
}
}).filter(isLinkParam) : null
}).filter(isLinkParam)
)


newClusterGraphData.nodes = uniqBy(newClusterGraphData.nodes, (node) => node.id)
newClusterGraphData.links = uniqBy(newClusterGraphData.links, (link) => JSON.stringify([link.source, link.target, link.rel]))

return newClusterGraphData
}
Loading