{"id":15541361,"url":"https://github.com/ivov/normative","last_synced_at":"2026-01-04T09:37:44.066Z","repository":{"id":55741024,"uuid":"258903215","full_name":"ivov/normative","owner":"ivov","description":"Desktop app for developing a legal dictionary, built with TypeScript, Electron, Firebase and MongoDB. Work in progress!","archived":false,"fork":false,"pushed_at":"2022-02-01T10:32:37.000Z","size":3053,"stargazers_count":3,"open_issues_count":5,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-02T06:16:20.741Z","etag":null,"topics":["docx","electron","firebase","mongodb","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ivov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-26T00:28:53.000Z","updated_at":"2023-05-01T22:28:31.000Z","dependencies_parsed_at":"2022-08-15T06:40:44.245Z","dependency_job_id":null,"html_url":"https://github.com/ivov/normative","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivov%2Fnormative","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivov%2Fnormative/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivov%2Fnormative/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivov%2Fnormative/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivov","download_url":"https://codeload.github.com/ivov/normative/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245013932,"owners_count":20547177,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docx","electron","firebase","mongodb","typescript"],"created_at":"2024-10-02T12:16:48.346Z","updated_at":"2026-01-04T09:37:44.041Z","avatar_url":"https://github.com/ivov.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# normative\r\n\r\n![](https://img.shields.io/github/last-commit/ivov/normative) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\r\n\r\nDesktop app for developing a legal dictionary and managing legal terminology.\r\n\r\nBuilt with TypeScript/Node, Electron, Firebase and MongoDB.\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"docs/ts.png\" width=\"150\"\u003e\r\n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\r\n    \u003cimg src=\"docs/electron.png\" width=\"140\"\u003e\r\n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\r\n    \u003cimg src=\"docs/fb.png\" width=\"145\"\u003e\r\n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\r\n    \u003cimg src=\"docs/mongodb.png\" width=\"73\"\u003e\r\n\u003c/p\u003e\r\n\r\n## Overview\r\n\r\nDesktop app for:\r\n\r\n1. parsing entries from two giant legal dictionaries in DOCX → `Parser`\r\n2. displaying, editing and exploring interconnected entries → `Client`\r\n\r\nThe app is intended to facilitate the development of a bilingual legal dictionary.\r\n\r\n:construction: **Work in progress**: The data conversion section of the app is complete, while the interface is under construction.\r\n\r\nFeatures:\r\n\r\n- User interface built with Tailwind CSS (forthcoming!)\r\n- TypeScript/Electron client and IPC channels\r\n- Authentication via Google Sign In + Firebase\r\n- Embedded web content from third-party sources (forthcoming!)\r\n- Storage of user preferences and search history (forthcoming!)\r\n- Snappy DOCX-to-JSON parser in TypeScript/Node\r\n- Completely tested and documented conversion process\r\n\r\n## Structure\r\n\r\n```\r\n.\r\n├── client            # desktop app\r\n├── config            # credentials management\r\n├── db                # DB managers, models, JSON/DOCX data\r\n├── docs              # documentation materials\r\n├── services          # parsing logic and helpers\r\n├── utils\r\n└── tests\r\n```\r\n\r\n## Installation\r\n\r\n1. Install [Node v10.15.1](https://nodejs.org/en/download/)\r\n2. Clone repo and install dependencies.\r\n3. Set up Firestore and Firebase Auth: See below\r\n4. Set up MongoDB: See below\r\n\r\n### Firestore and Firebase Auth setup\r\n\r\n1. Go to the [Firebase console](https://console.firebase.google.com/) and `Add project`\r\n2. Select `Database` from the left nav and `Create database`\r\n3. At the Firebase Console, go to `Project Overview` → `+ Add app` → `Web`\r\n4. Name the web app and note down its `apiKey`, `authDomain` and `projectId`.\r\n\r\n**TODO**: Write instructions for getting Google OAuth credentials.\r\n\r\n5. Create the following `.env` file at the `/config` dir.\r\n\r\n```\r\n# DOCX filepaths\r\nDOCX_PATH_ENGLISH=\"db/data/docx/sample_eng.docx\"\r\nDOCX_PATH_SPANISH=\"db/data/docx/sample_spa.docx\"\r\n\r\n# Firebase credentials\r\nFIREBASE_API_KEY=\"yourApiKey\"\r\nFIREBASE_AUTH_DOMAIN=\"your.auth.domain\"\r\nFIREBASE_PROJECT_ID=\"yourProjectId\"\r\n\r\n# Google OAuth credentials\r\nGOOGLE_OAUTH_CLIENT_ID=\"yourOAuthClientId\"\r\nGOOGLE_OAUTH_CLIENT_SECRET=\"yourOAuthClientSecret\"\r\nGOOGLE_OAUTH_REDIRECT_URI=\"yourOAuthRedirectUri\"\r\nGOOGLE_OAUTH_AUTHORIZE_URL=\"yourAuthorizeUrl\"\r\nGOOGLE_OAUTH_RESPONSE_TYPE=\"token\"\r\nGOOGLE_OAUTH_SCOPE=\"yourOauthScopes\"\r\n```\r\n\r\n### MongoDB setup\r\n\r\n1. Install [MongoDB Community Server](https://www.mongodb.com/download-center/community), including MongoDB compass.\r\n2. In MongoDB compass, connect to `mongodb://localhost:27017/`.\r\n3. Create a db called `normative` and, inside it, create two collections: `EnglishEntries` and `SpanishEntries`\r\n4. Index both collections on the `term` field.\r\n\r\n## Project scripts\r\n\r\n```\r\n$ npm run [script]\r\n```\r\n\r\n### Development\r\n\r\n| Script   | Action                                                                         |\r\n| -------- | ------------------------------------------------------------------------------ |\r\n| `client` | Run the Electron client.                                                       |\r\n| `dev`    | Run the Electron client while hot-reloading/recompiling any changes.           |\r\n| `test`   | Run ~30 unit tests for the parser.                                             |\r\n| `css`    | Optimize `./client/css/tailwind.css` with PostCSS plugins, only in production. |\r\n\r\n### JSON management\r\n\r\n| Script                 | Action                                                                          |\r\n| ---------------------- | ------------------------------------------------------------------------------- |\r\n| `conv:eng`             | Convert the source English DOCX into a single JSON file and a summary file.     |\r\n| `conv:spa`             | Convert the source Spanish DOCX into a single JSON file and a summary file.     |\r\n| `del:json:eng`         | Delete all JSON files in the `./conversion/json/English` directory.             |\r\n| `del:json:spa`         | Delete all JSON files in the `./conversion/json/Spanish` directory.             |\r\n| `log:entry:eng [term]` | Retrieve an English entry from JSON and log it to the console in pretty colors. |\r\n| `log:entry:spa [term]` | Retrieve a Spanish entry from JSON and log it to the console in pretty colors.  |\r\n\r\n### Firestore management\r\n\r\n| Script         | Action                                                                                          |\r\n| -------------- | ----------------------------------------------------------------------------------------------- |\r\n| `imp:fire:eng` | Import the single English JSON file and summary into the `EnglishEntries` Firestore collection. |\r\n| `imp:fire:spa` | Import the single Spanish JSON file and summary into the `SpanishEntries` Firestore collection. |\r\n| `del:fire:eng` | Delete all English entries from the `EnglishEntries` Firestore collection.                      |\r\n| `del:fire:spa` | Delete all Spanish entries from the `SpanishEntries` Firestore collection.                      |\r\n\r\n### MongoDB management\r\n\r\n| Script          | Action                                                                                        |\r\n| --------------- | --------------------------------------------------------------------------------------------- |\r\n| `imp:mongo:eng` | Import the single English JSON file and summary into the `EnglishEntries` MongoDB collection. |\r\n| `imp:mongo:spa` | Import the single Spanish JSON file and summary into the `SpanishEntries` MongoDB collection. |\r\n| `del:mongo:eng` | Delete all English entries from the `EnglishEntries` MongoDB collection.                      |\r\n| `del:mongo:spa` | Delete all Spanish entries from the `SpanishEntries` MongoDB collection.                      |\r\n\r\n## Parser\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"docs/cli.gif\"\u003e\r\n\u003c/p\u003e\r\n\r\nDesigned for efficiently parsing two giant English-Spanish and Spanish-English legal dictionaries in DOCX format, each containing over 90,000 entries with richly formatted fields such as `term`, `translation`, `definition`, `note`, `classifiedUnder`, `classifiedInto`, `tantamountTo`, `differentFrom`, `derivedFrom`, `derivedInto` and `reference`. Since the original DOCX files are proprietary, small samples of these files are included in the `./conversion/docx` directory.\r\n\r\nThe conversion process maps MS Word styles to custom tags for four fields, transforms all entries into HTML, extracts the text of custom tagged and non-custom-tagged (i.e. \"loose\") fields, parses each entry into an `Entry` object, and saves them all as one or multiple JSON files. Italics and superscript are retained!\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\r\n    \u003cimg src=\"docs/entry-docx.png\"\u003e\r\n\u003c/p\u003e\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\r\n    \u003cimg src=\"docs/entry-json2.png\"\u003e\r\n\u003c/p\u003e\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\r\n    \u003cimg src=\"docs/entry-cli.png\"\u003e\r\n\u003c/p\u003e\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\r\n    \u003cimg src=\"docs/entry-firestore.png\"\u003e\r\n\u003c/p\u003e\r\n\r\n### Test coverage for parser\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"docs/test-coverage-numbers.png\"\u003e\r\n\u003c/p\u003e\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"docs/test-coverage-desc.png\"\u003e\r\n\u003c/p\u003e\r\n\r\n## Client\r\n\r\nThe `Client` class controls the main process, spawns renderer processes and registers IPC channels for communication with the renderer processes. For now, the main process has three IPC channels registered for receiving and responding to requests from renderer processes: one for retrieving entries, one for retrieving summaries, and one for user login.\r\n\r\n```ts\r\nexport default class Client {\r\n\twindow: BrowserWindow | null;\r\n\tdb: DB;\r\n\r\n\tconstructor() {\r\n\t\tapp.on(\"ready\", this.createWindow);\r\n\t\tapp.on(\"window-all-closed\", this.onWindowAllClosed);\r\n\t\tapp.allowRendererProcessReuse = true;\r\n\r\n\t\tthis.db = new MongoDB(\"English\");\r\n\t\tthis.db.init();\r\n\r\n\t\tthis.registerIpcChannels();\r\n\t}\r\n\r\n\t/**Sets up all the channels for handling events from the renderer process.*/\r\n\tregisterIpcChannels() {\r\n\t\tconst ipcChannels: IpcChannel[] = [\r\n\t\t\tnew EntryChannel(this.db),\r\n\t\t\tnew SummaryChannel(this.db),\r\n\t\t\tnew AuthChannel()\r\n\t\t];\r\n\r\n\t\tipcChannels.forEach(channel =\u003e\r\n\t\t\tipcMain.on(channel.name, (event, argument?: string) =\u003e\r\n\t\t\t\tchannel.handle(event, argument)\r\n\t\t\t)\r\n\t\t);\r\n\t}\r\n\r\n\tprivate createWindow() {\r\n\t\t// renderer process\r\n\t\tthis.window = new BrowserWindow({\r\n\t\t\twidth: 800,\r\n\t\t\theight: 600,\r\n\t\t\tresizable: false,\r\n\t\t\twebPreferences: { nodeIntegration: true }\r\n\t\t});\r\n\r\n\t\tthis.window.loadURL(\"file://\" + process.cwd() + \"/client/index.html\");\r\n\t\tthis.window.webContents.openDevTools();\r\n\r\n\t\tthis.window.on(\"closed\", () =\u003e {\r\n\t\t\tthis.window = null; // ensure destruction!\r\n\t\t});\r\n\t}\r\n\r\n\tprivate onWindowAllClosed = () =\u003e {\r\n\t\tif (process.platform === \"darwin\") return; // replicate macOS\r\n\t\tthis.db.disconnect();\r\n\t\tapp.quit();\r\n\t};\r\n}\r\n```\r\n\r\nIPC requests originating in the view are encapsulated in the `Requester` class used inside the renderer process. This class forwards the requests to the `Client` and promisifies (i.e. returns inside a `Promise`) the responses it receives.\r\n\r\n```ts\r\nexport default class Requester {\r\n\tpublic request\u003cT\u003e(channel: string, term?: string): Promise\u003cT\u003e {\r\n\t\tipcRenderer.send(channel, term);\r\n\r\n\t\treturn new Promise(resolve =\u003e {\r\n\t\t\tipcRenderer.on(channel, (event, response) =\u003e {\r\n\t\t\t\tresolve(response);\r\n\t\t\t});\r\n\t\t});\r\n\t}\r\n}\r\n```\r\n\r\n## Author\r\n\r\n© 2020 Iván Ovejero\r\n\r\n## License\r\n\r\nDistributed under the MIT License. See [LICENSE.md](LICENSE.md)\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivov%2Fnormative","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivov%2Fnormative","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivov%2Fnormative/lists"}