Skip to content

Commit

Permalink
Added vetted licenses status toggle & filter
Browse files Browse the repository at this point in the history
Signed-off-by: Omkar Phansopkar <[email protected]>
  • Loading branch information
OmkarPh committed Oct 21, 2023
1 parent 8a217c8 commit 1545ce5
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 44 deletions.
11 changes: 10 additions & 1 deletion src/pages/Home/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,16 @@ const Home = () => {
}

function historyItemParser(historyItem: HistoryItem) {
if (historyItem.json_path) {
// During development, re-parse JSON file (Possible schema changes)
// Toggle this if you don't have schema changes & wish to save time in imports
const REIMPORT_JSON_IN_DEV = true;

if (
REIMPORT_JSON_IN_DEV &&
process.env.NODE_ENV === "development" &&
historyItem.json_path
) {
console.log("Re-import JSON file (Dev mode)");
if (!electronFs.existsSync(historyItem.json_path)) {
return reportInvalidEntry(historyItem, "JSON");
}
Expand Down
26 changes: 21 additions & 5 deletions src/pages/Licenses/Licenses.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,13 @@
padding-right: 12px;
}

.licenses-navigator-container .search-box {
width: 98%;
margin-bottom: 12px;
.licenses-navigator-container .filter-group {
margin-bottom: 10px;
padding-right: 5px;
}

.licenses-navigator-container .filter-group>div {
margin-bottom: 4px;
}

.licenses-navigator-container .search-box input {
Expand Down Expand Up @@ -89,7 +93,8 @@

.license-item>div {
padding: 2px;
padding-right: 4px;
padding-top: 4px;
padding-bottom: 0;
display: table-cell;
vertical-align: top;
}
Expand All @@ -100,11 +105,22 @@
word-break: break-word;
}


.license-item>.vet-toggle,
.license-item>.license-count {
width: 38px;
}

.license-item>.vet-toggle {
padding-top: 2px;
padding-right: 7px;
padding-right: 10px;
}

.license-item>.license-count {
padding-top: 6px;
}

.license-item>.vet-toggle>*,
.license-item>.license-count>* {
float: right;
}
167 changes: 132 additions & 35 deletions src/pages/Licenses/Licenses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import {
ListGroup,
ListGroupItem,
} from "react-bootstrap";
import { toast } from "react-toastify";
import { ThreeDots } from "react-loader-spinner";
import { useSearchParams } from "react-router-dom";
import Select from "react-select";

import LicenseEntity from "../../components/LicenseEntity/LicenseEntity";
import NoDataFallback from "../../components/NoDataSection";
Expand All @@ -18,16 +20,20 @@ import {
ActiveLicenseEntity,
LicenseClueDetails,
LicenseDetectionDetails,
VET_OPTIONS,
VetOption,
} from "./licenseDefinitions";
import { LicenseTypes } from "../../services/workbenchDB.types";

import "./Licenses.css";

const LicenseDetections = () => {
const [searchParams] = useSearchParams();
const [activeLicense, setActiveLicense] = useState<ActiveLicenseEntity | null>(
null
);
const [activeLicense, setActiveLicense] =
useState<ActiveLicenseEntity | null>(null);
const [searchedLicense, setSearchedLicense] = useState("");
const [vetFilter, setVetFilter] = useState<VetOption>(VET_OPTIONS.ALL);
const [vettedLicenses, setVettedLicenses] = useState<Set<string>>(new Set());

function activateLicenseDetection(licenseDetection: LicenseDetectionDetails) {
if (!licenseDetection) return;
Expand Down Expand Up @@ -59,17 +65,15 @@ const LicenseDetections = () => {
const newLicenseDetections: LicenseDetectionDetails[] = (
await db.getAllLicenseDetections()
).map((detection) => ({
id: Number(detection.getDataValue("id")),
vetted: detection.getDataValue("vetted"),
detection_count: Number(detection.getDataValue("detection_count")),
identifier: detection.getDataValue("identifier") || null,
license_expression: detection
.getDataValue("license_expression")
,
license_expression: detection.getDataValue("license_expression"),
detection_log: JSON.parse(
detection.getDataValue("detection_log") || "[]"
),
matches: JSON.parse(
detection.getDataValue("matches") || "[]"
),
matches: JSON.parse(detection.getDataValue("matches") || "[]"),
file_regions: JSON.parse(
detection.getDataValue("file_regions") || "[]"
),
Expand All @@ -81,27 +85,39 @@ const LicenseDetections = () => {
).map((clue) => {
return {
id: Number(clue.getDataValue("id")),
// @TODO - Find better way to have unique identifier for each clue
identifier: `clue-${
clue.getDataValue("license_expression") || ""
}-${Number(clue.getDataValue("id"))}`,
vetted: clue.getDataValue("vetted"),
fileId: Number(clue.getDataValue("fileId")),
filePath: clue.getDataValue("filePath") || "",
fileClueIdx: Number(clue.getDataValue("fileClueIdx")),
score:
clue.getDataValue("score") !== null
? Number(clue.getDataValue("score"))
: null,
license_expression:
clue.getDataValue("license_expression") || null,
rule_identifier:
clue.getDataValue("rule_identifier") || null,
matches: JSON.parse(
clue.getDataValue("matches") || "[]"
),
file_regions: JSON.parse(
clue.getDataValue("file_regions") || "[]"
),
license_expression: clue.getDataValue("license_expression") || null,
rule_identifier: clue.getDataValue("rule_identifier") || null,
matches: JSON.parse(clue.getDataValue("matches") || "[]"),
file_regions: JSON.parse(clue.getDataValue("file_regions") || "[]"),
};
});
setLicenseClues(newLicenseClues);

const newVettedLicenses = new Set<string>();
newLicenseDetections.forEach((detection) => {
if (detection.vetted) {
newVettedLicenses.add(detection.identifier);
}
});
newLicenseClues.forEach((clue) => {
if (clue.vetted) {
newVettedLicenses.add(clue.identifier);
}
});
setVettedLicenses(newVettedLicenses);

const queriedDetectionIdentifier: string | null = searchParams.get(
QUERY_KEYS.LICENSE_DETECTION
);
Expand Down Expand Up @@ -154,6 +170,46 @@ const LicenseDetections = () => {
})().then(endProcessing);
}, []);

const handleItemToggle = (
license: LicenseDetectionDetails | LicenseClueDetails,
licenseType: LicenseTypes,
newVettedStatus: boolean
) => {
function updateVettedLicenseStatus(identifier: string, newStatus: boolean) {
setVettedLicenses((prevVettedLicenses) => {
const newVettedLicenses = new Set(prevVettedLicenses);
if (newStatus) newVettedLicenses.add(identifier);
else newVettedLicenses.delete(identifier);
return newVettedLicenses;
});
}

// const newVettedStatus = !vettedLicenses.has(license.identifier);
db.toggleLicenseVettedStatus(
license.id,
licenseType,
newVettedStatus
).catch((err) => {
// Revert vetted status in UI if DB update fails
updateVettedLicenseStatus(license.identifier, !newVettedStatus);
console.log("Error updating vetted status: ", err);
toast.error("Couldn't update vetted license status!");
});

updateVettedLicenseStatus(license.identifier, newVettedStatus);
};

const shownByVetFilter = (
license: LicenseDetectionDetails | LicenseClueDetails
) => {
if (vetFilter === VET_OPTIONS.ALL) return true;
if (vetFilter === VET_OPTIONS.VETTED)
return vettedLicenses.has(license.identifier);
if (vetFilter === VET_OPTIONS.UNVETTED)
return !vettedLicenses.has(license.identifier);
return true;
};

if (!licenseDetections || !licenseClues) {
return (
<ThreeDots
Expand All @@ -169,7 +225,7 @@ const LicenseDetections = () => {
}

const totalLicenses = licenseDetections.length + licenseClues.length;
const DEFAULT_SECTION_HEIGHT_CAP = 0.8;
const DEFAULT_SECTION_HEIGHT_CAP = 0.75;
const capSectionSize = (ratio: number) => {
return (
Math.max(
Expand All @@ -191,22 +247,29 @@ const LicenseDetections = () => {

return (
<div>
<h4 className="licenses-title">License explorer</h4>
<Allotment className="license-container">
<Allotment.Pane
snap
minSize={200}
preferredSize="30%"
className="licenses-navigator-container"
>
<InputGroup className="search-box">
<Form.Control
aria-label="Search"
type="search"
placeholder="Search licenses"
onChange={(e) => setSearchedLicense(e.target.value)}
<div className="filter-group">
<Select
defaultValue={VET_OPTIONS.ALL}
onChange={(newVetFilter) => setVetFilter(newVetFilter)}
isMulti={false}
options={Object.values(VET_OPTIONS)}
/>
</InputGroup>
<InputGroup className="search-box">
<Form.Control
aria-label="Search"
type="search"
placeholder="Search licenses"
onChange={(e) => setSearchedLicense(e.target.value)}
/>
</InputGroup>
</div>
<Allotment vertical snap={false} minSize={90}>
<Allotment.Pane
preferredSize={licenseDetectionsSectionSize}
Expand All @@ -226,9 +289,11 @@ const LicenseDetections = () => {
activeLicense &&
activeLicense.type === "detection" &&
activeLicense.license === licenseDetection;
const showDetection = licenseDetection.license_expression
.toLowerCase()
.includes(searchedLicense.toLowerCase());
const showDetection =
shownByVetFilter(licenseDetection) &&
licenseDetection.license_expression
.toLowerCase()
.includes(searchedLicense.toLowerCase());

return (
<ListGroupItem
Expand Down Expand Up @@ -258,6 +323,22 @@ const LicenseDetections = () => {
{licenseDetection.detection_count}
</Badge>
</div>
<div className="vet-toggle">
<Form.Check
type="checkbox"
checked={vettedLicenses.has(
licenseDetection.identifier
)}
onChange={(e) =>
handleItemToggle(
licenseDetection,
LicenseTypes.DETECTION,
e.target.checked
)
}
onClick={(e) => e.stopPropagation()}
></Form.Check>
</div>
</div>
</ListGroupItem>
);
Expand All @@ -277,14 +358,16 @@ const LicenseDetections = () => {
activeLicense &&
activeLicense.type === "clue" &&
activeLicense.license === licenseClue;
const showClue = licenseClue.license_expression
.toLowerCase()
.includes(searchedLicense.toLowerCase());
const showClue =
shownByVetFilter(licenseClue) &&
licenseClue.license_expression
.toLowerCase()
.includes(searchedLicense.toLowerCase());

return (
<ListGroupItem
onClick={() => activateLicenseClue(licenseClue)}
key={licenseClue.id}
key={licenseClue.identifier}
className="license-group-item"
style={{
display: showClue ? "block" : "none",
Expand All @@ -299,6 +382,20 @@ const LicenseDetections = () => {
<div className="expression">
{licenseClue.license_expression}
</div>
<div className="vet-toggle">
<Form.Check
type="checkbox"
checked={vettedLicenses.has(licenseClue.identifier)}
onChange={(e) =>
handleItemToggle(
licenseClue,
LicenseTypes.CLUE,
e.target.checked
)
}
onClick={(e) => e.stopPropagation()}
></Form.Check>
</div>
</div>
</ListGroupItem>
);
Expand Down
14 changes: 14 additions & 0 deletions src/pages/Licenses/licenseDefinitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
} from "../../services/importedJsonTypes";

export interface LicenseDetectionDetails {
id: number;
identifier: string;
vetted: boolean;
license_expression: string;
detection_count: number;
detection_log: string[];
Expand All @@ -18,6 +20,8 @@ export interface LicenseClueDetails {
filePath: string;
fileClueIdx: number;
score: number | null;
vetted: boolean;
identifier: string;
license_expression: string | null;
rule_identifier: string | null;
matches: LicenseMatch[];
Expand All @@ -33,3 +37,13 @@ export type ActiveLicenseEntity =
type: "clue";
license: LicenseClueDetails;
};

export interface VetOption {
value: string;
label: string;
}
export const VET_OPTIONS: Record<string, VetOption> = {
ALL: { value: "ALL", label: "All" },
VETTED: { value: "VETTED", label: "Vetted" },
UNVETTED: { value: "UNVETTED", label: "Unvetted" },
};
Loading

0 comments on commit 1545ce5

Please sign in to comment.