Compare commits

..

2 Commits

Author SHA1 Message Date
deme
d8ba5b11f3 [examples/openspending][m]: implement analytics and update favicon 2023-05-19 15:12:38 -03:00
Luccas Mateus
bedc9a8d33 [examples/openspending][xl]: add table preview to showcase page, minor fixes (#895)
Co-authored-by: deme <joaommdemenech@gmail.com>
2023-05-19 14:44:34 -03:00
13 changed files with 3193 additions and 348 deletions

View File

@@ -49,20 +49,35 @@ export default function DatasetsSearch({
? dataset.countryCode === watch().country
: true
)
// TODO: Does that really makes sense?
// What if the fiscalPeriod is 2015-2017 and inputs are
// set to 2015-2016. It's going to be filtered out but
// it shouldn't.
.filter((dataset) =>
watch().minDate && watch().minDate !== ''
? dataset.fiscalPeriod?.start >= watch().minDate
: true
)
.filter((dataset) =>
watch().maxDate && watch().maxDate !== ''
? dataset.fiscalPeriod?.end <= watch().maxDate
: true
);
.filter((dataset) => {
const filterMinDate = watch().minDate;
const filterMaxDate = watch().maxDate;
const datasetMinDate = dataset.fiscalPeriod?.start;
const datasetMaxDate = dataset.fiscalPeriod?.end;
let datasetStartOverlaps = false;
if (datasetMinDate) {
datasetStartOverlaps =
datasetMinDate >= filterMinDate && datasetMinDate <= filterMaxDate;
}
let datasetEndOverlaps = false;
if (datasetMaxDate) {
datasetEndOverlaps =
datasetMaxDate >= filterMinDate && datasetMaxDate <= filterMaxDate;
}
if (filterMinDate && filterMaxDate) {
return datasetStartOverlaps || datasetEndOverlaps;
} else if (filterMinDate) {
return datasetMinDate >= filterMinDate;
} else if (filterMaxDate) {
return datasetMinDate <= filterMaxDate;
}
return true;
});
const paginatedDatasets = filteredDatasets.slice(
(page - 1) * itemsPerPage,
@@ -111,7 +126,9 @@ export default function DatasetsSearch({
</select>
</div>
<div className="sm:basis-1/6">
<label className="text-sm text-gray-600 font-medium">Min. date</label>
<label className="text-sm text-gray-600 font-medium">
Fiscal Period Start
</label>
<div className="relative">
<input
aria-label="Min. date"
@@ -122,7 +139,9 @@ export default function DatasetsSearch({
</div>
</div>
<div className="sm:basis-1/6">
<label className="text-sm text-gray-600 font-medium">Max. date</label>
<label className="text-sm text-gray-600 font-medium">
Fiscal Period End
</label>
<div className="relative">
<input
aria-label="Max. date"
@@ -196,9 +215,9 @@ const CloseIcon = () => {
id="Vector"
d="M18 18L12 12M12 12L6 6M12 12L18 6M12 12L6 18"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</g>
</svg>

View File

@@ -62,7 +62,7 @@ const Spinning = () => {
);
};
export const Table: React.FC<{ url: string }> = ({ url }) => {
export const FlatUiTable: React.FC<{ url: string }> = ({ url }) => {
return (
// Provide the client to your App
<QueryClientProvider client={queryClient}>
@@ -87,7 +87,7 @@ const TableInner: React.FC<{ url: string }> = ({ url }) => {
</div>;
if (parsedData)
return (
<div className="h-[500px] overflow-scroll">
<div className="h-[500px] w-full">
<Grid data={parsedData.data} />
</div>
);

View File

@@ -2,7 +2,7 @@ import { Header } from '../Header';
export default function Layout({ children }) {
return (
<div className="bg-white min-h-screen">
<div className="bg-white min-h-screen pb-32">
<Header />
{children}
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
"test": "vitest"
},
"dependencies": {
"@flowershow/core": "^0.4.13",
"@githubocto/flat-ui": "^0.14.1",
"@heroicons/react": "^2.0.18",
"@octokit/plugin-throttling": "^5.2.2",
@@ -22,7 +23,7 @@
"eslint": "8.39.0",
"eslint-config-next": "13.3.1",
"flexsearch": "0.7.21",
"next": "13.3.1",
"next": "13.3.0",
"next-seo": "^6.0.0",
"octokit": "^2.0.14",
"papaparse": "^5.4.1",

View File

@@ -13,6 +13,7 @@ import Layout from '../../../components/_shared/Layout';
import Link from 'next/link';
import { Project } from '../../../lib/project.interface';
import ExternalLinkIcon from '../../../components/icons/ExternalLinkIcon';
import { FlatUiTable } from '@/components/FlatUiTable';
export default function ProjectPage({
project,
@@ -21,7 +22,6 @@ export default function ProjectPage({
project: Project;
readme: string;
}) {
// Get description from datapackage or calculate
// excerpt from README by getting all the content
// up to the first dot.
@@ -98,7 +98,7 @@ export default function ProjectPage({
<h3 className="mb-1 mt-10">Data files</h3>
<p>
This dataset contains {project.files.length} file
{project.files.length != 1 ? '' : 's'}
{project.files.length == 1 ? '' : 's'}
</p>
<div className="inline-block min-w-full py-2 align-middle">
<table className="mt-0 min-w-full divide-y divide-gray-300">
@@ -137,7 +137,6 @@ export default function ProjectPage({
}
}
}
return (
<tr key={file.name}>
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
@@ -169,6 +168,44 @@ export default function ProjectPage({
</table>
</div>
<div className="flex flex-col gap-y-16 ">
{project.files?.map((file) => {
let size: number | string = file.size;
if (!size) {
if (file.bytes) {
if (file.bytes > 1000000) {
size = (file.bytes / 1000000).toFixed(2) + ' MB';
} else {
size = (file.bytes / 1000).toFixed(2) + ' kB';
}
}
}
return (
<div key={file.name}>
{file.path && (
<>
<h4>
{file.name}
{file.format ? `.${file.format}` : ''}
</h4>
{file.bytes >= 5132288 && (
<span>Previewing 5MB out of {size}</span>
)}
<FlatUiTable
url={
file.path.startsWith('http')
? file.path
: `https://raw.githubusercontent.com/${project.owner.name}/${project.repo.name}/main/${file.path}`
}
/>
</>
)}
</div>
);
})}
</div>
{readme && (
<>
<hr />
@@ -192,7 +229,6 @@ export async function getStaticPaths() {
github_pat
);
console.log(allProjects)
const paths = allProjects.results.map((project) => ({
params: {
// TODO: dynamize the org
@@ -223,7 +259,12 @@ export async function getStaticProps({ params }) {
const project = loadDataPackage(datapackage, repo);
// TODO: should this be moved to the loader?
const readme = await getProjectReadme(orgName, projectName, 'main', github_pat);
const readme = await getProjectReadme(
orgName,
projectName,
'main',
github_pat
);
return {
props: {

View File

@@ -2,10 +2,52 @@ import { AppProps } from 'next/app';
import './styles.css';
import { NextSeo } from 'next-seo';
import { useEffect } from 'react';
import { pageview } from '@flowershow/core';
import Script from 'next/script';
import Head from 'next/head';
import { useRouter } from 'next/router';
function CustomApp({ Component, pageProps }: AppProps) {
const router = useRouter();
const GA_TOKEN = 'G-GXZF7NRXX6';
useEffect(() => {
const handleRouteChange = (url) => {
pageview(url);
};
router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [router.events]);
return (
<>
<Head>
<link rel="shortcut icon" href="/squared_logo.png" />
</Head>
<NextSeo title="OpenSpending" />
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TOKEN}`}
/>
<Script
id="gtag-init"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', ${GA_TOKEN}, {
page_path: window.location.pathname,
});
`,
}}
/>
<main className="app">
<Component {...pageProps} />
</main>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -13,7 +13,10 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
"incremental": true,
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

View File

@@ -1,270 +0,0 @@
---
title: Learn how to use MarkdownDB with our First Tutorial!
description: We've just released our first tutorial that covers the fundamentals of MarkdownDB - our new package for treating markdown files as a database. If you've been curious about how to manage your markdown files more effectively, check it out!
date: 2023-05-26
author: Ola Rubaj
---
We've just released our first tutorial that covers the fundamentals of [MarkdownDB](https://github.com/datopian/markdowndb) - our new package for treating markdown files as a database. If you've been curious about how to manage your markdown files more effectively, this tutorial is an excellent starting point!
![Gif](/assets/blog/markdowndb.gif)
## What is MarkdownDB?
MarkdownDB can parse your markdown files, extract structured data (such as frontmatter, tags, back- and forward links and more), and create an index in a local SQLite database. It also provides a lightweight JavaScript API for querying the database and importing files into your application. With MarkdownDB, you have a powerful tool that allows you to efficiently query your markdown data.
[🔍 Click here to learn more!](https://github.com/datopian/markdowndb)
## What you're going to learn
This tutorial guides you through the steps of creating a small project catalog. In our case, we used our open-source projects on GitHub - but you could use anything! We use simple markdown files to describe each project and then display them with the MarkdownDB package.
## Step 1: Create a markdown file for each project
First, let's create a markdown file for each project. You can use real project details or make up some examples. For the sake of this tutorial, we'll create files for some of the projects we've built at Datopian.
```bash
mkdir projects
cd projects
touch markdowndb.md portaljs.md flowershow.md datapipes.md giftless.md data-cli.md
```
In each file we'll write some short info about a given project, like so:
```md
# What is MarkdownDB
MarkdownDB is a javascript library for treating markdown files as a database -- as a "MarkdownDB". Specifically, it:
- Parses your markdown files to extract structured data (frontmatter, tags, etc) and creates an index in a local SQLite database
- Provides a lightweight javascript API for querying the database and importing files into your application
```
## Step 2: Index markdown files into SQLite database
Once we have prepared our markdown files, we can store them (or more precisely - their metadata) in a database, so that we can then query it later for specific project files.
```bash
# npx @flowershow/markdowndb <path-to-folder-with-md-files>
npx @flowershow/markdowndb ./projects
```
The above command will output a `markdown.db` file in the directory where it was executed. So, in our case, the folder structure will look like this:
```
.
├── markdown.db
└── projects
├── data-cli.md
├── ...
└── portaljs.md
```
## Step 3: Explore the SQLite database
Now, let's explore the database. We can do it with any SQLite viewer, e.g. https://sqlitebrowser.org/. When we open the `markdown.db` file in the viewer, we'll see a list of tables:
- `files`: containing metadata of our markdown,
- `file_tags`: containing tags set in the frontmatter of our markdown files,
- `links`: containing wiki links, i.e. links between our markdown files,
- `tags`: containing all tags used in our markdown files.
In our case, the `files` table will look like this:
![[/assets/blog/sqlite-viewer.png]]
You can also explore the database from the command line, e.g.:
```bash
sqlite3 markdown.db
```
And then run SQL queries, e.g.:
```sql
SELECT * FROM files;
```
Which will output:
```bash
27ce406aac24e59af7a9f3c0c0a437c1d024152b|projects/data-cli.md|md|data-cli||{}
26b1b0b06a4f450646f9e22fc18ec069bf577d8c|projects/datapipes.md|md|datapipes||{}
dafdd0daf71a1b06db1988c57848dc36947375f4|projects/flowershow.md|md|flowershow||{}
32c8db33fb8758516bfefb6ab1f22d03b1e53a08|projects/giftless.md|md|giftless||{}
7e01cae193f12f5a4a9be2b89f22b429761bd313|projects/markdowndb.md|md|markdowndb||{}
5445349c6822704d6f531a83c2aca2e4f90de864|projects/portaljs.md|md|portaljs||{}
```
## Step 4: Query the database in the Node.js app
Now, let's write a simple script that will query the database for our projects and display them on the terminal.
First, let's create a new Node.js project:
```bash
mkdir projects-list
cd projects-list
npm init -y
```
Then, let's install the `@flowershow/markdowndb` package:
```bash
npm install @flowershow/markdowndb
```
Now, let's create a new file `index.js` and add the following code:
```js
import { MarkdownDB } from '@flowershow/markdowndb';
// change this to the path to your markdown.db file
const dbPath = 'markdown.db';
const client = new MarkdownDB({
client: 'sqlite3',
connection: {
filename: dbPath,
},
});
const mddb = await client.init();
// get all projects
const projects = await mddb.getFiles();
console.log(JSON.stringify(projects, null, 2));
process.exit(0);
```
Since we're using ES6 modules, we also need to add `"type": "module"` to our `package.json` file.
Before we run the above script, we need to make sure that the `dbPath` variable is pointing to our `markdown.db` file. If you want to store the database outside of your project folder, you can update the `dbPath` variable to point to the correct location. If you want to have it inside your project folder, you can copy it there, or simply re-run the `npx @flowershow/markdowndb <path-to-markdown-folder>` command from within your project folder.
Now, let's run the script:
```bash
node index.js
```
It should output the JSON with all our projects.
```json
[
{
"_id": "7e01cae193f12f5a4a9be2b89f22b429761bd313",
"file_path": "projects/markdowndb.md",
"extension": "md",
"url_path": "markdowndb",
"filetype": null,
"metadata": {}
},
...
]
```
## Step 5: Add metadata to project files
Now, let's add some metadata to our project files. We'll use frontmatter for that. Since we're creating a catalog of our GitHub projects, we'll add the following frontmatter fields to each file:
```md
---
name: markdowndb
description: Javascript library for treating markdown files as a database.
stars: 6
forks: 0
---
```
After adding the metadata, we need to re-index our markdown files into the database:
```bash
npx @flowershow/markdowndb ../projects
```
Now, if we run our script again, we'll see that the `metadata` field in the output contains the metadata we've added to our project files:
```json
[
{
"_id": "7e01cae193f12f5a4a9be2b89f22b429761bd313",
"file_path": "projects/markdowndb.md",
"extension": "md",
"url_path": "markdowndb",
"metadata": {
"name": "markdowndb",
"description": "Javascript library for treating markdown files as a database",
"stars": 6,
"forks": 0
}
},
...
]
```
## Step 6: Pretty print the output
Now, let's make the output a bit more readable. We'll use the `columnify` package for that:
```bash
npm install columnify
```
And then we'll update our `index.js` file:
```js {2,16-38}
import { MarkdownDB } from '@flowershow/markdowndb';
import columnify from 'columnify';
const dbPath = 'markdown.db';
const client = new MarkdownDB({
client: 'sqlite3',
connection: {
filename: dbPath,
},
});
const mddb = await client.init();
const projects = await mddb.getFiles();
// console.log(JSON.stringify(projects, null, 2));
const projects2 = projects.map((project) => {
const { file_path, metadata } = project;
return {
file_path,
...metadata,
};
});
const columns = columnify(projects2, {
truncate: true,
columnSplitter: ' | ',
config: {
description: {
maxWidth: 80,
},
},
});
console.log('\n');
console.log(columns);
console.log('\n');
process.exit(0);
```
The above script will output the following to the terminal:
![[/assets/blog/output.png]]
## Done!
That's it! We've just created a simple catalog of our GitHub projects using markdown files and the MarkdownDB package. You can find the full code for this tutorial [here](https://github.com/datopian/markdowndb/tree/main/examples/basic-example).
We look forward to seeing the amazing applications you'll build with this tool!
Happy coding!