From bd4064b11b31fce1469528fa74800a50c8757ab2 Mon Sep 17 00:00:00 2001 From: maithili232 Date: Wed, 8 Nov 2023 20:42:34 +0530 Subject: [PATCH 1/3] Layers page --- .../components/Layers/FilterButton.js | 53 ++++++ .../deepchem/components/Layers/LayerCard.js | 59 +++++++ new-website/deepchem/pages/layers.js | 155 ++++++++++++++++++ new-website/deepchem/styles/globals.css | 47 ++++++ new-website/utils/layers/get_layers.py | 111 +++++++++++++ 5 files changed, 425 insertions(+) create mode 100644 new-website/deepchem/components/Layers/FilterButton.js create mode 100644 new-website/deepchem/components/Layers/LayerCard.js create mode 100644 new-website/deepchem/pages/layers.js create mode 100644 new-website/utils/layers/get_layers.py diff --git a/new-website/deepchem/components/Layers/FilterButton.js b/new-website/deepchem/components/Layers/FilterButton.js new file mode 100644 index 00000000..d7d72ade --- /dev/null +++ b/new-website/deepchem/components/Layers/FilterButton.js @@ -0,0 +1,53 @@ +import Image from "next/image"; + +import deepchemCross from "../../public/icons/deepchem-cross.png"; + +/** + * The FilterButton component is used to create a button for each filter + * @component + * @param {Object} props - props passed to the component + * @param {Array} props.category - array of all the available filters + * @param {String} props.name - name of the particular filter + * @param {Image} props.image - image associated to the filter + * @return {JSX.Element} - A JSX element representing the FilterButton + */ +const FilterButton = ({ category, name, image }) => { + const TRUNC_LENGTH = 20; + const nameShort = name.replace(/Model$/g, ""); + + return ( + <> +
+ {image ? ( + {name} + ) : ( + category.includes(name) && ( + {name} + ) + )} +

+ {nameShort.length > TRUNC_LENGTH + ? nameShort.substring(0, TRUNC_LENGTH) + "..." + : nameShort} +

+
+ + ); +}; + +export default FilterButton; diff --git a/new-website/deepchem/components/Layers/LayerCard.js b/new-website/deepchem/components/Layers/LayerCard.js new file mode 100644 index 00000000..69e4de7c --- /dev/null +++ b/new-website/deepchem/components/Layers/LayerCard.js @@ -0,0 +1,59 @@ +import Image from "next/image"; +import Link from "next/link"; + +import deepchemPytorch from "../../public/icons/deepchem-pytorch.png"; +import deepchemKeras from "../../public/icons/deepchem-keras.png"; + +/** + * Function to parse and format strings that are passed to the model card + * @function + * @param {string} name - string of the name passed + * @return {string} - The parsed and formatted string + */ +function parseName(name) { + name = name.replaceAll(/([A-Z]+)/g, " $1"); + name = name.replace(/([^ ])(Layer)/, "$1 Layer"); + return name; + } + +const LayerCard = ({ layer }) => { + let models = layer.models.length + ? layer.models.join(", ") + : "N/A"; + models = models + " " + "\xa0".repeat(300); + + return ( + <> + +
+
+
+ {parseName(layer.name)} +
+
+ {layer.category === "torch" && ( + PyTorch + )} + {layer.category === "keras" && ( + Keras + )} +
+
+ {layer.category} +
+
+ { +
+

Acceptable Models

+

+ {models} +

+
+ } +
+ + + ); +}; + +export default LayerCard; \ No newline at end of file diff --git a/new-website/deepchem/pages/layers.js b/new-website/deepchem/pages/layers.js new file mode 100644 index 00000000..72884ae4 --- /dev/null +++ b/new-website/deepchem/pages/layers.js @@ -0,0 +1,155 @@ +import React, { useEffect, useState } from "react"; + +import Image from "next/image"; + +import LayerCard from "/components/Layers/LayerCard"; +import FilterButton from "/components/Layers/FilterButton"; + +import layers from "/data/layers/layers.json"; +import modelList from "/data/layers/models.json"; + +import deepchemPyTorch from "/public/icons/deepchem-pytorch.png"; +import deepchemKeras from "/public/icons/deepchem-keras.png"; +import deepchemFilter from "/public/icons/deepchem-filter.png"; + + +/** + * Models component that displays the models page of the application + * @component + * @return {JSX.Element} The JSX element to render the Model component + */ +const Layers = () => { + + const [filteredLayers, setFilteredLayers] = useState(layers); + const [models, setModels] = useState([]); + const [isPopUp, setIsPopUp] = useState(false); + + const handleClick = (category, value) => { + switch (category) { + case "models": + models.includes(value) + ? setModels(models.filter((item) => item !== value)) + : setModels([...models, value]); + break; + default: + break; + } + }; + + const handlePopUp = () => { + setIsPopUp(!isPopUp); + }; + + useEffect(() => { + let newlayers = []; + const flayers = layers; + if ( + models.length === 0 + ) { + newlayers = layers; + } else { + flayers.map((flayer) => { + let exist = 1; + models.map((value) => { + if (!flayer.models.includes(value)) { + exist = 0; + } + }); + if (exist == 1) { + newlayers.push(flayer); + } + }); + } + + setFilteredLayers(newlayers); + }, [models]); + + useEffect(() => { + window.addEventListener("resize", () => { + if (window.innerWidth > 1024) { + setIsPopUp(false); + } + }); + }, []); + + return ( + +
+
+
+ {/* HEADING BEGIN */} +
+
Our Layers
+
+ +
+ {/* HEADING END */} + {/* BODY BEGIN */} +
+ {/* FILTER SECTION BEGIN */} +
+ {/* MODEL BEGIN */} +
+
Model
+
+ {modelList.map((model, index) => ( +
+ +
+ ))} +
+
+ {/* MODEL END */} + {/* LAYER CARDS SECTION BEGIN */} +
+ {filteredLayers.length ? ( + filteredLayers.map((layer) => ( + + )) + ) : ( +
+ +

No such layers exist!

+
+ )} +
+ {/* MODEL CARDS SECTION END */} +
+ {/*BODY END*/} +
+
+
+ ); +}; + +export default Layers; diff --git a/new-website/deepchem/styles/globals.css b/new-website/deepchem/styles/globals.css index c17e7d34..7788a14c 100644 --- a/new-website/deepchem/styles/globals.css +++ b/new-website/deepchem/styles/globals.css @@ -42,6 +42,7 @@ canvas { .explore, .about, .models, + .layers, .tutorials { zoom: 0.8; } @@ -227,3 +228,49 @@ footer { @apply pb-4; } } + +/* layers page */ +@layer component { + + .btn-container-filter { + @apply flex flex-row items-start gap-2.5 flex-wrap; + } + + .rmv-filter { + @apply p-0 normal-case min-w-0; + } + + .btn-filter { + @apply flex flex-row items-center gap-2.5 py-0.5 px-2 box-border bg-white border border-solid border-dc-light-gray rounded-md; + } + + .btn-selected-filter { + @apply flex flex-row items-center gap-2.5 py-0.5 px-2 box-border bg-dc-light-gray border border-solid border-dc-light-gray rounded-md; + } + + .btn-text-filter { + @apply text-[0.8rem] text-dc-gray text-ellipsis font-medium; + } + + .btn-text-selected-filter { + @apply text-[0.8rem] text-dc-white break-all; + } + + .category-text-filter { + @apply text-lg text-dc-light-blue; + } + + .category-filter { + @apply flex flex-col items-start gap-2.5; + } + + .layer-container { + @apply w-full grid grid-cols-1 md:grid-cols-2 3xl:grid-cols-3; + } + + .layer-card { + @apply h-fit md:min-h-[260px]; + } + +} + diff --git a/new-website/utils/layers/get_layers.py b/new-website/utils/layers/get_layers.py new file mode 100644 index 00000000..4398f866 --- /dev/null +++ b/new-website/utils/layers/get_layers.py @@ -0,0 +1,111 @@ +""" +Python script to download all the layer data csv files from the official Deepchem repo and extract the necessary data +""" +import os +import json +import dataclasses +from json import JSONEncoder +import subprocess +import pandas as pd +import numpy as np + +PATH = '../../deepchem/data/layers/' +REDIRECT_URL = 'https://deepchem.readthedocs.io/en/latest/api_reference/layers.html#' +CHEATSHEETS = { + 'keras': 'https://raw.githubusercontent.com/deepchem/deepchem/master/docs/source/api_reference/keras_layers.csv', + 'torch': 'https://raw.githubusercontent.com/deepchem/deepchem/master/docs/source/api_reference/torch_layers.csv' +} + +layer_list = [] + +@dataclasses.dataclass +class Layer: + """ + Layer class to store a single layer information + + Parameters + ---------- + name: str + Name of the layer + url: str + redirect url to the layer on the official deepchem documentation + category: str + Category of the layer + layer_id: int + Unique id of the layer + """ + name: str + url: str + category: str + models : list + layer_id: int + +class LayerListEncoder(JSONEncoder): + """ + LayerEncoder class to encode the Layer object to JSON + """ + def default(self, o): + """ + Default function to encode the Layer object to JSON + + Returns + ------- + json: str + """ + return o.__dict__ + +def convert_to_json(data, name, is_layer=False): + """ + Convert the list of data to JSON format and write to a file + + Parameters + ---------- + data: list + The list of data to be converted to JSON + name: str + The name of the file to be written + is_layer: bool + Flag to check if the data is a list of Layer objects, different encoding is required for Layer objects + """ + if is_layer: + data = json.dumps(data, indent=4, cls=LayerListEncoder) + else: + data = list({item for sublist in data for item in sublist}) + data = list(filter(None, data)) + data = json.dumps(data, indent=4) + + with open(PATH + name, 'w', encoding="utf-8") as file: + file.write(data) + +def fetch_data(): + """ + Function to fetch the layer csv files and extract necessary data + """ + for category in CHEATSHEETS.items(): + subprocess.call(f'curl -o {PATH}{category[0] + ".csv"} {category[1]}', shell=True) + + index = -1 + for filename in os.listdir(PATH): + for row in pd.read_csv(PATH + filename, on_bad_lines='skip').replace(np.nan, '').iterrows(): + name = row[1]['Layer'] + url = REDIRECT_URL + name.lower() + category = filename.split('.')[0] + models = row['Model'].split(' ') if row['Model'] != '' else [] + index += 1 + + model_list.append(models) + layer_list.append(Layer(name, url, category, models, index)) + + +def main(): + """ + Main function to execute the script + """ + os.makedirs(PATH) + fetch_data() + convert_to_json(layer_list, 'layers.json', is_layer=True) + convert_to_json(model_list, 'models.json') + +if __name__ == '__main__': + main() + From 2f8c7096c5a064a8cb158d78e60f50de412981a0 Mon Sep 17 00:00:00 2001 From: maithili232 Date: Wed, 8 Nov 2023 20:53:37 +0530 Subject: [PATCH 2/3] fix --- new-website/utils/layers/get_layers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/new-website/utils/layers/get_layers.py b/new-website/utils/layers/get_layers.py index 4398f866..df2082e6 100644 --- a/new-website/utils/layers/get_layers.py +++ b/new-website/utils/layers/get_layers.py @@ -17,6 +17,7 @@ } layer_list = [] +model_list = [] @dataclasses.dataclass class Layer: From 5d21e4d306e05e92ecf1f4c3b72a96eefaf9591a Mon Sep 17 00:00:00 2001 From: maithili232 Date: Fri, 10 Nov 2023 19:55:40 +0530 Subject: [PATCH 3/3] updates --- .../components/Layers/FilterButton.js | 53 ------------------ .../{Models => common}/FilterButton.js | 0 new-website/deepchem/pages/index.js | 8 +-- new-website/deepchem/pages/layers.js | 6 +-- new-website/deepchem/pages/models.js | 3 +- new-website/deepchem/styles/globals.css | 54 ++++--------------- 6 files changed, 17 insertions(+), 107 deletions(-) delete mode 100644 new-website/deepchem/components/Layers/FilterButton.js rename new-website/deepchem/components/{Models => common}/FilterButton.js (100%) diff --git a/new-website/deepchem/components/Layers/FilterButton.js b/new-website/deepchem/components/Layers/FilterButton.js deleted file mode 100644 index d7d72ade..00000000 --- a/new-website/deepchem/components/Layers/FilterButton.js +++ /dev/null @@ -1,53 +0,0 @@ -import Image from "next/image"; - -import deepchemCross from "../../public/icons/deepchem-cross.png"; - -/** - * The FilterButton component is used to create a button for each filter - * @component - * @param {Object} props - props passed to the component - * @param {Array} props.category - array of all the available filters - * @param {String} props.name - name of the particular filter - * @param {Image} props.image - image associated to the filter - * @return {JSX.Element} - A JSX element representing the FilterButton - */ -const FilterButton = ({ category, name, image }) => { - const TRUNC_LENGTH = 20; - const nameShort = name.replace(/Model$/g, ""); - - return ( - <> -
- {image ? ( - {name} - ) : ( - category.includes(name) && ( - {name} - ) - )} -

- {nameShort.length > TRUNC_LENGTH - ? nameShort.substring(0, TRUNC_LENGTH) + "..." - : nameShort} -

-
- - ); -}; - -export default FilterButton; diff --git a/new-website/deepchem/components/Models/FilterButton.js b/new-website/deepchem/components/common/FilterButton.js similarity index 100% rename from new-website/deepchem/components/Models/FilterButton.js rename to new-website/deepchem/components/common/FilterButton.js diff --git a/new-website/deepchem/pages/index.js b/new-website/deepchem/pages/index.js index 7f9b15ab..80b6dddf 100644 --- a/new-website/deepchem/pages/index.js +++ b/new-website/deepchem/pages/index.js @@ -229,11 +229,11 @@ const Home = () => { title="Tutorials" link={"/tutorials"} /> - {/* */} + title="LAYERS" + link={"/layers"} + /> {/* EXPLORE END */} diff --git a/new-website/deepchem/pages/layers.js b/new-website/deepchem/pages/layers.js index 72884ae4..f9eeca0d 100644 --- a/new-website/deepchem/pages/layers.js +++ b/new-website/deepchem/pages/layers.js @@ -3,7 +3,7 @@ import React, { useEffect, useState } from "react"; import Image from "next/image"; import LayerCard from "/components/Layers/LayerCard"; -import FilterButton from "/components/Layers/FilterButton"; +import FilterButton from "/components/common/FilterButton"; import layers from "/data/layers/layers.json"; import modelList from "/data/layers/models.json"; @@ -83,7 +83,7 @@ const Layers = () => { >
{/* HEADING BEGIN */} -
+
Our Layers
{/* HEADING END */} {/* BODY BEGIN */} -
+
{/* FILTER SECTION BEGIN */}
{
- {/* HEADING END */} {/* BODY BEGIN */}
diff --git a/new-website/deepchem/styles/globals.css b/new-website/deepchem/styles/globals.css index 7788a14c..fecf4abc 100644 --- a/new-website/deepchem/styles/globals.css +++ b/new-website/deepchem/styles/globals.css @@ -145,7 +145,7 @@ footer { @apply !shadow-none; } -/* models page */ +/* models and layers page */ @layer components { .btn-container-filter { @apply flex flex-row items-start gap-2.5 flex-wrap; @@ -186,6 +186,14 @@ footer { .model-card { @apply h-fit md:min-h-[260px]; } + + .layer-container { + @apply w-full grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-3; + } + + .layer-card { + @apply h-fit md:min-h-[260px]; + } } /* tutorials page */ @@ -229,48 +237,4 @@ footer { } } -/* layers page */ -@layer component { - - .btn-container-filter { - @apply flex flex-row items-start gap-2.5 flex-wrap; - } - - .rmv-filter { - @apply p-0 normal-case min-w-0; - } - - .btn-filter { - @apply flex flex-row items-center gap-2.5 py-0.5 px-2 box-border bg-white border border-solid border-dc-light-gray rounded-md; - } - - .btn-selected-filter { - @apply flex flex-row items-center gap-2.5 py-0.5 px-2 box-border bg-dc-light-gray border border-solid border-dc-light-gray rounded-md; - } - - .btn-text-filter { - @apply text-[0.8rem] text-dc-gray text-ellipsis font-medium; - } - - .btn-text-selected-filter { - @apply text-[0.8rem] text-dc-white break-all; - } - - .category-text-filter { - @apply text-lg text-dc-light-blue; - } - - .category-filter { - @apply flex flex-col items-start gap-2.5; - } - - .layer-container { - @apply w-full grid grid-cols-1 md:grid-cols-2 3xl:grid-cols-3; - } - - .layer-card { - @apply h-fit md:min-h-[260px]; - } - -}