├── .DS_Store ├── .gitattributes ├── .github ├── .DS_Store └── workflows │ └── publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── icon.png ├── index.html ├── index.ts ├── package.json ├── tsconfig.json └── yarn.lock /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawhney17/logseq-property-visualizer/aef03314e6fa97f305e2bd1b6f9c4b5217de4174/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawhney17/logseq-property-visualizer/aef03314e6fa97f305e2bd1b6f9c4b5217de4174/.github/.DS_Store -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Build plugin 2 | 3 | on: 4 | push: 5 | # Sequence of patterns matched against refs/tags 6 | tags: 7 | - '*' # Push events to matching any tag format, i.e. 1.0, 20.15.10 8 | 9 | env: 10 | PLUGIN_NAME: logseq-property-visualizer 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Use Node.js 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: '16.x' # You might need to adjust this value to your own version 22 | - name: Build 23 | id: build 24 | run: | 25 | npm i && npm run build 26 | mkdir ${{ env.PLUGIN_NAME }} 27 | cp README.md package.json icon.png ${{ env.PLUGIN_NAME }} 28 | mv dist ${{ env.PLUGIN_NAME }} 29 | zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }} 30 | ls 31 | echo "::set-output name=tag_name::$(git tag --sort version:refname | tail -n 1)" 32 | - name: Create Release 33 | uses: ncipollo/release-action@v1 34 | id: create_release 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | VERSION: ${{ github.ref }} 38 | with: 39 | allowUpdates: true 40 | draft: false 41 | prerelease: false 42 | 43 | - name: Upload zip file 44 | id: upload_zip 45 | uses: actions/upload-release-asset@v1 46 | env: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | with: 49 | upload_url: ${{ steps.create_release.outputs.upload_url }} 50 | asset_path: ./${{ env.PLUGIN_NAME }}.zip 51 | asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip 52 | asset_content_type: application/zip 53 | 54 | - name: Upload package.json 55 | id: upload_metadata 56 | uses: actions/upload-release-asset@v1 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | with: 60 | upload_url: ${{ steps.create_release.outputs.upload_url }} 61 | asset_path: ./package.json 62 | asset_name: package.json 63 | asset_content_type: application/json 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build and Release Folders 2 | bin-debug/ 3 | bin-release/ 4 | [Oo]bj/ 5 | [Bb]in/ 6 | 7 | # Other files and folders 8 | .settings/ 9 | 10 | # Executables 11 | *.swf 12 | *.air 13 | *.ipa 14 | *.apk 15 | 16 | # Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` 17 | # should NOT be excluded as they contain compiler settings and other important 18 | # information for Eclipse / Flash Builder. 19 | node_modules 20 | dist 21 | .parcel-cache 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 sawhney17 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # logseq-property-visualizer 2 | A plugin to chart or visualize trends in the values of page properties over time. 3 | ## Setup 4 | 1. First ensure that you have [hkgnp's](https://github.com/hkgnp) [Table Render](https://github.com/hkgnp/logseq-tablerender-plugin) and [Chart Render](https://github.com/hkgnp/logseq-chartrender-plugin) plugins 5 | 2. Use the slash menu and type `property visualizer` 6 | 3. Assign variables in the syntax 7 | - {{renderer :property_visualizer, {property name}, {type of graph or table}, {graph or table}, {last x days}} 8 | 4. Base the types from the readme of either plugin 9 | 5. Samples 10 | - `{{renderer :property_visualizer, happiness, line white 300, chart, 40}}` 11 | - Will generate lined chart, with width 300, color white, graphing property "happiness" over the last 40 days 12 | - Screen Shot 2022-01-17 at 9 31 53 PM 13 | 14 | - `{{renderer :property_visualizer, fulfillment}}` 15 | - Will generate table with sum, median and average. 16 | - Screen Shot 2022-01-17 at 9 30 51 PM 17 | 18 | - `{{renderer :property_visualizer, happiness, data nosum}}` 19 | - Will generate simple table showing the values of happiness 20 | - Screen Shot 2022-01-17 at 9 34 52 PM 21 | 6. Important: Will only fetch page properties of journal pages 22 | - Add a page property to every journal page when you want to track something 23 | - Track habits, word written, etc. 24 | ## Limitations 25 | 1. Only works when property is mentioned on the page property of a journal page 26 | - Screen Shot 2022-01-17 at 10 22 19 PM -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawhney17/logseq-property-visualizer/aef03314e6fa97f305e2bd1b6f9c4b5217de4174/icon.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | logseq-property-visualizer 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import '@logseq/libs' 2 | import { 3 | BlockEntity, 4 | PageEntity, 5 | IBatchBlock, 6 | } from '@logseq/libs/dist/LSPlugin.user'; 7 | /** 8 | * main entry 9 | */ 10 | function timeConverter(x: number) { 11 | var a = new Date(x * 1000); 12 | var months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']; 13 | var year = a.getFullYear(); 14 | var month = months[a.getMonth()]; 15 | var date = a.getDate(); 16 | var time = year + month + date; 17 | return time; 18 | } 19 | function convertDate(inputDate: number) { 20 | const dateString = inputDate.toString() 21 | console.log("hi") 22 | const year = parseInt(dateString.substring(0, 4)) 23 | const month = parseInt(dateString.substring(4, 6)) 24 | const day = parseInt(dateString.substring(6, 8)) 25 | console.log(year, month, day) 26 | console.log(new Date(year, month - 1, day)) 27 | return new Date(year, month - 1, day); 28 | } 29 | 30 | async function main() { 31 | const uniqueIdentifier = () => 32 | Math.random() 33 | .toString(36) 34 | .replace(/[^a-z]+/g, ''); 35 | 36 | logseq.provideModel({ 37 | async insertFormattedBlock(e: any) { 38 | const { blockUuid, propertyName, title, displayer, range } = e.dataset 39 | var query = ` 40 | [:find (pull ?b [*]) 41 | :where 42 | [?b :block/properties ?p] 43 | [(get ?p :${propertyName})]] 44 | ` 45 | console.log(range) 46 | 47 | try { 48 | let ret = await logseq.DB.datascriptQuery(query) 49 | const result0 = ret?.flat() 50 | logseq.Editor.insertBlock(blockUuid, title, { sibling: false }) 51 | 52 | logseq.Editor.insertBlock(blockUuid, "Date", { sibling: false }) 53 | logseq.Editor.insertBlock(blockUuid, propertyName, { sibling: false }) 54 | 55 | let parentBlock = await logseq.Editor.getBlock(blockUuid, { includeChildren: true }) 56 | if (parentBlock?.children) { //Checking to make sure blocks were successfully created 57 | //defining constants 58 | let headerBlock = parentBlock.children[0] 59 | let x_value_block = parentBlock.children[1] 60 | let y_value_block = parentBlock.children[2] 61 | let header_uuid = headerBlock["uuid"] 62 | let x_uuid = x_value_block["uuid"]! 63 | let y_uuid = y_value_block["uuid"] 64 | let date = new Date() 65 | let cutoff = range * (1000 * 3600 * 24) 66 | 67 | if (result0 && result0.length > 0) { //Ensuring that the results of the datascript query isn't empty 68 | var results = [] 69 | for (const constant in result0) { 70 | try { 71 | if ([result0[constant]][0]["journal?"]) { 72 | if (![result0[constant]][0]["page"] != undefined) { 73 | console.log(cutoff) 74 | console.log(date.getTime() - convertDate([result0[constant]][0]["journal-day"]).getTime()) 75 | if (date.getTime() - convertDate([result0[constant]][0]["journal-day"]).getTime() < cutoff) { 76 | results.push([result0[constant]][0]) 77 | } 78 | } 79 | } 80 | } 81 | catch (err) { 82 | console.log(err) 83 | } 84 | } 85 | console.log(results) 86 | console.log(results) 87 | for (const constant in results) { 88 | if ([results[constant]][0]["original-name"] !== undefined) { 89 | logseq.Editor.insertBlock(x_uuid, [results[constant]][0]["original-name"], { sibling: false }); 90 | logseq.Editor.insertBlock(y_uuid, String([results[constant]][0]["properties"][propertyName]), { sibling: false }); 91 | } 92 | } 93 | 94 | } 95 | logseq.Editor.updateBlock(blockUuid, `{{renderer :${displayer}s_${uniqueIdentifier()}}}`); 96 | logseq.Editor.moveBlock(y_uuid, header_uuid, { children: true }); 97 | logseq.Editor.moveBlock(x_uuid, header_uuid, { children: true }); 98 | } 99 | 100 | } 101 | catch (err) { 102 | console.log(err) 103 | } 104 | 105 | }, 106 | }), 107 | logseq.provideStyle(` 108 | .formatter-btn { 109 | border: 1px solid var(--ls-border-color); 110 | white-space: initial; 111 | padding: 2px 4px; 112 | border-radius: 4px; 113 | user-select: none; 114 | cursor: default; 115 | display: flex; 116 | align-content: center; 117 | } 118 | 119 | .formatter-btn:hover { 120 | background-color: #defcf0; 121 | border-color: #9ddbc7; 122 | color: #0F9960; 123 | } 124 | `) 125 | 126 | logseq.Editor.registerSlashCommand('Property Visualizer', async () => { 127 | await logseq.Editor.insertAtEditingCursor(`{{renderer :property_visualizer, }}`); 128 | }); 129 | 130 | logseq.Editor.registerSlashCommand('Property Visualizer (guided)', async () => { 131 | await logseq.Editor.insertAtEditingCursor(`{{renderer :property_visualizer, property name, chart/table styling(eg. data nosum, area white 500), chart/table, date range}}`); 132 | }); 133 | 134 | logseq.App.onMacroRendererSlotted(async ({ slot, payload }) => { 135 | var [type, template, title, displayStyle, dateRange] = payload.arguments; 136 | if (title == undefined) { 137 | title = "data" 138 | } 139 | 140 | if (displayStyle == undefined) { 141 | displayStyle = "table" 142 | } 143 | if (dateRange == undefined) { 144 | dateRange = "10000" 145 | console.log("ultimate") 146 | } 147 | // logseq.Editor.removeBlock 148 | 149 | if (type == ':property_visualizer') { 150 | logseq.provideUI({ 151 | key: 'logseq visualizer plugin', 152 | reset: true, 153 | slot, 154 | template: ` 155 | 157 | `, 158 | }); 159 | } 160 | else return; 161 | }); 162 | } 163 | 164 | logseq.ready(main).catch(console.error) 165 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logseq-property-visualizer", 3 | "version": "1.0.4", 4 | "description": "A plugin to visualize and graph trends in page properties", 5 | "author": "@sawhney17", 6 | "license": "MIT", 7 | "scripts": { 8 | "dev": "parcel ./index.html --public-url ./", 9 | "build": "parcel build --public-url . --no-source-maps index.html" 10 | }, 11 | "devDependencies": { 12 | "@logseq/libs": "^0.0.1-alpha.34", 13 | "parcel": "^2.0.0" 14 | }, 15 | "logseq": { 16 | "id": "_km4vf3swk", 17 | "main": "dist/index.html", 18 | "icon": "./logo.png" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "ESNext", 8 | /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 9 | "module": "ESNext", 10 | /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 11 | // "lib": [], /* Specify library files to be included in the compilation. */ 12 | // "allowJs": true, /* Allow javascript files to be compiled. */ 13 | // "checkJs": true, /* Report errors in .js files. */ 14 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 15 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 16 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 17 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 18 | // "outFile": "./", /* Concatenate and emit output to single file. */ 19 | // "outDir": "./", /* Redirect output structure to the directory. */ 20 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 21 | // "composite": true, /* Enable project compilation */ 22 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 23 | // "removeComments": true, /* Do not emit comments to output. */ 24 | "noEmit": true, 25 | /* Do not emit outputs. */ 26 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 27 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 28 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 29 | 30 | /* Strict Type-Checking Options */ 31 | "strict": true, 32 | /* Enable all strict type-checking options. */ 33 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 34 | // "strictNullChecks": true, /* Enable strict null checks. */ 35 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 36 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 37 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 38 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 39 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 40 | 41 | /* Additional Checks */ 42 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 43 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 44 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 45 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 46 | 47 | /* Module Resolution Options */ 48 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 49 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 50 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 51 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 52 | // "typeRoots": [], /* List of folders to include type definitions from. */ 53 | // "types": [], /* Type declaration files to be included in compilation. */ 54 | "allowSyntheticDefaultImports": true, 55 | /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 56 | "esModuleInterop": true, 57 | /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 58 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 59 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 60 | 61 | /* Source Map Options */ 62 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 63 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 64 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 65 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 66 | 67 | /* Experimental Options */ 68 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 69 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 70 | 71 | /* Advanced Options */ 72 | "skipLibCheck": true, 73 | /* Skip type checking of declaration files. */ 74 | "forceConsistentCasingInFileNames": true 75 | /* Disallow inconsistently-cased references to the same file. */ 76 | } 77 | } 78 | --------------------------------------------------------------------------------