diff --git a/README.md b/README.md index 27c9726..8879254 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,17 @@ In conjunction with this, we're also building an online game where this is prese [Visit https://learnawesome.vercel.app/](https://learnawesome.vercel.app/) -This is the exact same version. Your bookmarks will still be saved in localStorage so be assured that no personal data is being tracked or saved on this site. +image -But if you'd like faster performance or to self-host this, say in your company's intranet, you need a general-purpose computer (that means Linux/Windows/Mac but not crippled OSes like Android or iOS) with Datasette (which is an exploratory tool for SQLite databases) installed. You can find [installation instructions specific to your operating system here](https://docs.datasette.io/en/stable/installation.html). +image -After cloning this git repository on your local machine, run `datasette . -o` in the top-level directory to start the datasette serve and open the app in your browser. +image + +Your bookmarks are saved in localStorage so be assured that no personal data is being tracked or saved on this site. + +But if you'd like faster performance or to self-host this, you need a general-purpose computer (that means Linux/Windows/Mac) with Datasette (which is an exploratory tool for SQLite databases) installed. You can find [installation instructions specific to your operating system here](https://docs.datasette.io/en/stable/installation.html). + +After cloning this git repository on your local machine, run `npm run start` in the top-level directory to start the datasette server and open the app in your browser. ## To contribute: @@ -26,7 +32,7 @@ This is a Wikipedia-scale project and we could use all kind of help: - To donate funds, [visit our OpenCollective](https://opencollective.com/learnawesome) - To report bugs, [create an issue](https://github.com/learn-awesome/learndb/issues) - To improve our topic taxonomy (improve sub-topics / prerequisites etc), [raise a PR on our Github with changes in `db/topics.csv` file](https://github.com/learn-awesome/learndb/tree/main/db) -- To improve the data about learning resources, [raise a PR on our Github with changes in `db/items.csv` file](https://github.com/learn-awesome/learndb/tree/main/db) +- To improve the data about learning resources, first read [db/README.md](db/README.md) and [raise a PR on our Github with changes in `db/items.csv` file](https://github.com/learn-awesome/learndb/tree/main/db) - To improve design and suggest features, [start a discussion](https://github.com/learn-awesome/learndb/discussions) - To fix technical bugs, [propose solutions on the issues](https://github.com/learn-awesome/learndb/issues) - For anything else, [start a discussion](https://github.com/learn-awesome/learndb/discussions) @@ -34,8 +40,7 @@ This is a Wikipedia-scale project and we could use all kind of help: ## To develop: When you modify the *.csv files in `db/`, you should re-generate the sqlite database with `./generatedb.sh`. -Run `npm run dev` to keep live-building the JS bundle as you edit the source code. -And then run `datasette . -o` to open the app in your browser. +Run `npm run dev` to keep live-building the JS bundle as you edit the source code. This automatically runs `datasette . -o` to open the app in your browser. You can install Datasette's Vercel plugin with: `datasette install datasette-publish-vercel`. To publish this, we first run `npm run build` followed by `npm run publish`. @@ -44,7 +49,8 @@ To publish this, we first run `npm run build` followed by `npm run publish`. The dataset here is identical to https://learnawesome.org/. But there are no user accounts, no social features like learning feeds or ActivityPub. Users' bookmarks are saved in browser's localStorage. -The source data is in `db/*.csv` files. This is imported into a sqlite database with `./generatedb.sh`. +The source data is in `db/*.csv` files. The schema is described in [db/README.md](db/README.md). +These CSV files get imported into a sqlite database with `./generatedb.sh`. We then rely on datasette to load this file and offer JSON APIs over HTTP. Settings and metadata are specified in `settings.json` and `metadata.json` which datasette uses. diff --git a/db/README.md b/db/README.md new file mode 100644 index 0000000..6c48cc9 --- /dev/null +++ b/db/README.md @@ -0,0 +1,38 @@ +# CSV format + +## topics.csv + +`name` is used as primary key and therefore, must be unique and avoid uppercase and special characters other than hyphen and slash. Here are some examples: `physics`, `linear-algebra`, `nations/india`, `programming-languages/objective-c`. + +`display_name` is used as human-readable name and can preserve uppercase. For eg: `ADHD`. + +`parent_id` should be the name of the parent topic. This makes it possible to show a hierarchical view. If a topic does not have `parent_id`, it would be at the top-level but if it doesn't have children topics of its own, it will be clubbed under a dummy top-level topic called `Misc`. + +`sort_index` is an integer that's used for controlling the ordering in which topics are displayed. + + +## items.csv + +`iid` should be a unique UUID. It is needed because `reviews.csv` needs to refer to items and there is no other natural primary key. Later, if we'd want to build collections of items, the same `iid` key would be helpful. + +`description` can contain markdown with multiple lines. + +`links` is an array value separated by `;`. Each item in this array a pair of `format` and `url` separated by `|`. For eg, `links` can have a value like this: `summary|https://sivers.org/book/Decisive;book|https://www.goodreads.com/book/show/15798078-decisive;summary|https://fourminutebooks.com/decisive-summary/`. + +We are considering including other fields like `ipfsHash` and `image` in each value of `links`. This decision is yet to be made. + +`topics` is a array value of topic names separated by `;`. These should exactly match `topics` table's `name` column. + +`creators` is arbitrary string for now. For eg: `Charles Darwin`. In future, this might become a full record on its own including fields like `name`,`website`,`twitter`,`email`. In that case, we will have to somehow figure out unique key for each creator that could serve the role of primary key and foreign key. + +`difficulty` must be empty or one of these: `childlike`, `beginner`, `intermediate`, `advanced`, `research`. + +`rating` is on a 5.0 point scale with up to two decimal places allowed. This is a curated value and should not be simply copied from external sources. + +`tags` can describe quality: `visual`, `entertaining`, `challenging`, `inspirational`, `interactive`. + +## reviews.csv + +`item_id` is a foreign key to `items.csv`. +`by` is the name of the person or item. +`blurb` is small description in markdown format. \ No newline at end of file diff --git a/package.json b/package.json index 4907c46..7e09da7 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "test": "echo \"Error: no test specified\" && exit 1", "build": "rollup -c", "dev": "rollup -c -w", - "start": "sirv public --no-clear", + "start": "datasette . -o", "publish": "datasette publish vercel learn.db --project learnawesome -m metadata.json --template-dir templates --static static:static --setting max_returned_rows 20000" }, "devDependencies": { diff --git a/rollup.config.js b/rollup.config.js index 04dd8a4..96b769b 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -20,7 +20,7 @@ function serve() { return { writeBundle() { if (server) return; - server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { + server = require('child_process').spawn('npm', ['run', 'start'], { stdio: ['ignore', 'inherit', 'inherit'], shell: true }); diff --git a/src/App.svelte b/src/App.svelte index 25817f5..248a43c 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -97,10 +97,6 @@ - - - - @@ -127,11 +123,6 @@ - - - -

Datasette

-
\ No newline at end of file diff --git a/src/FormatDetail.svelte b/src/FormatDetail.svelte index 49b816a..40194aa 100644 --- a/src/FormatDetail.svelte +++ b/src/FormatDetail.svelte @@ -10,28 +10,27 @@ let query = { text: "", topic: "", - format: "", level: "", - tags: "", + tag: "", sortby: "rating" }; - $: fetch(`/learn/items.json?_shape=array&links__contains=${format}|`) + $: query && fetch(`/learn/items.json?_shape=array&_size=100&links__contains=${format}|&topics__contains=${query.topic}`) .then(r => r.json()) .then(data => { items = data; }); function handleQueryChanged(event){ - console.log("queryChanged: ", event.detail); + // console.log("queryChanged: ", event.detail); query = event.detail; } $: filteredItems = items.filter(item => { if(query.text && !item.name.toLowerCase().includes(query.text.toLowerCase())){ return false; } - if(query.format && !item.links.includes(query.format)) { return false; } + if(query.topic && !item.topics.includes(query.topic)){ return false; } if(query.level && item.difficulty != query.level){ return false; } - // TODO Apply tags filter + if(query.tag && !item.tags.includes(query.tag)){ return false; } return true; }).sort((a,b) => { if(query.sortby == 'rating') { return (a.rating - b.rating) }; diff --git a/src/SearchForm.svelte b/src/SearchForm.svelte index f18d591..59fd4c5 100644 --- a/src/SearchForm.svelte +++ b/src/SearchForm.svelte @@ -3,16 +3,12 @@ import { createEventDispatcher } from 'svelte'; export let alltopics; - export let hideFormat = false; export let hideTopic = false; - export let hideQuality = true; let query = { text: "", topic: "", - format: "", level: "", - quality: "", sortby: "rating", tag: "" }; @@ -24,24 +20,19 @@
- + {#if !hideTopic} - { return {label: t.display_name, value: t.name}; }).sort((a,b) => a.label.localeCompare(b.label)).slice(0,10)} selected={null}/> + + {#each alltopics.sort((a,b) => a.display_name.localeCompare(b.display_name)) as topic} + {topic.display_name} + {/each} + {/if} - {#if !hideFormat} - - Any format - Books - Videos - Podcasts - - {/if} - - + Any tag Inspirational Educational @@ -49,6 +40,7 @@ Entertaining Visual Interactive + Open (no login or pay) @@ -60,15 +52,6 @@ Research - {#if !hideQuality} - - Any quality - Visual - Interactive - Entertaining - - {/if} - Sort by Rating diff --git a/src/TopicDetail.svelte b/src/TopicDetail.svelte index 5402965..f26d7f3 100644 --- a/src/TopicDetail.svelte +++ b/src/TopicDetail.svelte @@ -14,9 +14,8 @@ let query = { text: "", topic: "", - format: "", level: "", - tags: "", + tag: "", sortby: "rating" }; @@ -28,15 +27,14 @@ }); function handleQueryChanged(event){ - console.log("queryChanged: ", event.detail); + // console.log("queryChanged: ", event.detail); query = event.detail; } $: filteredItems = items.filter(item => { if(query.text && !item.name.toLowerCase().includes(query.text.toLowerCase())){ return false; } - if(query.format && !item.links.includes(query.format)) { return false; } if(query.level && item.difficulty != query.level){ return false; } - // TODO: apply tags filter + if(query.tag && !item.tags.includes(query.tag)){ return false; } return true; }).sort((a,b) => { if(query.sortby == 'rating') { return (a.rating - b.rating) }; diff --git a/src/TopicList.svelte b/src/TopicList.svelte index 6ad38f1..a95a910 100644 --- a/src/TopicList.svelte +++ b/src/TopicList.svelte @@ -4,4 +4,7 @@ import SearchForm from "./SearchForm.svelte" + diff --git a/src/TopicMasonryGrid.svelte b/src/TopicMasonryGrid.svelte index 5437bc4..f2c2f8d 100644 --- a/src/TopicMasonryGrid.svelte +++ b/src/TopicMasonryGrid.svelte @@ -84,7 +84,7 @@ -
+
{#each [...map.entries()].sort((t1,t2) => (t1[0].sort_index || 100) - (t2[0].sort_index || 100)) as parent}
diff --git a/src/tailwindui/ComboBox.svelte b/src/tailwindui/ComboBox.svelte index c9e4b06..82f627b 100644 --- a/src/tailwindui/ComboBox.svelte +++ b/src/tailwindui/ComboBox.svelte @@ -1,15 +1,24 @@ - - + + + + {#each filteredOptions as opt (opt.value)} - {opt.label} + {opt.label} {/each} diff --git a/static/map.html b/static/map.html index 9394c40..54bd042 100644 --- a/static/map.html +++ b/static/map.html @@ -6,6 +6,7 @@ TreeMap + - +

- + LearnAwesome