### Clone and Install TimeTagger in Development Mode Source: https://github.com/almarklein/timetagger/blob/main/README.md Clone the repository and install the project in editable mode for development. This is the initial setup step. ```sh git clone https://github.com/almarklein/timetagger.git cd timetagger pip install -e . ``` -------------------------------- ### Install and Run TimeTagger Source: https://github.com/almarklein/timetagger/blob/main/README.md Install the TimeTagger library using pip and run the server from the command line. Ensure you have Python 3.6 or higher. ```bash # Install pip install -U timetagger # Run python -m timetagger ``` -------------------------------- ### Install Additional Developer Dependencies Source: https://github.com/almarklein/timetagger/blob/main/README.md Install essential tools for development, including linters, formatters, and testing frameworks. These are required for maintaining code quality. ```sh pip install invoke black flake8 pytest pytest-cov requests ``` -------------------------------- ### GET Settings Endpoint Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Retrieve all current settings by making a GET request to the settings endpoint. ```http GET ./settings ``` -------------------------------- ### API Base URL Examples Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Shows the default API base URL for a self-hosted instance and the URL for the official TimeTagger.app service. ```text http://localhost/timetagger/api/v2/ ``` ```text https://timetagger.app/api/v2/ ``` -------------------------------- ### GET settings Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Retrieves all settings objects configured for the TimeTagger instance. ```APIDOC ## GET ./settings ### Description Retrieves all settings objects configured for the TimeTagger instance. ### Method GET ### Endpoint ./settings ### Response #### Success Response (200) - **settings** (list) - A list of settings objects. ``` -------------------------------- ### GET version Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Retrieve the current version of the TimeTagger server. This is useful for compatibility checks and debugging. ```APIDOC ## GET version ### Description To get the server version, perform the following request. ### Method GET ### Endpoint ./version ### Response #### Success Response (200) - **version** (string) - A string indicating the TimeTagger server version. ### Response Example { "version": "1.2.3" } ``` -------------------------------- ### Get Updates from Server Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Clients can efficiently retrieve updated records and settings by providing a timestamp of the last known update. The response indicates if the client's cache needs to be reset. ```http GET ./updates?since= ``` -------------------------------- ### Get Server Version Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Retrieve the current version of the TimeTagger server. This is useful for compatibility checks and debugging. ```http GET ./version ``` -------------------------------- ### GET updates Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Clients can efficiently get updates by caching records and settings locally. This endpoint allows clients to retrieve all data changes since a specified timestamp. ```APIDOC ## GET updates ### Description Clients can cache the records and settings locally and efficiently get updates. Such clients have access to all the data, while also being up-to-date. The web client uses this approach (it never uses `GET records`). ### Method GET ### Endpoint ./updates?since= ### Query Parameters - **since** (timestamp) - Required - A timestamp indicating the last known update time on the client. ### Response #### Success Response (200) - **server_time** (timestamp) - The time of the server when the update was sampled. This should be used in the `since` field of the next update request. - **reset** (0 or 1) - Indicates whether the client should purge its local cache. - **records** (list) - A list of record objects that have changed since the provided timestamp. Can be empty. - **settings** (list) - A list of setting objects that have changed since the provided timestamp. Can be empty. ### Response Example { "server_time": 1678886400, "reset": 0, "records": [ { "key": "some-record-key", "t1": 1678882800, "t2": 1678886400, "ds": "Example record description", "mt": 1678886400, "st": 1678886400 } ], "settings": [ { "key": "some-setting-key", "value": "some-value", "mt": 1678886400, "st": 1678886400 } ] } ``` -------------------------------- ### GET Records Endpoint Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Retrieve records within a specified time range, with optional filters for running state, hidden status, and tags. ```http GET ./records?timerange=-&running=&hidden=&tag= ``` -------------------------------- ### Service Worker Registration and PWA Logic Source: https://github.com/almarklein/timetagger/blob/main/timetagger/app/index.md This function handles the registration of a service worker for PWA features. It includes logic for detecting PWA compatibility, managing installation prompts, and handling updates. ```javascript function register_service_worker() { // Could disable on localhost, because localhost is also likely used for other things. // However, since the SW is local to /timetagger/app by default, it should be fine. // if (location.hostname !== "localhost" && location.hostname !== "127.0.0.1") { return; } // SW supported? if (!('serviceWorker' in navigator)) { return; } // Structure for the PWA installation workflow window.pwa = { sw_reg: null, // set when sw is registered deferred_prompt: null, // set when browser considers this a PWA install: async function() { window.pwa.deferred_prompt.prompt(); const { outcome } = await window.pwa.deferred_prompt.userChoice; window.pwa.deferred_prompt = null; }, update: function () { if (window.pwa.sw_reg) { window.pwa.sw_reg.update(); } }, show_refresh_button: function () { let style, html, el; style = 'background:#fff; color:#444; padding:0.3em; border: 1px solid #777; border-radius:4px; '; style += 'position:absolute; top: 34px; left:4px; font-size:80%; '; html = "
"; html += "New version available, "; html += "refresh"; html += " to update.
" el = document.createElement("div"); el.innerHTML = html; el = el.children[0]; document.getElementById("canvas").parentNode.appendChild(el); } }; // Register the service worker navigator.serviceWorker.register('sw.js').then(reg => { window.pwa.sw_reg = reg; }); // Detect when the browser agrees that this is a PWA window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); // Prevent the mini-infobar from appearing on mobile window.pwa.deferred_prompt = e; // Store event for later use }); // Detect when a new service worker is activated. This happens after an update // (or just after page load) when a new SW is found, installed, and activated. var page_start_time = performance.now(); navigator.serviceWorker.addEventListener('controllerchange', function () { console.log("New service worker detected.") // Prevent continuous refresh when dev tool SW refresh is on if (page_start_time === null) { return; } if (performance.now() - page_start_time < 3000) { page_start_time = null; window.location.reload(); // User just arrived/refreshed, auto-refresh is ok } else { window.pwa.show_refresh_button(); // Prompt the user to refresh instead } }); // Auto-update each several hours var nhours = 4 tools.register_long_timer_in_secs("check_pwa_update", nhours * 3600, () => {window.pwa.update()}); } ``` -------------------------------- ### GET records Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Retrieves a list of records, optionally filtered by time range, running state, hidden state, and tags. Records are included if they are partially within the specified time range. ```APIDOC ## GET ./records ### Description Retrieves a list of records, optionally filtered by time range, running state, hidden state, and tags. Records are included if they are partially within the specified time range. ### Method GET ### Endpoint ./records ### Parameters #### Query Parameters - **timerange** (string) - Required - A hyphen-separated string of two Unix timestamps representing the start and end of the desired range. If the timestamps are equal, it queries records including that timestamp. If reversed, it queries records fully occupying the range. - **running** (boolean) - Optional - Filters records by their running state. Set to `true` for running records, `false` for stopped records. If unset, all records matching the time range are returned. - **hidden** (boolean) - Optional - Filters records by their hidden state. Set to `true` for hidden records, `false` for non-hidden records. If unset, all records matching the time range are returned. - **tag** (string) - Optional - Filters records by tags in their description. Provide one or more comma-separated tags (e.g., `work,urgent`). Omit the leading `#` character or URL-encode it. Only records containing all specified tags will be returned. ### Response #### Success Response (200) - **records** (list) - A list of record objects that are (partially) within the specified range. ``` -------------------------------- ### Initialize Demo Data Store and Canvas Source: https://github.com/almarklein/timetagger/blob/main/timetagger/app/demo.md Sets up the demo environment by initializing a simulated data store and the TimeTagger canvas. It also displays an informational dialog to the user. ```javascript window.addEventListener("load", function() { if (!window.browser_supported) {return;} window.store = new window.stores.DemoDataStore(); var canvas_element = document.getElementById('canvas'); window.canvas = new window.front.TimeTaggerCanvas(canvas_element); // Notify user that this is the Demo var dialog = new dialogs.DemoInfoDialog(window.canvas); setTimeout(dialog.open, 200); // In the demo, enter dev-mode when serving on localhost if (location.hostname == "localhost" || location.hostname == "127.0.0.1") { enable_check_update_on_dbl_click(); } }); ``` -------------------------------- ### Initialize Sandbox Environment Source: https://github.com/almarklein/timetagger/blob/main/timetagger/app/sandbox.md Sets up the SandboxDataStore and TimeTaggerCanvas upon page load. It also displays an informational dialog to the user. ```javascript window.addEventListener("load", function() { if (!window.browser_supported) {return;} window.store = new window.stores.SandboxDataStore(); var canvas_element = document.getElementById('canvas'); window.canvas = new window.front.TimeTaggerCanvas(canvas_element); // Notify user that this is the Sandbox var dialog = new dialogs.SandboxInfoDialog(window.canvas); setTimeout(dialog.open, 200); }); ``` -------------------------------- ### List Available Invoke Tasks Source: https://github.com/almarklein/timetagger/blob/main/README.md View all available tasks that can be run using the invoke command-line tool. This helps in understanding the development workflow. ```sh invoke -l ``` -------------------------------- ### Initialize and Refresh Data on Load Source: https://github.com/almarklein/timetagger/blob/main/timetagger/pages/account.md This script sets up an array of functions to refresh various data points and attaches an event listener to call these functions when the window finishes loading. This ensures that the account information is up-to-date when the page is accessed. ```javascript var refresh_functions = [refresh_auth_status, refresh_api_token]; function refresh() { for (let func of refresh_functions) { func(); } } window.addEventListener("load", refresh); ``` -------------------------------- ### Autoformat Code with Invoke and Black Source: https://github.com/almarklein/timetagger/blob/main/README.md Automatically format the project's code according to predefined style guidelines using the Black formatter. This ensures code consistency. ```sh invoke format ``` -------------------------------- ### timetagger.server.IMAGE_EXTS Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/libapi.md A list of supported image file extensions for the assets server. ```APIDOC ## timetagger.server.IMAGE_EXTS ### Description A list of supported image file extensions for the assets server. ### Type list[str] ``` -------------------------------- ### JavaScript for BCrypt Hashing and Credential Generation Source: https://github.com/almarklein/timetagger/blob/main/timetagger/common/cred.html This snippet handles user input for username and password, generates a BCrypt hash of the password, and constructs credential strings. It shows how to escape dollar signs for different use cases. ```javascript async function load() { let input_u = document.getElementById("input_u"); let input_p = document.getElementById("input_p"); input_u.oninput = calculate_result; input_p.oninput = calculate_result; document.getElementById("but_copy1").onclick = function() {copy_result(0);}; document.getElementById("but_copy2").onclick = function() {copy_result(1);}; document.getElementById("but_copy3").onclick = function() {copy_result(2);}; window.results = ["", "", ""]; } window.addEventListener('load', load); async function calculate_result() { let username = document.getElementById("input_u").value; let pw = document.getElementById("input_p").value; let hash = dcodeIO.bcrypt.hashSync(pw, 8); // Note that dollar signs need to be escaped for the replace function too window.results = [ username + ":" + hash, username + ":" + hash.replaceAll("$", "\\$$"), username + ":" + hash.replaceAll("$", "$$$$"), ]; document.getElementById("span_r1").innerText = window.results[0]; document.getElementById("span_r2").innerText = window.results[1]; document.getElementById("span_r3").innerText = window.results[2]; } async function copy_result(index) { navigator.clipboard.writeText(window.results[index]); } ``` -------------------------------- ### Handle Unsupported Browser Source: https://github.com/almarklein/timetagger/blob/main/timetagger/app/_template.html Adds an event listener for the 'load' event to check browser support. If the browser is not supported, it updates the main content element with a message instructing the user to use a modern browser. ```javascript window.addEventListener("load", function() { var el = document.getElementById("main-content"); if (!window.browser_supported) { el.innerHTML = "This browser is not supported, please use (a modern version of) Firefox, Chrome, Safari, or Edge."; } }); ``` -------------------------------- ### timetagger.server.FONT_EXTS Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/libapi.md A list of supported font file extensions for the assets server. ```APIDOC ## timetagger.server.FONT_EXTS ### Description A list of supported font file extensions for the assets server. ### Type list[str] ``` -------------------------------- ### PUT settings Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Updates settings by sending a JSON-encoded list of settings objects in the request body. ```APIDOC ## PUT ./settings ### Description Updates settings by sending a JSON-encoded list of settings objects in the request body. ### Method PUT ### Endpoint ./settings ### Request Body - **settings** (list) - Required - A JSON-encoded list of settings objects to be updated. ### Response #### Success Response (200) - **accepted** (list) - The keys of the accepted settings. - **failed** (list) - The keys of the rejected settings. - **errors** (object) - Error messages corresponding to the rejected settings and possibly additional error messages. ``` -------------------------------- ### Set Demo Time Source: https://github.com/almarklein/timetagger/blob/main/timetagger/app/demo.md Allows setting a specific 'current' time for the demo, useful for generating consistent screenshots. It overrides the native `dt.now` function. ```javascript function set_demotime() { // Call to run demo at a specific moment in time, nice for making screenshots var demodeltatime = dt.now() - new Date("2021-04-07T16:15:00").getTime() / 1000; dt.now = function() { return new Date().getTime() / 1000 - demodeltatime}; } ``` -------------------------------- ### timetagger.server.create_assets_from_dir Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/libapi.md Creates assets from a directory. This function is used by the assets server to process and prepare assets. ```APIDOC ## timetagger.server.create_assets_from_dir ### Description Creates assets from a directory. This function is used by the assets server to process and prepare assets. ### Function Signature `create_assets_from_dir(directory: str) -> None` ### Parameters * **directory** (str) - The path to the directory containing assets. ``` -------------------------------- ### Initialize Timetagger Version and Feature Detection Source: https://github.com/almarklein/timetagger/blob/main/timetagger/app/_template.html Sets the application version and detects essential browser features required for Timetagger to run. It checks for crypto, getRandomValues, atob, history, Uint8Array, localStorage, indexedDB, and Path2D. ```javascript window.timetaggerversion = '{{ versionstring }}'; window.latest_release_version = null; // Detect features window.browser_supported = false; if (window.crypto && window.crypto.getRandomValues && window.atob && window.history && window.Uint8Array && window.localStorage && window.indexedDB && window.Path2D ) { window.browser_supported = true; // probably } ``` -------------------------------- ### JavaScript Login Functions Source: https://github.com/almarklein/timetagger/blob/main/timetagger/pages/login.md Contains the core JavaScript functions for handling user login, including authentication requests, response handling, and token management. It requires the 'tools.js' script. ```javascript async function login(payload, silent) { // Reset status let el = document.getElementById("result"); if (!silent) { el.innerHTML = "Logging in ..." } await tools.sleepms(100); // The body is obfuscated with base64, but not encrypted. let body = btoa(JSON.stringify(payload)); // Do request let url = tools.build_api_url("bootstrap_authentication"); let init = {method: "POST", headers: {}, body: body}; let res = await fetch(url, init); // Handle response if (res.status != 200) { if (!silent) { let text = await res.text(); el.innerText = "Could not get token: " + text; el.innerHTML = el.innerHTML + "
TimeTagger home"; } } else { let token = JSON.parse(await res.text()).token; tools.set_auth_info_from_token(token); el.innerText = "Token exchange succesful"; let state = tools.url2dict(location.hash); location.replace(state.page || "./app/"); } } async function login_localhost() { await login({"method": "localhost"}); } async function login_credentials() { let input_u = document.getElementById("input_u"); let input_p = document.getElementById("input_p"); await login({"method": "usernamepassword", "username": input_u.value, "password": input_p.value}); } async function load() { let but1 = document.getElementById("submit_up"); let but2 = document.getElementById("submit_localhost"); let input_p = document.getElementById("input_p"); but1.onclick = login_credentials; but2.onclick = login_localhost; input_p.onkeydown = function (e) { if (e.key == "Enter" || e.key == "Return") {login_credentials();} }; if (location.hostname == "localhost" || location.hostname == "127.0.0.1") { but2.style.display = "block"; } // Try to autheticate through a reverse proxy but ignore the unsuccessful result await login({"method": "proxy"}, true); } window.addEventListener('load', load); ``` -------------------------------- ### TimeTagger Authentication with Credentials Source: https://github.com/almarklein/timetagger/blob/main/README.md Configure TimeTagger authentication using username and BCrypt hash via command-line arguments or environment variables. The hash can be generated on the timetagger.app website. ```bash # Using command-line args python -m timetagger --credentials=test:$2a$08$0CD1NFiIbancwWsu3se1v.RNR/b7YeZd71yg3cZ/3whGlyU6Iny5i # Using environment variables export TIMETAGGER_CREDENTIALS='test:$2a$08$0CD1NFiIbancwWsu3se1v.RNR/b7YeZd71yg3cZ/3whGlyU6Iny5i' python -m timetagger ``` -------------------------------- ### Run Tests with Invoke and Pytest Source: https://github.com/almarklein/timetagger/blob/main/README.md Execute all project tests using Pytest. This command is crucial for verifying the functionality and stability of the codebase. ```sh invoke tests ``` -------------------------------- ### Copy API Key to Clipboard Source: https://github.com/almarklein/timetagger/blob/main/timetagger/pages/account.md This function copies the content of the API token display element to the user's clipboard. It provides visual feedback by changing the button icon after a successful copy operation. ```javascript async function copy_api_key() { let el = document.getElementById('apitoken'); let but = document.getElementById('copyapikey'); tools.copy_dom_node(el) but.innerHTML = ""; await tools.sleepms(1000) but.innerHTML = ""; } ``` -------------------------------- ### PUT Settings Endpoint Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Update settings by sending a JSON-encoded list of settings objects in the request body. ```http PUT ./settings ``` -------------------------------- ### Clean Temporary Files with Invoke Source: https://github.com/almarklein/timetagger/blob/main/README.md Remove temporary files generated during development. This command helps in maintaining a clean project environment. ```sh invoke clean ``` -------------------------------- ### Initialize TimeTagger App and Register Service Worker Source: https://github.com/almarklein/timetagger/blob/main/timetagger/app/index.md This snippet initializes the TimeTagger application's data store and canvas on page load. It also calls a function to register the service worker for PWA functionality. ```javascript window.addEventListener("load", function() { if (!window.browser_supported) {return;} window.store = new window.stores.ConnectedDataStore(); var canvas_element = document.getElementById('canvas'); window.canvas = new window.front.TimeTaggerCanvas(canvas_element); // Register service worker, only when loading the actual app. register_service_worker(); }); ``` -------------------------------- ### TimeTagger Authentication with Reverse Proxy Source: https://github.com/almarklein/timetagger/blob/main/README.md Enable and configure TimeTagger to use authentication information provided by a reverse proxy via HTTP headers. Specify trusted proxy IPs and the header name. ```bash # Using command-line args python -m timetagger --proxy_auth_enabled=True --proxy_auth_trusted=127.0.0.1 --proxy_auth_header=X-Remote-User # Using environment variables export TIMETAGGER_PROXY_AUTH_ENABLED=True TIMETAGGER_PROXY_AUTH_TRUSTED=127.0.0.1 TIMETAGGER_PROXY_AUTH_HEADER=X-Remote-User python -m timetagger ``` -------------------------------- ### Lint Code with Invoke and Flake8 Source: https://github.com/almarklein/timetagger/blob/main/README.md Detect linting errors and style issues in the code using Flake8. This command helps in identifying potential bugs and improving code quality. ```sh invoke lint ``` -------------------------------- ### timetagger.server.get_webtoken_unsafe Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/libapi.md Generates a web token in an unsafe manner. This function is intended for specific internal use cases and should be used with caution. ```APIDOC ## timetagger.server.get_webtoken_unsafe ### Description Generates a web token in an unsafe manner. This function is intended for specific internal use cases and should be used with caution. ### Function Signature `get_webtoken_unsafe(user: str) -> str` ### Parameters * **user** (str) - The user identifier for whom the token is generated. ### Returns * (str) - The generated web token. ``` -------------------------------- ### timetagger.server.authenticate Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/libapi.md Handles user authentication for the API server. This function is part of the TimeTagger library's API server functionalities. ```APIDOC ## timetagger.server.authenticate ### Description Handles user authentication for the API server. This function is part of the TimeTagger library's API server functionalities. ### Function Signature `authenticate(token: str) -> dict` ### Parameters * **token** (str) - The authentication token. ### Returns * (dict) - Authentication details. ``` -------------------------------- ### timetagger.server.AuthException Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/libapi.md Custom exception class for authentication errors in the TimeTagger API server. ```APIDOC ## timetagger.server.AuthException ### Description Custom exception class for authentication errors in the TimeTagger API server. ### Usage This exception is raised when authentication fails. ``` -------------------------------- ### timetagger.server.enable_service_worker Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/libapi.md Enables the service worker for the assets server. This function configures the service worker for enhanced asset delivery. ```APIDOC ## timetagger.server.enable_service_worker ### Description Enables the service worker for the assets server. This function configures the service worker for enhanced asset delivery. ### Function Signature `enable_service_worker() -> None` ``` -------------------------------- ### Reset API Key Source: https://github.com/almarklein/timetagger/blob/main/timetagger/pages/account.md This function is a convenience wrapper to call the refresh_api_token function with the reset parameter set to true, effectively resetting the API token. ```javascript async function reset_api_key() { await refresh_api_token(true); } ``` -------------------------------- ### Enable Dev-Mode with Double-Click Auto-Refresh Source: https://github.com/almarklein/timetagger/blob/main/timetagger/app/demo.md Enables a developer mode feature where a double-click on the page triggers a service worker update check. If a new version is detected, the page will auto-refresh. ```javascript function enable_check_update_on_dbl_click() { // More of a dev-mode so we can make a change, restart server, // and then double-click in app to auto-refresh when new version is detected. // SW supported? if (!('serviceWorker' in navigator)) { return; } // Structure for the PWA workflow window.pwa = { sw_reg: null, // set when sw is registered update: function () { console.log("Checking for update ...") if (window.pwa.sw_reg) { window.pwa.sw_reg.update(); } }, }; // Register the service worker navigator.serviceWorker.register('sw.js').then(reg => { window.pwa.sw_reg = reg; }); // Detect when a new service worker is activated. This happens after an update // (or just after page load) when a new SW is found, installed, and activated. var page_start_time = performance.now(); navigator.serviceWorker.addEventListener('controllerchange', function () { console.log("New service worker detected.") // Prevent continuous refresh when dev tool SW refresh is on if (page_start_time === null) { return; } if (performance.now() - page_start_time < 3000) { page_start_time = null; window.location.reload(); // User just arrived/refreshed, auto-refresh is ok } }); // Double-click invokes an update that will auto-refresh when a new version is found document.body.ondblclick = function () { page_start_time = performance.now(); window.pwa.update(); }; } ``` -------------------------------- ### Reset Web Token Seed Source: https://github.com/almarklein/timetagger/blob/main/timetagger/pages/account.md This function resets the web token seed, which logs out all other devices associated with the user's account. It provides visual feedback during the reset process. ```javascript async function reset_webtoken_seed() { let el = document.getElementById('logoutallbutton'); el.innerHTML = "Resetting web token seed ..."; await tools.renew_webtoken(true, true); await tools.sleepms(1000); el.innerHTML = "Done!"; await tools.sleepms(1000); el.innerHTML = "Logout all other devices"; } ``` -------------------------------- ### timetagger.server.md2html Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/libapi.md Converts Markdown text to HTML. This function is used by the assets server to render Markdown content. ```APIDOC ## timetagger.server.md2html ### Description Converts Markdown text to HTML. This function is used by the assets server to render Markdown content. ### Function Signature `md2html(md_text: str) -> str` ### Parameters * **md_text** (str) - The Markdown text to convert. ### Returns * (str) - The converted HTML text. ``` -------------------------------- ### PUT Records Endpoint Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Submit new records or update existing ones by sending a JSON-encoded list of record objects in the request body. ```http PUT ./records ``` -------------------------------- ### JavaScript Logout Function Source: https://github.com/almarklein/timetagger/blob/main/timetagger/pages/logout.md Handles the user logout process by calling the tools.logout() function, parsing the URL hash for redirection, and then replacing the current history entry. This script is automatically executed on page load. ```javascript async function logout() { await tools.logout(); let state = tools.url2dict(location.hash); location.replace(state.page || "./"); } window.addEventListener('load', logout); ``` -------------------------------- ### PUT records Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/webapi.md Edits existing records or submits new records by sending a JSON-encoded list of record objects in the request body. ```APIDOC ## PUT ./records ### Description Edits existing records or submits new records by sending a JSON-encoded list of record objects in the request body. ### Method PUT ### Endpoint ./records ### Request Body - **records** (list) - Required - A JSON-encoded list of record objects to be created or updated. ### Response #### Success Response (200) - **accepted** (list) - The keys of the accepted records. - **failed** (list) - The keys of the rejected records. - **errors** (object) - Error messages corresponding to the rejected records and possibly additional error messages. ``` -------------------------------- ### timetagger.server.user2filename Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/libapi.md Converts a user identifier to a filename. This utility function is part of the TimeTagger library's server-side utilities. ```APIDOC ## timetagger.server.user2filename ### Description Converts a user identifier to a filename. This utility function is part of the TimeTagger library's server-side utilities. ### Function Signature `user2filename(user: str) -> str` ### Parameters * **user** (str) - The user identifier. ### Returns * (str) - The corresponding filename. ``` -------------------------------- ### timetagger.server.api_handler_triage Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/libapi.md Triage function for handling API requests. This function routes incoming API requests to the appropriate handlers. ```APIDOC ## timetagger.server.api_handler_triage ### Description Triage function for handling API requests. This function routes incoming API requests to the appropriate handlers. ### Function Signature `api_handler_triage(request: dict) -> dict` ### Parameters * **request** (dict) - The incoming API request object. ### Returns * (dict) - The result of the API request handling. ``` -------------------------------- ### Refresh API Token Source: https://github.com/almarklein/timetagger/blob/main/timetagger/pages/account.md This function retrieves the user's API token. It can optionally reset the token if the 'reset' parameter is true. The function updates the UI with the token or an error message and manages the state of the 'Reset API token' button. ```javascript async function refresh_api_token(reset) { let el = document.getElementById('apitoken'); let resetapikeybutton = document.getElementById('resetapikey'); let auth = tools.get_auth_info(); el.innerHTML = "Getting Getting API token ..."; await tools.sleepms(200); if (auth) { let url = tools.build_api_url("apitoken"); if (reset) { url += "?reset=1"; } let init = {method: "GET", headers:{authtoken: auth.token}}; let res = await fetch(url, init); if (res.status != 200) { el.innerText = "Fail: " + await res.text(); return; } d = JSON.parse(await res.text()); el.innerText = d.token; resetapikeybutton.disabled = false; } else { el.innerHTML = "Not available."; resetapikeybutton.disabled = true; } } ``` -------------------------------- ### timetagger.server.filename2user Source: https://github.com/almarklein/timetagger/blob/main/docs/docs/libapi.md Converts a filename to a user identifier. This utility function is part of the TimeTagger library's server-side utilities. ```APIDOC ## timetagger.server.filename2user ### Description Converts a filename to a user identifier. This utility function is part of the TimeTagger library's server-side utilities. ### Function Signature `filename2user(filename: str) -> str` ### Parameters * **filename** (str) - The filename. ### Returns * (str) - The corresponding user identifier. ``` -------------------------------- ### Refresh Authentication Status Source: https://github.com/almarklein/timetagger/blob/main/timetagger/pages/account.md This function retrieves and displays the current authentication status of the user. It updates the UI to show if the user is logged in and with which username. It also enables or disables the 'Logout all other devices' button based on the login status. ```javascript async function refresh_auth_status() { let el = document.getElementById('authstatus'); let logoutallbutton = document.getElementById('logoutallbutton'); el.innerHTML = "Getting auth status ..."; await tools.sleepms(200); let auth = tools.get_auth_info(); if (auth) { let html = "Logged in as " + auth.username + ""; el.innerHTML = html; logoutallbutton.disabled = false; } else { el.innerHTML = "Not logged in."; logoutallbutton.disabled = true; } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.