[{"content":"When I was writing the code for qlBuilder reload command I knew I\u0026rsquo;ll have to come back to it, at some point, and refactor the part that is displaying the reload progress. The plan was to separate this functionality as a separate module or even a package so it can be used elsewhere as well.\nAnd that time has finally came. But instead of a separate package this functionality is now part of enigma-mixin package.\nThe usage of mGetReloadProgress mixin functionality is a bit niche but it can be used to get the reload progress while reloading an app via enigma.js (same as what Qlik is displaying when reloading an app).\nWith vanilla enigma.js we have to use GetProgress() API method. But working with it can be a bit tricky. Or at least not trivial.\nThe main difference between this and the majority of the other API methods is that reloading an app is a long running process and we\u0026rsquo;ll have to call GetProgress() periodically (every X ms) while the app is reloading in order to get the current progress.\nThe principle here is to not await for the DoReload() (or DoReloadEx()) method but to wrap it in a Promise and inside it to start the reload. After the reload promise is started we\u0026rsquo;ll start pooling every X ms to get the reload progress. Once the doReload() is complete we\u0026rsquo;ll resolve the main promise and return the doReload() response.\nconst doc = await global.openDoc(\u0026#34;some-app-id\u0026#34;); const reloadApp = await new Promise(function (resolve, reject) { // start the app reload doc.doReload().then((r) =\u0026gt; { // once the app is reloaded resolve the main promise resolve(r); }); // while the app is reloading get the reload progress // every 200ms and do something with the response data setInterval(function () { global.getProgress().then(function (msg) { //do something with the message here }) }, 200) }); // continue here after the app reload is complete The above snippet gives us an idea how to work with getProgress() method. The next (main?) issue will be to parse the response data of getProgress(). And that can also be challenging due to the nature of the messages/progress.\nAs you can see getting the reload progress is not as simple as it sounds.\nTo simplify the process of getting the reload progress I\u0026rsquo;ve prepared an enigma.js mixin. More about mixin can be found here and here\nBy \u0026ldquo;simplify\u0026rdquo; I mean that you won\u0026rsquo;t have to deal with getProgress() response data and the hassle that this can bring. I can talk a bit more about the response structure but that can be \u0026hellip; boring 😄(Reach out if you want to know more)\nThe pseudo code below demonstrates how to import and use the mixin into enigma.js session and how to use it:\nimport { globalMixin } from \u0026#34;enigma-mixin\u0026#34;; const session = enigma.create({ schema, mixins: globalMixin, url: `ws://my-qlik-sense/app/engineData`, createSocket: (url) =\u0026gt; new WebSocket(url), }); // open the web socket connection const global = await session.open(); // open the required app const doc = await global.openDoc(\u0026#34;some-app-id\u0026#34;); // init the get progress mixin const reloadProgress = global.mGetReloadProgress(); // once the mixin is initialized we can // prepare the emitter reloadProgress.emitter.on(\u0026#34;progress\u0026#34;, (msg) =\u0026gt; { // here we will receive the reload messages console.log(msg); }); const reloadApp = await new Promise(function (resolve, reject) { // or doc.doReloadEx doc.doReload().then((r) =\u0026gt; { // give it another few ms to make sure we have all the messages setTimeout(function () { // stop the mixin from pooling for new messages. // The app reload is already complete at this point reloadProgress.stop(); // resolve the main promise resolve(r); }, 300); }); // once the reload is started we\u0026#39;ll start the pooling reloadProgress.start(); }); The parsed and formatted reload messages will be available inside the reloadProgress.emitter.on(\u0026quot;progress\u0026quot;)... function. At the end the messages here will (should) be identical to what is displayed in Qlik.\nQlik (left) and mixin (right) reload results\nCheck out the source code for more details about this specific mixin or if you want to know more about the other available mixins. The mGetReloadProgress mixin have a few (optional) parameters that can be set. Like how often to pool for progress and to include/exclude all transient messages.\nP.S. The same setup can be used with doSave() method as well. Although it seems that Qlik is not sending any data in the response. Not sure if this is helpful in some way \u0026hellip; just saying 😉\nStefan\n","permalink":"https://sstoichev.eu/post/enigma-mixin-reload-progress/","summary":"\u003cp\u003eWhen I was writing the code for \u003ca href=\"https://github.com/Informatiqal/qlbuilder\"\u003eqlBuilder\u003c/a\u003e \u003ccode\u003ereload\u003c/code\u003e command I knew I\u0026rsquo;ll have to come back to it, at some point, and refactor the part that is displaying the reload progress. The plan was to separate this functionality as a separate module or even a package so it can be used elsewhere as well.\u003c/p\u003e","title":"Get app reload progress (enigma.js mixin)"},{"content":"While working with Qlik we can encouter errors or messages that can be a bit cryptic and contain just an error/message code.\nVery often these codes are followed by a short description. But sometimes either the description is missing or its \u0026hellip; to short :)\nFinally found some free time to compile a list of the error/messages codes, their short and long description.\nMy data source is a few years old so most likely is not complete. But still there are 300+ codes there.\nRepository\nIf you happen to know more of these, please feel free to open PR in the repo.\nStefan\n","permalink":"https://sstoichev.eu/post/qlik-error-messages-codes/","summary":"\u003cp\u003eWhile working with Qlik we can encouter errors or messages that can be a bit cryptic and contain just an error/message code.\u003c/p\u003e\n\u003cp\u003eVery often these codes are followed by a short description. But sometimes either the description is missing or its \u0026hellip; to short :)\u003c/p\u003e\n\u003cp\u003eFinally found some free time to compile a list of the error/messages codes, their short and long description.\u003c/p\u003e\n\u003cp\u003eMy data source is a few years old so most likely is not complete. But still there are 300+ codes there.\u003c/p\u003e","title":"Qlik Error/Messages Codes"},{"content":"The original announcement can be found here\nAutomatiqal CLI is officially entering its Beta phase!\nYou might not be aware but Automatiqal CLI allows Qlik Sense (QSEoW) deployments/administration tasks to be described in YAML/JSON files.\nAutomatiqal CLI supports:\nmultiple authentication methods multiple ways to define variables define onError blocks and a lot more! Please have a look at the introduction video below. More videos will be added soon!\nAny feedback is greatly appreciated!\nStefan\n","permalink":"https://sstoichev.eu/post/automatiqal-cli-beta-cross-post/","summary":"\u003cp\u003eThe original announcement can be found here\u003c/p\u003e\n\u003cp\u003eAutomatiqal CLI is officially entering its Beta phase!\u003c/p\u003e\n\u003cp\u003eYou might not be aware but Automatiqal CLI allows Qlik Sense (QSEoW) deployments/administration tasks to be described in YAML/JSON files.\u003c/p\u003e\n\u003cp\u003eAutomatiqal CLI supports:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003emultiple authentication methods\u003c/li\u003e\n\u003cli\u003emultiple ways to define variables\u003c/li\u003e\n\u003cli\u003edefine onError blocks\u003c/li\u003e\n\u003cli\u003eand a lot more!\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003ePlease have a look at the introduction video below. More videos will be added soon!\u003c/p\u003e\n\u003cdiv style=\"position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;\"\u003e\n      \u003ciframe allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen\" loading=\"eager\" referrerpolicy=\"strict-origin-when-cross-origin\" src=\"https://www.youtube.com/embed/ZU13H9uw1lM?autoplay=0\u0026amp;controls=1\u0026amp;end=0\u0026amp;loop=0\u0026amp;mute=0\u0026amp;start=0\" style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;\" title=\"YouTube video\"\u003e\u003c/iframe\u003e\n    \u003c/div\u003e\n\n\u003c!-- https://youtu.be/?si=bVvksGnSBwMxEolC --\u003e\n\u003cp\u003eAny feedback is greatly appreciated!\u003c/p\u003e","title":"Automatiqal CLI Beta! (Cross-Post)"},{"content":"In one of the previous post we saw how to use web ticket to establish connection with Qlik Sense Engine. And probably for most cases the approach there is enough.\nBut what if our web app have a requirement to communicate with other Qlik service(s) (in addition to the Engine)? For example, we want to list the reload tasks to which the user have access (this information is provided by Qlik Repository Service REST API)?\nThe problem with the web ticket is that once it is consumed it can\u0026rsquo;t be used again. And if we use the ticket to open web socket Engine connection then we can\u0026rsquo;t use the same ticket to get the list of the reload tasks. We\u0026rsquo;ll end up with situation where the Engine connection is authenticated and established but upon requesting the list of reload tasks Qlik will response with Unauthenticated.\nTo overcome this we can \u0026ldquo;exchange\u0026rdquo; the web ticket for a Qlik session. The exchange will be in a form of a session cookie. We\u0026rsquo;ll have the session cookie set in the browser and then our web app can freely open Engine connection (without web ticket) and communicate with other Qlik Sense services. In similar way the Hub is doing it.\nPre-requisites (QMC) Couple of settings have to be set in the Virtual Proxy before we can start (if they are not set already):\nWhite list - add the web app url to the host white list SameSite attributes - make sure that the SameSite attributes are set to None (at least) Code workflow The code workflow is:\nget web ticket from Qlik (in the same manner as in the previous post) use the ticket to retrieve the content of any static Qlik resource via HTTP request. In this example I\u0026rsquo;m going to use /resources/autogenerated/qlik-styles.css but it can be any other static file that requires authentication in order to be viewed/downloaded since we are using the ticket in HTTP request the response from Qlik will be the content of the requested resource + session cookie once session cookie is set (the browser is taking care of this) we can continue and connect to the Engine and \u0026ldquo;ask\u0026rdquo; Qlik for different data (in our case get list of reload tasks) Code retrieve the static file content export const load = async ({ url, params }) =\u0026gt; { // look for \u0026#34;qlikTicket\u0026#34; query parameter const qlikTicket = url.searchParams.get(\u0026#34;qlikTicket\u0026#34;); // if \u0026#34;qlikTicket\u0026#34; is present // use the ticket to retrieve the content of \u0026#34;qlik-styles.css\u0026#34; // if all is ok Qlik will send the session cookie // and the browser will set it if (qlikTicket) { const qlikStylesURL = `https://${qsHost}/resources/autogenerated/qlik-styles.css?QlikTicket=${qlikTicket}`; const qlikStylesResponse = await fetch(qlikStylesURL, { credentials: \u0026#34;include\u0026#34;, }); } return true; }; variables const qsHost = \u0026#34;my-sense-instance.com\u0026#34;; const reloadURI = `http://localhost:3000`; const xrfkey = \u0026#34;1234567890123456\u0026#34;; const qsReloadTasksURL = `https://${qsHost}/qrs/reloadtask?Xrfkey=${xrfkey}`; let qlikApps = []; let qlikReloadTasks = []; Notification events // redirect to the login page if the Engine connection response indicates that we have to authenticate session.on(\u0026#34;notification:OnAuthenticationInformation\u0026#34;, (data) =\u0026gt; { if (data.loginUri) goto(data.loginUri); }); // once the Engine connection is established, get the list of the reload tasks (in the logged in user context) session.on(\u0026#34;notification:OnConnected\u0026#34;, async () =\u0026gt; { qlikReloadTasks = await fetch(qsReloadTasksURL, { credentials: \u0026#34;include\u0026#34;, headers: { \u0026#34;X-Qlik-Xrfkey\u0026#34;: `${xrfkey}`, }, }).then((res) =\u0026gt; res.json()); }); HTML structure \u0026lt;main\u0026gt; \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;Qlik apps:\u0026lt;/h1\u0026gt; {#each qlikApps as qlikApp} \u0026lt;div title={qlikApp.qDocId}\u0026gt;{qlikApp.qDocName}\u0026lt;/div\u0026gt; {/each} \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;Qlik Reload Tasks:\u0026lt;/h1\u0026gt; {#each qlikReloadTasks as reloadTask} \u0026lt;div title={reloadTask.id}\u0026gt;{reloadTask.name}\u0026lt;/div\u0026gt; {/each} \u0026lt;/div\u0026gt; \u0026lt;/main\u0026gt; Result Once the whole process is complete we should see something like:\nAnd if we inspect the network request will see:\nthe qlik-styles.css request is with attached QlikTicket the response is 200 OK and the response headers are containing the session cookie qlik-styles.css response\nqlik-styles.css response headers\nwe can see the cookie being set in the browser Browser (session) cookie\nboth the Engine connection and Repository API request are made without any ticket added to the urls Web Socket (Engine) connection\nHTTP (Repository API) connection\nComplete code The full code is available HERE. Follow the readme there to install and run it.\nHope you liked it!\nStefan\n","permalink":"https://sstoichev.eu/post/web-app-authentication-with-qseow-web-ticket-session-cookie/","summary":"\u003cp\u003eIn one of the previous \u003ca href=\"__GHOST_URL__/2021/09/09/qseow-authentication-svelte/\"\u003epost\u003c/a\u003e we saw how to use web ticket to establish connection with Qlik Sense Engine. And probably for most cases the approach there is enough.\u003c/p\u003e\n\u003cp\u003eBut what if our web app have a requirement to communicate with other Qlik service(s) (in addition to the Engine)? For example, we want to list the reload tasks to which the user have access (this information is provided by \u003ca href=\"https://help.qlik.com/en-US/sense-developer/May2021/Subsystems/RepositoryServiceAPI/Content/Sense_RepositoryServiceAPI/RepositoryServiceAPI-Introduction.htm\"\u003eQlik Repository Service REST API\u003c/a\u003e)?\u003c/p\u003e","title":"Web app authentication with QSEoW (web ticket + session cookie)"},{"content":"The initial idea was simple \u0026hellip; on paper. Spend few days just to prove that its possible. And it was doable. But to achieve it I had to build solid foundation.\nI\u0026rsquo;m not going to say what was the initial idea \u0026hellip; yet. But will share where it\u0026rsquo;s \u0026ldquo;home\u0026rdquo; will be and will release all the components of the \u0026ldquo;foundation\u0026rdquo;.\nSince there will be multiple components I\u0026rsquo;ve decided that it\u0026rsquo;s a good idea to separate them in their own space.\nSo let me introduce you to Informatiqal\nInformatiqal has it\u0026rsquo;s own website and it\u0026rsquo;s own GitHub organization:\nCode for a few mode modules is publicly available but want to meet you with the \u0026ldquo;base of the base\u0026rdquo;. The others are still WIP.\nQlik REST API This a \u0026ldquo;generic\u0026rdquo; package. It simplifies the communication with various Qlik Sense REST API endpoints (Repository, Proxy, SaaS etc.) but still we have to know what actual endpoint have to be called and what data has to be passed.\nBut on top of it few more packages are/will be created which are going to make the development process more pleasant.\nqlik-rest-api can be used from within both NodeJS and browser(s). The example below shows how certificate authentication can be used (from NodeJS) to communicate with Qlik Repository REST API.\nimport fs from \u0026#34;fs\u0026#34;; import https from \u0026#34;https\u0026#34;; // read the certificates const crt = fs.readFileSync(\u0026#34;path/to/certificate.pem\u0026#34;); const key = fs.readFileSync(\u0026#34;path/to/certificate_key.pem\u0026#34;); // init https agent const httpsAgent = new https.Agent({ rejectUnauthorized: false, cert: crt, key: key, }); // create the config object by passing the created httpAgent const config = { host: \u0026#34;my-qlik-sense-instance\u0026#34;, port: 4242, // optional. default is 4242 httpsAgent: httpsAgent, authentication: { user_dir: \u0026#34;SOME_DIR\u0026#34;, user_name: \u0026#34;SOME_USER\u0026#34;, }, }; // initialize the repo client by passing the conifg object const repoClient = new QlikRepositoryClient(config); // invoke Repository API \u0026#34;about\u0026#34; endpoint const result = await repoClient.Get(\u0026#34;about\u0026#34;); And the result variable will be in format:\nAnd the data property will hold the response data:\nFeel free to check the package repository page. Don\u0026rsquo;t forget to check the Wiki section for more details and examples.\nDev documentation of all exposed methods, types and interfaces can be found HERE\nP.S. Please feel free to open issue for any question or bug you encounter :)\nHope you liked it!\nStefan\n","permalink":"https://sstoichev.eu/post/informatiqal/","summary":"\u003cp\u003eThe initial idea was simple \u0026hellip; on paper. Spend few days just to prove that its possible. And it was doable. But to achieve it I had to build solid foundation.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m not going to say what was the initial idea \u0026hellip; yet. But will share where it\u0026rsquo;s \u0026ldquo;home\u0026rdquo; will be and will release all the components of the \u0026ldquo;foundation\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eSince there will be multiple components I\u0026rsquo;ve decided that it\u0026rsquo;s a good idea to separate them in their own space.\u003c/p\u003e","title":"Meet Informatiqal"},{"content":"This post is long overdue but finally found the time to complete it.\nHere we\u0026rsquo;ll have a look how to establish connection (via enigma.js) to Qlik Sense Enterprise on Windows (QSEoW) when hosting an web app outside of Qlik (not as an extension). And more specifically we\u0026rsquo;ll have a look at what is the authentication work flow.\nThe (very simple) web app will just list the apps that are available for the logged in user. I\u0026rsquo;ll be using Svelte as web framework (more specifically SvelteKit) but the general work flow should work in the same way with other frameworks as well.\nLet\u0026rsquo;s begin!\nThe problem When our web app is hosted by Qlik (as an extension) we can only access it through Qlik\u0026rsquo;s extensions url (like: https://my-sense-instance.com/extensions/MyMashup/index.html) It\u0026rsquo;s not a very nice URL. And every time when new version of our web app is ready we\u0026rsquo;ll have to remove the old extension and re-upload the new version. (of course this can be automated)\nThe benefit of having the web app hosted by Qlik is that in order to access it you\u0026rsquo;ll have to be authenticated. If not - Qlik will handle the redirection.\nIn our case we will redirect the user to Qlik\u0026rsquo;s login page, receive web ticket and use the ticket to establish connection with the engine.\nScenario The scenario is very simple:\nusing enigma.js to establish session with Qlik Engine. once the session is esablished get list of all user\u0026rsquo;s apps display the apps list on the page Session events Crucial part of the authentication work flow is enigma\u0026rsquo;s capability to \u0026ldquo;listen\u0026rdquo; for specific events (check out the Session API part of the documentation)\nEnigma can \u0026ldquo;listen\u0026rdquo; for various events (like session closed, session suspended, all the traffic send/received to/from the Engine or any named notification).\nIn our case we\u0026rsquo;ll listen for OnAuthenticationInformation event. This event can tell us if we must authenticate and if we must it will provide us with the URL to the Qlik\u0026rsquo;s login page. Once authenticated there Qlik will re-direct us back to the web app but with the Qlik ticket in the url. The ticket then can be used when establishing connection with enigma\nGeneral workflow When the user opens the web page we\u0026rsquo;ll try and connect to QSEoW via Web Sockets (await session.open()) If the users is not authenticated Qlik will \u0026ldquo;raise\u0026rdquo; OnAuthenticationInformation notification event. The event contains simple JSON object: { mustAuthenticate: true, loginUri: \u0026#34;https://...\u0026#34; } In our code we are listening for OnAuthenticationInformation events and when receive one (and mustAuthenticate: true) we are going to redirecting the user to the loginUri value from the event loginUri is the main Qlik login page (not part of our web app!). The user enters their credentials there If the credentials are ok then Qlik redirect back to our web app page but with qlikTicket added to the url as a parameter Again we send session.open() but this time we\u0026rsquo;ll provide the qlikTicket Qlik replies with SESSION_CREATED And at this point we are all set and can continue communicating with the Engine. All requests will be made in the scope of the logged user The image below illustrate what is the general work flow to establish session and authenticate\nCode The first bit of code is to handle the URL parameter. If the URL contains qlikTicket we\u0026rsquo;ll pass the value as property. When the page is loaded for the first time the value of qlikTicket will be empty.\n\u0026lt;script context=\u0026#34;module\u0026#34;\u0026gt; export const load = async ({ url }) =\u0026gt; { let qlikTicket = url.searchParams.get(\u0026#34;qlikTicket\u0026#34;); return { props: { qlikTicket } }; }; \u0026lt;/script\u0026gt; Importing enigma and goto from Svelte navigation. goto will be use to re-direct to the login url.\n\u0026lt;script\u0026gt; import { goto } from \u0026#34;$app/navigation\u0026#34;; import enigma from \u0026#34;enigma.js\u0026#34;; import schema from \u0026#34;enigma.js/schemas/12.67.2.json\u0026#34;; Define Qlik host and URL for the web app. The web app location can be found programmatically of course.\nconst qsHost = \u0026#34;wss://my-sense-instance.com\u0026#34;; const reloadURI = `http://localhost:3000`; Define few variables:\nqlikTicket - will hold the Qlik ticket (when we have it) qlikApps - will hold the list of the Qlik apps qlikTicketString - the ticket have to be passed as URL parameter itself. This variable will contain this string export let qlikTicket; let qlikApps = []; let qlikTicketString = qlikTicket ? `\u0026amp;QlikTicket=${qlikTicket}` : \u0026#34;\u0026#34;; Generate the session object. The interesting part here is the url. The url is containing three \u0026ldquo;elements\u0026rdquo;:\n${qsHost}/app/engineData - setting the Qlik host as usual reloadURI=${encodeURIComponent(reloadURI) - this part tells Qlik to return to this location once the user is successfully authenticated (in our case this will be http://localhost:3000) ${qlikTicketString} - when we have the ticket it will be passed here const session = enigma.create({ schema, url: `${qsHost}/app/engineData?reloadURI=${encodeURIComponent( reloadURI )}${qlikTicketString}`, createSocket: (url) =\u0026gt; new WebSocket(url), }); The session event part. If notification of type OnAuthenticationInformation is received and the event data contains loginUri then re-direct the page to this url. Once the user authenticate there Qlik will return us to our web page but with the ticket appended at the end. The url will looks like:\nhttp://localhost:3000/?qlikTicket=YMnqm8WvW8AXSA99\nsession.on(\u0026#34;notification:OnAuthenticationInformation\u0026#34;, (data) =\u0026gt; { if (data.loginUri) goto(data.loginUri); }); The main logic. Once the session is successfully established - get the list of the apps and populate qlikApps variable with it.\n(async function () { let global = await session.open(); qlikApps = await global.getDocList(); console.log(qlikApps); await session.close(); })(); \u0026lt;/script\u0026gt; The HTML part is very basic:\n\u0026lt;main\u0026gt; \u0026lt;h1\u0026gt;Qlik apps:\u0026lt;/h1\u0026gt; {#each qlikApps as qlikApp} \u0026lt;div\u0026gt;{qlikApp.qDocName}\u0026lt;/div\u0026gt; {/each} \u0026lt;/main\u0026gt; And the result:\nOnce the local development is complete the bundle can be hosted anywhere\nComplete code The full code is available in the code repository. Follow the readme there to install and run it.\nHope you liked it!\n","permalink":"https://sstoichev.eu/post/qseow-authentication-svelte/","summary":"\u003cp\u003eThis post is long overdue but finally found the time to complete it.\u003c/p\u003e\n\u003cp\u003eHere we\u0026rsquo;ll have a look how to establish connection (via \u003ca href=\"https://github.com/qlik-oss/enigma.js/\"\u003eenigma.js\u003c/a\u003e) to Qlik Sense Enterprise on Windows (\u003ccode\u003eQSEoW\u003c/code\u003e) when hosting an web app outside of Qlik (not as an extension). And more specifically we\u0026rsquo;ll have a look at what is the authentication work flow.\u003c/p\u003e\n\u003cp\u003eThe (very simple) web app will just list the apps that are available for the logged in user. I\u0026rsquo;ll be using \u003ccode\u003eSvelte\u003c/code\u003e as web framework (more specifically \u003ca href=\"https://kit.svelte.dev/\"\u003eSvelteKit\u003c/a\u003e) but the general work flow should work in the same way with other frameworks as well.\u003c/p\u003e","title":"Web app authentication with QSEoW (web ticket)"},{"content":"Administrating Qlik Sense sometimes means spending a lot of the time in QMC.\nAnd sometimes I need to export some of the tables there just to keep the info somewhere else while I\u0026rsquo;m on a different screen in QMC.\nFinally sat down to solve this specific issue - Chrome/Edge extension that can export QMC tables to CSV file or copy the content to the clipboard.\nUI The extension layout is \u0026ldquo;separated\u0026rdquo; into two areas:\nCopy - exports the table and paste it into the clipboard. The only option here is the delimiter - comma (default) or tabs (will make the life easier if we are pasting in Excel Export - exports the table and downloads it as CSV file. The file name can be specified (without the .csv) or it will be auto generated (in uuid format) Browser permissions The extension require the following browser permissions:\nactiveTab - access to the current active tab hosts - access to the host matching the patterns https://*/qmc/* and https://*/*/qmc/*. This should filter only the host with QMC (with or without virtual proxy). If the extension is started on a host which is not matching the patterns (for example https://stackoverflow.com/) the following error message will be shown: If the extension is started on correct host but there are no tables found then the following message will be shown:\nInstallation The extension is available for Chrome, Edge (Chromium) and Firefox and can be downloaded from Google and Microsoft extension stores:\nChrome Web Store Edge Add-ons Store Firefox Add-ons store Hope you liked it! Stefan\n","permalink":"https://sstoichev.eu/post/export-qmc-tables/","summary":"\u003cp\u003eAdministrating Qlik Sense sometimes means spending a lot of the time in QMC.\u003c/p\u003e\n\u003cp\u003eAnd sometimes I need to export some of the tables there just to keep the info somewhere else while I\u0026rsquo;m on a different screen in QMC.\u003c/p\u003e\n\u003cp\u003eFinally sat down to solve this specific issue - \u003ccode\u003eChrome\u003c/code\u003e/\u003ccode\u003eEdge\u003c/code\u003e extension that can export QMC tables to CSV file or copy the content to the clipboard.\u003c/p\u003e\n\u003ch3 id=\"ui\"\u003eUI\u003c/h3\u003e\n\u003cp\u003eThe extension layout is \u0026ldquo;separated\u0026rdquo; into two areas:\u003c/p\u003e","title":"Export QMC tables"},{"content":"Another interesting feature of enigma.js are interceptors. Interceptors are similar to the mixins functions but they are executed on lower level - request/response level.\nRequest interceptors are invoked right before the request is send to the Engine and response interceptors are invoked right after the response from the Engine is received and before the data is send to the calling function.\nYou can think of the interceptors as \u0026ldquo;enigma.js middleware\u0026rdquo;.\nThere are couple of functions available for request and response:\nRequest onFulfiled - invoked just before the request is going to be send to the Engine so you can alter the request is needed Response onFulfiled - invoked when the response from the engine is ok (no errors) onRejected - invoked when the Engine returns an error so you can handle the errors on enigma.js level The code below shows how to construct request interceptor:\nconst enigma = require(\u0026#39;enigma.js\u0026#39;); const schema = require(\u0026#39;enigma.js/schemas/12.20.0.json\u0026#39;); const WebSocket = require(\u0026#39;ws\u0026#39;); const session = enigma.create({ schema, url: \u0026#39;ws://localhost:9076/app/engineData\u0026#39;, createSocket: (url) =\u0026gt; new WebSocket(url), requestInterceptors: [{ onFulfilled: function myHandler(sessionReference, request) { // do somthing with the request data here // OR return the original request return request }, }], }); Example A simple example of response interceptor can be - change the return data format of a getLayout() method which gets the current selections data.\nThis interceptor will \u0026ldquo;listen\u0026rdquo; for getLayout requests and if the response is for a specific object type it will \u0026ldquo;flatten\u0026rdquo; the returned data and return it in the following format:\n[ ... {\u0026#34;field\u0026#34;: \u0026#34;name-of-the-field\u0026#34;, \u0026#34;value\u0026#34;: \u0026#34;selected-value-1\u0026#34;}, {\u0026#34;field\u0026#34;: \u0026#34;name-of-the-field\u0026#34;, \u0026#34;value\u0026#34;: \u0026#34;selected-value-2\u0026#34;}, {\u0026#34;field\u0026#34;: \u0026#34;some-other-field\u0026#34;, \u0026#34;value\u0026#34;: \u0026#34;selected-value-1\u0026#34;} ... ] First lets prepare the session object and define our interceptor there: const session = enigma.create({ schema, url: \u0026#39;ws://localhost:9076/app/engineData\u0026#39;, createSocket: (url) =\u0026gt; new WebSocket(url), responseInterceptors: [{ onFulfilled: function toggleDelta(session, request, response) { // check if the request method is getLayout and // the response is for object with type current-selections-modified if ( request.method === \u0026#39;GetLayout\u0026#39; \u0026amp;\u0026amp; response.qInfo.qType == \u0026#39;current-selections-modified\u0026#39; ) { // if the above is valid then \u0026#34;flatten\u0026#34; the response return response.qSelectionObject.qSelections.map((s) =\u0026gt; { return s.qSelectedFieldSelectionInfo.map((f) =\u0026gt; { return { field: s.qField, value: f.qName } }) }).flat() } // for every other response return the original response return response; }, }], }); Let\u0026rsquo;s select some values first: let field = await qDoc.getField(\u0026#39;Product Sub Group Desc\u0026#39;) let selectValues = await field.selectValues([ { qText: \u0026#39;Ice Cream\u0026#39; }, { qText: \u0026#39;Juice\u0026#39; }, { qText: \u0026#39;Chips\u0026#39; } ]) Once we have the interceptor code ready and some selections are made then we can prepare the properties of the object that will hold the selections: let selectionObjectProps = { \u0026#34;qInfo\u0026#34;: { \u0026#34;qId\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;qType\u0026#34;: \u0026#34;current-selections-modified\u0026#34; }, \u0026#34;qSelectionObjectDef\u0026#34;: {} } Check out the type of our object - current-selections-modified. Our interceptor will change the data output only if the response is for this object type. This way we will not \u0026ldquo;disrupt\u0026rdquo; the data for the other object types.\nonce we have the properties set, we continue our enigma calls as usual: create session object and get its layout: let sessionObj = await qDoc.createSessionObject(selectionObjectProps); let sessionObjLayout = await sessionObj.getLayout(); // out interceptor will be invoked here console.log(sessionObjLayout) The result of the last row will print the following \u0026ldquo;flatten\u0026rdquo; array:\n[ { field: \u0026#39;Product Sub Group Desc\u0026#39;, value: \u0026#39;Juice\u0026#39; }, { field: \u0026#39;Product Sub Group Desc\u0026#39;, value: \u0026#39;Ice Cream\u0026#39; }, { field: \u0026#39;Product Sub Group Desc\u0026#39;, value: \u0026#39;Chips\u0026#39; } ] It\u0026rsquo;s probably not the most practical example but it will give you an idea what enigma.js interceptors are. There are couple of more examples in enigma.js repository itself\nConclusion The same interceptor can be written as mixin as well and instead intercepting the response just call the mixin which will return the same result. But if you want low level / fine grain control over the request/response traffic then interceptors are the way.\nCode Check out the repository for the full example code\nHope you liked it!\nStefan\n","permalink":"https://sstoichev.eu/post/enigma-js-interceptors/","summary":"\u003cp\u003eAnother interesting feature of enigma.js are interceptors. Interceptors are similar to the mixins functions but they are executed on lower level - request/response level.\u003c/p\u003e\n\u003cp\u003eRequest interceptors are invoked right before the request is send to the Engine and response interceptors are invoked right after the response from the Engine is received and before the data is send to the calling function.\u003c/p\u003e\n\u003cp\u003eYou can think of the interceptors as \u0026ldquo;enigma.js middleware\u0026rdquo;.\u003c/p\u003e","title":"enigma.js - interceptors"},{"content":"In the previous post we saw what are enigma.js mixins. I\u0026rsquo;ve started an repo some time ago (and npm package) where I\u0026rsquo;ve been adding mixins that I\u0026rsquo;ve found useful (or mixins that I\u0026rsquo;ve found boring to write all the time). And want to introduce you to this list.\nAt the moment the repo contains only mixins that are relevant to application/document.\n(to distinct the mixins methods from the default enigma.js ones all mixin methods, from this package, are having prefix m)\nThe mixins are \u0026ldquo;split\u0026rdquo; into groups:\nSelections mSelectionsAll - returns the \u0026ldquo;native\u0026rdquo; reponse from Qlik ragarding the current selections\nmSelectionsFields - an array of only the field names, having selections in it\nmSelectionsSimple - without any parameters returns a flat array with all selections\n[ {field: \u0026#34;Field 1\u0026#34;: value: \u0026#39;Value 1\u0026#39;}, {field: \u0026#34;Field 1\u0026#34;: value: \u0026#39;Value 2\u0026#39;}, {field: \u0026#34;Field 2\u0026#34;: value: \u0026#39;Value 10\u0026#39;}, ... ] if groupByField parameter (optional and default is false) is passed then the result will be grouped by a field name:\n[ {field: \u0026#34;Field 1\u0026#34;: values: [...]}, {field: \u0026#34;Field 2\u0026#34;: values: [...]}, ... ] mSelectInField - provide an field name and array of values to be selected\nTables and fields mGetTablesAndFields - returns an array with {table: \u0026quot;data-table-name\u0026quot;, field: \u0026quot;field-name\u0026quot;} values mGetTables - an array with all data tables mGetFields - an array with all fields mCreateSessionListbox - creates a listbox session object for the provided field Variables mVariableGetAll - array with all variables mVariableUpdateById - updates the definition of an existing variable by providing the variable ID mVariableUpdateByName - updates the definition of an existing variable by providing the variable name mVariableCreate - creates a new variable from the provided name, comment (optional) and definition Extensions mExtensionObjectsAll - returns an array with all extension objects in the current app. Returned fields: appId, appName, objId, objType, extName, extVersion, extVisible, extIsBundle, extIsLibrary, qProps Unbuild mUnbuild - extracts all parts of an Qlik Sense app (apart from the data itself) into JSON object Build mBuild - the opposite of un-building - provide a json object with the app components and this mixin will update (or create) all the objects in the target app If you want to use the build and unbuild commands outside JavaScript then I highly recommend using Corectl\nInstallation The installation is very simple:\nnpm install --save enigma-mixins Usage (make sure that enigma.js is already installed)\nimport enigma from \u0026#34;enigma.js\u0026#34;; import schema from \u0026#34;enigma.js/schemas/12.20.0.json\u0026#34;; import enigmaMixins from \u0026#34;enigma-mixins\u0026#34;; // Create enigma session as usual but include the mixin property const session = enigma.create({ schema, mixins: enigmaMixins, // this is where the mixinx are added url: \u0026#34;ws://localhost:4848/app/engineData\u0026#34;, createSocket: (url) =\u0026gt; new WebSocket(url), }); Once the session is established and the document is open, then you\u0026rsquo;ll have all the mixins available:\nContribution Feel free to open an PR with your mixins :)\nRepository and npm Code repository NPM Hope you liked it!\nStefan\n","permalink":"https://sstoichev.eu/post/introducing-enigma-mixin/","summary":"\u003cp\u003eIn the \u003ca href=\"__GHOST_URL__/2020/06/23/enigma-js-mixins/\"\u003eprevious post\u003c/a\u003e we saw what are \u003ca href=\"https://github.com/qlik-oss/enigma.js\"\u003eenigma.js\u003c/a\u003e  \u003ca href=\"https://github.com/qlik-oss/enigma.js/blob/master/docs/api.md#mixins\"\u003emixins\u003c/a\u003e. I\u0026rsquo;ve started an repo some time ago (and \u003cem\u003enpm\u003c/em\u003e package) where I\u0026rsquo;ve been adding mixins that I\u0026rsquo;ve found useful (or mixins that I\u0026rsquo;ve found boring to write all the time). And want to introduce you to this list.\u003c/p\u003e\n\u003cp\u003eAt the moment the repo contains only mixins that are relevant to application/document.\u003c/p\u003e\n\u003cp\u003e(to distinct the mixins methods from the default enigma.js ones all mixin methods, from this package, are having prefix \u003ccode\u003em\u003c/code\u003e)\u003c/p\u003e","title":"Custom enigma.js mixins"},{"content":"You know it already but enigma.js is the JavaScript wrapper around the Qlik\u0026rsquo;s Engine API.\nIf you are building a mashup/web app or some sort of automation that require Engine communication you\u0026rsquo;ll have to use Engine API and here enigma.js is your friend.\nJust to mention that JavaScript is not the only \u0026ldquo;flavor\u0026rdquo; of Enigma. You can find enigma for Go and .NET\nBut in this post I want to introduce you to one very cool (but not very popular) functionality that Enigma provides - mixins.\nMixins allow us to extend, with additional methods, or override existing methods. Mixins are bound to at least one type (type being - Doc, GenericObject, GenericBookmark etc.). The official documentation can be found in the official enigma.js repo.\nLet\u0026rsquo;s write some code (basics) Essentially each mixin is a JS object with few properties - init, types and extends and/or overrides.\nA very simple mixin will be:\nconst docMixin = { types: [\u0026#39;Doc\u0026#39;], init(args) { console.log(`My mixin is being initialized`); }, extend: { myMixin() { console.log(\u0026#39;My mixin was called!\u0026#39;); }, } } From the snipped above, we can see:\nthis snipped is available for docs (apps) when the mixin is initialised a message will be printed in the console when its called the mixin will only print a message To use the mixin we have to add this object (or objects) to the Enigma session:\nconst session = enigma.create({ schema, mixins: [docMixin], url: \u0026#39;ws://localhost:9076/app/engineData\u0026#39;, createSocket: (url) =\u0026gt; new WebSocket(url), }); Once we have the session established then we can open an app and use our mixin:\nlet global = await session.open() let doc = await global.openDoc(\u0026#39;/data/Consumer Sales.qvf\u0026#39;) doc.myMixin() In our case the mixin is pretty general (just printing a message) and we might want to use it for other types and not just in the apps context. To add it to more types just edit the type array:\n... types: [\u0026#39;Doc\u0026#39;, \u0026#39;GenericObject\u0026#39;, \u0026#39;GenericBookmark\u0026#39;] ... A bit more advanced example Getting the layout of an object is a two step process:\nlet qObject = await doc.getObject(\u0026#39;some-object-id-here\u0026#39;); let qLayout = await qObject.getLayout(); What about if we write an mixin that is doing these two steps for us so we can have a single command to get the layout? An example mixin can look like this:\nconst docMixin = { types: [\u0026#39;Doc\u0026#39;], init(args) { }, extend: { getLayoutMixin(objectId) { let qObject = await doc.getObject(objectId); let qLayout = await qObject.getLayout(); return [ qObject, qLayout ] } } } const session = enigma.create({ schema, mixins: [docMixin], url: \u0026#39;ws://localhost:9076/app/engineData\u0026#39;, createSocket: (url) =\u0026gt; new WebSocket(url), }); let global = await session.open() let doc = await global.openDoc(\u0026#39;/data/Consumer Sales.qvf\u0026#39;) let [myObject, myObjectLayout] = await doc.getLayoutMixin(\u0026#39;some-object-id\u0026#39;); Conclusion Personally I found the mixins to be very cool and handy feature. But also I found them very underused (including myself here as well).\nI\u0026rsquo;ve been setting my own collection of useful mixins in my free time and the next post will be about this collection but you can have a look in the repo if you want.\nHope you liked it!\nStefan\n","permalink":"https://sstoichev.eu/post/enigma-js-mixins/","summary":"\u003cp\u003eYou know it already but \u003ca href=\"https://github.com/qlik-oss/enigma.js\"\u003eenigma.js\u003c/a\u003e is the JavaScript wrapper around the Qlik\u0026rsquo;s Engine API.\u003c/p\u003e\n\u003cp\u003eIf you are building a mashup/web app or some sort of automation that require Engine communication you\u0026rsquo;ll have to use Engine API and here enigma.js is your friend.\u003c/p\u003e\n\u003cp\u003eJust to mention that JavaScript is not the only \u0026ldquo;flavor\u0026rdquo; of Enigma. You can find enigma for \u003ca href=\"https://github.com/qlik-oss/enigma-go\"\u003e\u003cem\u003eGo\u003c/em\u003e\u003c/a\u003e and \u003ca href=\"https://github.com/q2g/enigma.net\"\u003e\u003cem\u003e.NET\u003c/em\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c!-- \u003cfigure class=\"kg-card kg-bookmark-card\"\u003e\n  \u003ca href=\"https://github.com/qlik-oss/enigma-go\" class=\"kg-bookmark-container\"\u003e\n    \u003cdiv class=\"kg-bookmark-content\"\u003e\n      \u003cdiv class=\"kg-bookmark-title\"\u003eqlik-oss/enigma-go\u003c/div\u003e\n      \u003cdiv class=\"kg-bookmark-description\"\u003eGo library for consuming Qlik’s Associative Engine. - qlik-oss/enigma-go\u003c/div\u003e\n      \u003cdiv class=\"kg-bookmark-metadata\"\u003e\n        \u003cimg src=\"https://github.githubassets.com/favicons/favicon.svg\" class=\"kg-bookmark-icon\"\u003e\n        \u003cspan class=\"kg-bookmark-author\"\u003eqlik-oss\u003c/span\u003e\n        \u003cspan class=\"kg-bookmark-publisher\"\u003eGitHub\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \n    \u003cdiv class=\"kg-bookmark-thumbnail\"\u003e\n      \u003cimg src=\"https://avatars3.githubusercontent.com/u/23365920?s=400\u0026amp;v=4\"\u003e\n    \u003c/div\u003e\n    \n  \u003c/a\u003e\n  \n\u003c/figure\u003e --\u003e\n\u003c!-- \u003cfigure class=\"kg-card kg-bookmark-card\"\u003e\n  \u003ca href=\"https://github.com/q2g/enigma.net\" class=\"kg-bookmark-container\"\u003e\n    \u003cdiv class=\"kg-bookmark-content\"\u003e\n      \u003cdiv class=\"kg-bookmark-title\"\u003eq2g/enigma.net\u003c/div\u003e\n      \u003cdiv class=\"kg-bookmark-description\"\u003e.NET library for consuming Qlik’s Associative Engine. - q2g/enigma.net\u003c/div\u003e\n      \u003cdiv class=\"kg-bookmark-metadata\"\u003e\n        \u003cimg src=\"https://github.githubassets.com/favicons/favicon.svg\" class=\"kg-bookmark-icon\"\u003e\n        \u003cspan class=\"kg-bookmark-author\"\u003eq2g\u003c/span\u003e\n        \u003cspan class=\"kg-bookmark-publisher\"\u003eGitHub\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \n    \u003cdiv class=\"kg-bookmark-thumbnail\"\u003e\n      \u003cimg src=\"https://avatars2.githubusercontent.com/u/29521584?s=400\u0026amp;v=4\"\u003e\n    \u003c/div\u003e\n    \n  \u003c/a\u003e\n  \n\u003c/figure\u003e --\u003e\n\u003cp\u003eBut in this post I want to introduce you to one very cool (but not very popular) functionality that Enigma provides - \u003cstrong\u003emixins.\u003c/strong\u003e\u003c/p\u003e","title":"enigma.js - mixins"},{"content":"qlbuilder In general qlbuilder is a tool that communicates with Qlik apps (using the Engine API) and set the script. The script is located on the local machine and can be \u0026ldquo;stitched\u0026rdquo; from multiple files.\nWhy? Everyone have their own preferred IDE/text editor and we can write own Qlik Sense load scripts there and use include/must_include inside the app to \u0026ldquo;bring\u0026rdquo; the script inside the app.\nBut it will be nice if we can write the scrips locally and upload them directly to the app as it is.\nThe basic idea is that we will write each Qlik Sense script tab in a separate .qvs file and qlbuilder will combine them (when needed) in a single file and set it\u0026rsquo;s content to the dedicated Qlik Sense app (on dedicated QS environment).\nqlbuilder can perform few more actions, apart from combining and setting the script: check for syntax errors, reload app, \u0026ldquo;watching\u0026rdquo; for script changes and check for syntax errors etc.\nWith this approach I can write my script in Visual Studio Code (in combination with Qlik for Visual Studio Code extension) and use all VS Code shortcuts, themes etc.\nInstallation qlbuilder is a Node JS module and can be installed as a global package\nnpm install -g qlbuilder Folder structure The folder structure used from qlbuilder looks like this:\nproject-name │ config.yml │───dist │ LoadScript.qvs └───src │ 0--Main.qvs │ 1--Config.qvs │ 2--Mapping.qvs │ ... src is the main folder. In this folder are located all qvs files. Each file will result in a separate script tab, when uploaded. The .qvs files are combined in alphabetical order (for now. please have a look at the Road map section at the end of this post)\nConfig files There are two configuration files that are used from qlbuilder:\nconfig.yml\nThis file is mandatory and it\u0026rsquo;s created during the project/folder creation. This file is located in the root folder and defines the Qlik Sense environments and apps ID that are relevant to this script.\nThe file is in yaml format and looks like this:\n- name: dev host: 192.168.0.100 # IP/FQDN of QS engine (central node) secure: false # default value is true appId: 12345678-1234-1234-1234-12345678901 authentication: type: winform The above config defines:\nQS environment called dev the environment\u0026rsquo;s host is 192.168.0.100 the environment is access through http the appId (that this script belongs to) is 12345678-1234-1234-1234-12345678901 the authentication for this environment is Windows/Forms - winform (for more information on authentication types and other possible settings please have a look at qlbuilder GitHub repo)\nThe config contains the \u0026ldquo;public\u0026rdquo; environment information and can be commit to git repo.\n.qlbuilder.yml\nThe non-public info (authentication information) is held in another configuration file .qlbuilder.yml (there is option to use environment variables as well)\nThis file is not created through the install/create process and should be located in current user home folder. For example: C:\\Users\\My-User-Name\\.qlbuilder.yml. Using the above example, with winform authentication and environment named dev, the config will looks like:\ndev: QLIK_USER: DOMAIN\\username QLIK_PASSWORD: my-secret-password This information will be used by qlbuilder during the authentication process to generate the sessionId\nCommands qlbuilder is executed from command line and accept few parameters:\ncreate [project name] - create the initial project folder structure, sample script and config file. If folder with the same name already exists, and the developer agree to re-create, then this command will re-create only the qlbuilder specific files and folders and will not modify any other folders or files\nbuild - combines all qvs files from src folder into a single load script. The result script is created in dist folder (ideally this file should not be edited)\ncheckscript [env name] - check the script for syntax errors. The check is made against temp (session) Qlik app\ngetscript [env name] - gets the script from the app id (specified in config.yml) and splits it into multiple qvs files (in src folder). This operation deletes all files in src folder as first step!\nreload [env name] - reloads the QS app and output the reload progress. (the reload is performed on the QS Engine itself)\nsetscript [env name] -\nbuilds the script from the files in src folder checks the script for syntax errors (if there are errors the process is stopped) opens the QS app and replaces the script with the build one saves the app watch [env name] - now this is a funny one :) \u0026hellip; when started in watch mode qlbuilder will check for syntax error on each file save (any qvs file in src folder). During watch mode few sub-commands that can be used:\ns or set - sets the script to the app and saves it r or rl - reloads the app (if successful the app is saved) c or cls - clears the console x - exit qlbuilder ? - outputs these commands again Version Control Having the script locally allows us to put the project folder under version control and keep history of the script changes (anyone thinking about git hooks as well? 😉)\nRoad map More or less qlbuilder includes all the features I had in mind initially. But there are few things that can be added:\ninclude / must_include - (optional config) detect if these statements are present in the script. If found - get the content of the included scripts and replace the statement with the actual script. This can \u0026ldquo;save\u0026rdquo; apps from failing because of external script being edited after the app is published different way to organize script files? - at the moment the script files are combined based on alphabetical order. It might be a bit annoying when adding new file in the middle of the script - all files that follow need to be renamed For more information please have a read at the readme at the qlbuilder GitHub repo\nFell free to open issues there if you experiencing any problems or have requests/suggestions\nAny suggestions are very welcome!\nHope you liked it! Stefan\n","permalink":"https://sstoichev.eu/post/introducing-qlbuilder/","summary":"\u003ch3 id=\"qlbuilder\"\u003eqlbuilder\u003c/h3\u003e\n\u003cp\u003eIn general \u003ccode\u003eqlbuilder\u003c/code\u003e is a tool that communicates with Qlik apps (using the Engine API) and set the script. The script is located on the local machine and can be \u0026ldquo;stitched\u0026rdquo; from multiple files.\u003c/p\u003e\n\u003ch3 id=\"why\"\u003eWhy?\u003c/h3\u003e\n\u003cp\u003eEveryone have their own preferred IDE/text editor and we can write own \u003ccode\u003eQlik Sense\u003c/code\u003e load scripts there and use \u003ccode\u003einclude\u003c/code\u003e/\u003ccode\u003emust_include\u003c/code\u003e inside the app to \u0026ldquo;bring\u0026rdquo; the script inside the app.\u003c/p\u003e\n\u003cp\u003eBut it will be nice if we can write the scrips locally and upload them directly to the app as it is.\u003c/p\u003e","title":"Introducing qlbuilder"},{"content":"Creating Qlik extensions/mashups is usually quite fun. But it will be better if you have a way to develop locally and deploy to Qlik.\nUsually I\u0026rsquo;m using qExt or sense-go but in this specific case I didn\u0026rsquo;t had the chance to use either (enterprise restrictions)\nLuckily for me Qlik-CLI was available. And Qlik-CLI is doing a great job dealing with Qlik Repository API.\nI\u0026rsquo;ve decided to write a small PowerShell script that can be used to import the extension from my local PC to Qlik.\nThe script will perform the following actions when started:\ngenerate wbfolder.wbl file - this is not mandatory but without this file the extension files will not be available for edit in dev-hub compress the src folder and place the archive into the build folder connect to Qlik check if extension with the same name already exists and if there is one - delete it (assume this is an older version) import the archive from build folder After the script is done the new extension/mashup code will be available.\nFolder structure The script assume the following folder structure (need to exists before run)\nroot │ └──+ build │ └─── extension-name.zip (auto-generated and uploaded) │ └──+ src │ │───+ static (optional) │ │ │─── image.png │ │ └─── ... │ │ │ │─── extension-name.css │ │─── extension-name.js │ │─── extension-name.qext │ │─── wbfolder.wbl │ └─── ... │ └─── upload.ps1 Usage copy the code from below and create upload.ps1 file in your root folder (the file name can be anything). edit the initial variables ($extensionName and $qEnv) to match your extension name and QS DNS every time when need to upload the extension/mashup just start the upload.ps1 Summary Probably not the most elegant way to achieve this but might help if you need quick and \u0026ldquo;dirty\u0026rdquo; way to make (or edit exiting one) extension/mashup and use your text editor/IDE of choice.\nAnd since this approach is using PowerShell you can extend it to your needs ;)\nPowerShell Script $extensionName = \u0026#34;mashup-extension-name\u0026#34; $qEnv = \u0026#34;computerName\u0026#34; function generateWBL { # remove the existing wbfolder.wbl (if exists) Remove-Item .\\src\\wbfolder.wbl -ErrorAction Ignore # list all files under the src folder and add them to the wbl file $existingFiles = Get-ChildItem -Path .\\src\\ -File | foreach {$_.name} | Out-File -FilePath .\\src\\wbfolder.wbl } function compressFolder{ # archive the content of the src folder Compress-Archive -Path .\\src\\* -DestinationPath .\\build\\$extensionName.zip -Force Write-Host ZIP file created } function connectQlik { ## connect to Qlik using Qlik-CLI Connect-Qlik -computerName \u0026#34;$qEnv\u0026#34; -TrustAllCerts Write-Host Connected to Qlik } function removeOldExtVersion { # Query Qlik for extension with the same name $ext = Get-QlikExtension -Filter \u0026#34;name eq \u0026#39;$extensionName\u0026#39;\u0026#34; | Measure-Object # if there is such extension - delete it if ( $ext.Count -gt 0) { Remove-QlikExtension -ename \u0026#34;$extensionName\u0026#34; Write-Host Older version found and removed } } function importExtension { # Import the archive for the build folder Import-QlikExtension -ExtensionPath .\\build\\$extensionName.zip Write-Host New version was imported } function main { generateWBL compressFolder connectQlik removeOldExtVersion importExtension Write-Host DONE } main ","permalink":"https://sstoichev.eu/post/deploy-extension-mashup-with-qlik-cli/","summary":"\u003cp\u003eCreating Qlik extensions/mashups is usually quite fun. But it will be better if you have a way to develop locally and deploy to Qlik.\u003c/p\u003e\n\u003cp\u003eUsually I\u0026rsquo;m using \u003ca href=\"link-here\"\u003eqExt\u003c/a\u003e or \u003ca href=\"link-here\"\u003esense-go\u003c/a\u003e but in this specific case I didn\u0026rsquo;t had the chance to use either (enterprise restrictions)\u003c/p\u003e\n\u003cp\u003eLuckily for me \u003ca href=\"https://github.com/ahaydon/Qlik-Cli\"\u003eQlik-CLI\u003c/a\u003e was available. And \u003ccode\u003eQlik-CLI\u003c/code\u003e is doing a great job dealing with Qlik Repository API.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve decided to write a small \u003ccode\u003ePowerShell\u003c/code\u003e script that can be used to import the extension from my local PC to Qlik.\u003c/p\u003e","title":"Deploy Qlik extension/mashup with Qlik-CLI"},{"content":"With multi-stage Qlik environment (DEV, TEST, PROD etc) the load scripts are usually including some kind of a check - \u0026ldquo;In which environment am I?\u0026rdquo;.\nBased on the result different/additional actions might be performed. For example:\nif the environment is not PROD then mask/scramble the client names use different data connection names (providers) on each environment \u0026hellip; It will be nice if there is something more centralised that can provide this information (and not only) to any app on the cluster. Something like a global meta-data \u0026ldquo;storage\u0026rdquo; that can be changed/updated (by the administrators)\nIn general my approach is to use a dedicated Custom Property and each value to be in format key:pair. Values can be accessed for reading using a (dedicated) REST data connection in the beginning of each script. If we keep the keys the same then we can write one script and use it on every environment.\n###Custom Property\nWe will create new custom property (in my case I\u0026rsquo;ve named it ENVIRONMENT) and all the global values will be added to this CP.\nIn QMC:\ncreate new custom property add as many CP values as you want as key:value pairs (I\u0026rsquo;ve picket : as a separator but this can be anything). For example: name:DEV central repository ip:192.168.1.10 central repository host:qlik.central.repo Created ENVIRONMENT custom property\n###Data Connection\nWe will use the Repository Service REST API to get the ENVIRONMENT custom property values. We need to create a new data connection that will be used to extract the data.\nDuring the installation QS creates few monitor_apps_REST_* data connection which are used by the documents in the Monitoring apps stream (like License Monitor or Operations Monitor). We can use one of these connectors, as a base and create new one that will return the values only for our custom property.\nopen any application and create new Data Connection (at this point there is no need the connection to be a specific type. We are creating just a placeholder) In QMC -\u0026gt; Data Connection -\u0026gt; monitor_apps_REST_task -\u0026gt; copy the Connection string go back and edit the placeholder connection (in my case Environment) replace the connection string with the one from monitor_apps_REST_task and apply the change go back to the application and edit the Environment connection in the URL replace /task/ with /custompropertydefinition/ in the Query parameters section add new one: Name: filter Value: name eq 'ENVIRONMENT' Save At this point you can preview the data from the connection and if everything is ok you should see something like this:\n###Usage\nOnce all is set we can create a small QS script that gets the raw values and \u0026ldquo;convert\u0026rdquo; them to a more readable QS format in form of a table with two columns: ENV_NAME and ENV_VALUE\nLIB CONNECT TO \u0026#39;Environment\u0026#39;; RestConnectorMasterTable: SQL SELECT \u0026#34;__KEY_root\u0026#34;, (SELECT \u0026#34;@Value\u0026#34;, \u0026#34;__FK_choiceValues\u0026#34; FROM \u0026#34;choiceValues\u0026#34; FK \u0026#34;__FK_choiceValues\u0026#34; ArrayValueAlias \u0026#34;@Value\u0026#34;) FROM JSON (wrap on) \u0026#34;root\u0026#34; PK \u0026#34;__KEY_root\u0026#34;; ENVIRONMENT: Load subfield([@Value], \u0026#39;:\u0026#39;, 1) as ENV_NAME, subfield([@Value], \u0026#39;:\u0026#39;, 2) as ENV_VALUE ; Load [@Value], [__FK_choiceValues] as [__KEY_root] RESIDENT RestConnectorMasterTable WHERE NOT IsNull([__FK_choiceValues]); DROP TABLE RestConnectorMasterTable; This script can be placed in the beginning of each app OR even better - host the script somewhere and each app (on each environment) can Must_Include it.\nUpon reload only one table is returned:\n###Conclusion\nHaving a centralised place for a cluster-wide variables is a nice to have. I\u0026rsquo;ve seen multiple approaches on how to deal with this: specific document prefixes for each env, flat files in each data folder etc. But the problem there is that there is always enough space for someone to make a mistake (overwrite the flat file when moving data is my favorite)\nAnother plus for custom property and data connection is that these objects are subject of security rules. Which means that we can grant everyone read access over them but very limited audience can edit them which (might 🤞) lead to less errors and maintenance.\n###Bonus\nIf you have/use Qlik-CLI then the PowerShell script below will do the \u0026ldquo;dirty\u0026rdquo; setup for you. The script will:\ncreate new custom property with some pre-defined values create new REST data connection using the monitor_apps_REST_task as a template edit the new data connection change the URL add the additional query parameter # QS Central Repo Address $computerName = \u0026#34;qlik.central.repo\u0026#34; # or localhost if started on the server itself # Properties for the new data connection # ideally the user/pass should be the ones # under which the QS service is running $dataConnProps = @{ name = \u0026#34;Environment\u0026#34; userName = \u0026#34;domain\\username\u0026#34; password = \u0026#34;password\u0026#34; } # Properties for the new custom property # Can populate some values when creating it # \u0026#34;Objects = App\u0026#34; is just to create the CP (cant create it without it from Qlik-CLI) $customPropProps = @{ name = \u0026#34;ENVIRONMENT\u0026#34; values = @(\u0026#34;name:DEV\u0026#34;, \u0026#34;central repository ip:192.168.1.10\u0026#34;) objects = @(\u0026#34;App\u0026#34;) } function createNewDataConnection { # Get the existing monitor_apps_REST_task connection details $connector = Get-QlikDataConnection -filter \u0026#34;name eq \u0026#39;monitor_apps_REST_task\u0026#39;\u0026#34; # Replace the endoint to point to return custom prop details instead of a task # Add one more query parameter which will return data only for the Environment custom prop $newConnectionString = $connector.connectionString. Replace(\u0026#34;/qrs/task/full\u0026#34;, \u0026#34;/qrs/custompropertydefinition/full\u0026#34;). Replace(\u0026#34;queryParameters=xrfkey%20000000000000000\u0026#34;, \u0026#34;queryParameters=xrfkey%20000000000000000%1filter%2name eq \u0026#39;$($customPropProps.name)\u0026#39;\u0026#34;) # Create the connection New-QlikDataConnection -name $dataConnProps.name -connectionstring $newConnectionString -type \u0026#34;QvRestConnector.exe\u0026#34; -username $dataConnProps.userName } function createNewCustomProperty { # Create the Environment custom property New-QlikCustomProperty -name $($customPropProps.name) -choiceValues $($customPropProps.values) -objectTypes $($customPropProps.objects) } function connectToQlik { Connect-Qlik -computerName $computerName -TrustAllCerts } function main { connectToQlik createNewCustomProperty createNewDataConnection } main Stefan\n","permalink":"https://sstoichev.eu/post/qlik-sense-environment-values-approach/","summary":"\u003cp\u003eWith multi-stage Qlik environment (DEV, TEST, PROD etc) the load scripts are usually including some kind of a check - \u0026ldquo;\u003cstrong\u003eIn which environment am I?\u003c/strong\u003e\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eBased on the result different/additional actions might be performed. For example:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eif the environment is not PROD then mask/scramble the client names\u003c/li\u003e\n\u003cli\u003euse different data connection names (providers) on each environment\u003c/li\u003e\n\u003cli\u003e\u0026hellip;\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIt will be nice if there is something more centralised that can provide this information (and not only) to any app on the cluster. Something like a global meta-data \u0026ldquo;storage\u0026rdquo; that can be changed/updated (by the administrators)\u003c/p\u003e","title":"Global Environment Values Approach (Qlik Sense)"},{"content":"Recently I\u0026rsquo;ve started working on one of the idea from my wild ides list. For info click here (but more on it later when there is something working to be shown). For this we need to write our QV scrips in external text editor. My personal choose is VS Code.\nQuick search in the VS Code extension marketplace shows that there is Qlik for Visual Studio Code extension for highlighting QV code developed by Xavier Hahn. The extension is working very nice and if we open a file with .qvs extension then the text will be highlighted with the QV syntax.\nI\u0026rsquo;ve spend few days copy/pasting and web scraping the Qlik Help website but managed to add and snippets integration in the extension. So now when .qvs file is open there will be code highlight and IntelliSense (auto-complete but not only) integration as well.\nHighlight (screen from VS Code marketiplace) IntelliSense integration - filter the functions as you type Descriptions - press the i button to view the function description (not for all functions. Will slowly add as much as I can) Quick code - pick function from the drop-down and VS Code will point you to the first parameter(YearId in this example). When finished with it just press Tab key to move to the next one (WeekId) Install To install the extension:\nopen VS Code and press Ctrl + P type/paste ext install qlik click on the extension on the left side click on Install button after the installation is complete VS Code will want to be restarted after the restart every .qvs file will benefit from the extension Using .qvs files External script files can be used in QlikView and Qlik Sense using Inclide or Must_Include commands. Both commands will try to execute the script in the file but Must_Incliude will result in script error if the external file is not found. Include will ignore the error and the script will continue.\nUsage examples:\n$(Include=..\\scripts\\MappingTablesLoad.qvs); $(Must_Include=c:\\Projects\\TempProject\\scripts\\FactLoad.qvs); Hope you liked it! Stefan\n","permalink":"https://sstoichev.eu/post/qlik-for-visual-studio-code/","summary":"\u003cp\u003eRecently I\u0026rsquo;ve started working on one of the idea from my wild ides list. For info \u003ca href=\"https://github.com/countnazgul/qlikview-projects-documentation\"\u003eclick here\u003c/a\u003e (but more on it later when there is something working to be shown). For this we need to write our QV scrips in external text editor. My personal choose is \u003ca href=\"https://code.visualstudio.com/\"\u003eVS Code\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eQuick search in the VS Code extension marketplace shows that there is \u003ca href=\"https://marketplace.visualstudio.com/items?itemName=Gimly81.qlik\"\u003eQlik for Visual Studio Code\u003c/a\u003e extension for highlighting QV code developed by \u003ca href=\"https://github.com/Gimly\"\u003eXavier Hahn\u003c/a\u003e. The extension is working very nice and if we open a file with \u003ccode\u003e.qvs\u003c/code\u003e extension then the text will be highlighted with the QV syntax.\u003c/p\u003e","title":"Qlik for Visual Studio Code"},{"content":"I like how flexible Qlik Sense is. With its massive API if we want we can re-build the GUI (and not only) from scratch and just use the background services (like the Engine or Repository).\nI have some (small) issues with how the current Hub looks and behaves and decided to build something simple that fits my needs.\nAt the end I\u0026rsquo;ve wanted something simple that will provide me with quick search capabilities, few shortcuts and the options to create, delete and duplicate files.\nAnd below is the final result\nThe code is available in GitHub\nJust a search box, list with all the apps (to which I have access) and create new app button.\nSearching For the search easyautocomplete plugin is used.\nThe search works as any other search box. The search is performed on the QS apps titles Create new app Pressing the top right green button will show the create new app modal\nHere the name of the new app is specified and which will be the initial screen after the app is created. As developer (and not only) \u0026ldquo;Data editor\u0026rdquo; or \u0026ldquo;Data manager\u0026rdquo; are the main ones.\nOptions Agains each app few shortcuts are present\nto which stream the app belongs open the \u0026ldquo;App Overview\u0026rdquo; open the \u0026ldquo;Data Manager\u0026rdquo; open the \u0026ldquo;Data Editor\u0026rdquo; open the \u0026ldquo;Data Model Viewer\u0026rdquo; Duplicate app (not available in Desktop mode) Delete app Hope you liked it! Stefan\n","permalink":"https://sstoichev.eu/post/qlik-sense-custom-hub/","summary":"\u003cp\u003eI like how flexible Qlik Sense is. With its massive API if we want we can re-build the GUI (and not only) from scratch and just use the background services (like the Engine or Repository).\u003c/p\u003e\n\u003cp\u003eI have some (small) issues with how the current Hub looks and behaves and decided to build something simple that fits my needs.\u003c/p\u003e\n\u003cp\u003eAt the end I\u0026rsquo;ve wanted something simple that will provide me with quick search capabilities, few shortcuts and the options to create, delete and duplicate files.\u003c/p\u003e","title":"Qlik Sense Custom Hub"},{"content":"Recently I\u0026rsquo;ve found a very neat Adnroid app which I can run on my Chromebook - Termux- \u0026ldquo;Termux combines powerful terminal emulation with an extensive Linux package collection.\u0026rdquo;\nIn essence after installing it you will have a nice Linux distro on your Android device without root.\nIn my case I\u0026rsquo;ve decided to try if I can use Termux for Node development.\nNode development The initial plan was to install node inside Termux and to use it to install npm packages and run code.\nAfter installing the app there is a need to run termux-setup-storage command from within Termux to get access to the Chromebook storage. After this I\u0026rsquo;ve tried to install some packages directly in the Downloads folder but unfortunately because Termux do not provide root access I was unable to install them.\nThe plan was changed then. I\u0026rsquo;ve decided to install npm packages inside Termux, write my code inside some editor (like Caret) and save it somewhere in the Downloads folder and then move only the code file(s) into Termux where the npm packages are installed and the server is running.\nInstalling the packages inside Termux and copying manually the main js file worked just fine but this needed to be more automated.\nGulp Since I was going to use Node I\u0026rsquo;ve decided to use Gulp for the automated copy (and for the other tasks as well). I\u0026rsquo;m using the code below to sync the files between source folder and destination folder with gulp-newer plugin (there are few other plungs that can be used as well)\nvar gulp = require(\u0026#39;gulp\u0026#39;); var newer = require(\u0026#39;gulp-newer\u0026#39;); var source = \u0026#39;/data/data/com.termux/files/home/storage/downloads/nodetest\u0026#39;; var destination = \u0026#39;../nodetest\u0026#39; gulp.task(\u0026#39;sync\u0026#39;, function() { return gulp.src(source + \u0026#39;/**/*\u0026#39;) .pipe(newer(destination)) .pipe(gulp.dest(destinatioin)); }); gulp.task(\u0026#39;watch\u0026#39;, function() { gulp.watch(source + \u0026#39;/**/*\u0026#39;, [\u0026#39;sync\u0026#39;]); }); After starting the gulp watch task every time file in the source folder is changed it will be moved in the destination folder.\nnodemon I\u0026rsquo;ve also installed nodemon so after every file change the server will be restarted automatically. In my case I\u0026rsquo;ve used --delay 2.5 as a parameter in nodemon to delay the restart with 2.5 seconds - just to make sure that all files are copied.\nResult If Termux is used on phone or tabled to view your web app you just need to navigate to http://localhost. For Chromebooks the situation is a bit more different. You need to find the ip address of the Android instance. This can be done by executing the following command from Termux:\nip addr list And look for the non-localhost address. (in my case this was 192.168.254.2)\nConclusion In conclusion I can say that Termux opens a lot of possibilities for Chromebook/Android developers without entering to Dev mode.\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/termux/","summary":"\u003cp\u003eRecently I\u0026rsquo;ve found a very neat \u003ccode\u003eAdnroid\u003c/code\u003e app which I can run on my Chromebook - \u003ca href=\"https://play.google.com/store/apps/details?id=com.termux\"\u003eTermux\u003c/a\u003e- \u003cstrong\u003e\u0026ldquo;Termux combines powerful terminal emulation with an extensive Linux package collection.\u0026rdquo;\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIn essence after installing it you will have a nice Linux distro on your Android device without root.\u003c/p\u003e\n\u003cp\u003eIn my case I\u0026rsquo;ve decided to try if I can use Termux for Node development.\u003c/p\u003e\n\u003ch2 id=\"node-development\"\u003eNode development\u003c/h2\u003e\n\u003cp\u003eThe initial plan was to install \u003ccode\u003enode\u003c/code\u003e inside Termux and to use it to install \u003ccode\u003enpm\u003c/code\u003e packages and run code.\u003c/p\u003e","title":"Termux"},{"content":"At the moment VSCode is my preferred code editor (although I\u0026rsquo;m side using Atom sometimes as well). And when I\u0026rsquo;m writing something which uses qSocks I\u0026rsquo;ve always have GitHub and Qlik Sense help site open so I can check the available methods and what parameters need to be provided for these methods. Plus I\u0026rsquo;m a \u0026ldquo;bit\u0026rdquo; lazy and always copy/paste code from another projects I have and adapt it to the current needs.\nFor these reasons I\u0026rsquo;ve made VSCode snippets extension. The extension is used from within VSCode and include all qSocks methods as snippets. Just type qsocck and pick the method that you need.\nFor example if qsocks.global.qTProduct method is picked the following code will be generated:\nglobal.qTProduct().then(function(product) { }); If your local variable is different from global you just need to replace it in the generated code. Also the returned variable product can be replaced if you are not happy with the convention. But the main code will be generated automatically.\nThe same is applied and for the request parameters. For example the qsocks.global.createSessionAppFromApp method expect source app id as parameter. The snipped will generate the following code:\nglobal.createSessionAppFromApp(\u0026#39;qSrcAppId\u0026#39;).then(function(sessionApp) { });\u0026#34; In the above example qSrcAppId need to be replaced with the app id you need (also global and sessionApp if you want)\nAnother advantage of using snippets is that you can prepare your logic very quickly instead writing (or copy/paste from somewhere) all the code. Just chain your methods, quickly picking them from the list, change the necessary input/output variables and focus on building your internal logic.\nThe extension include snippets for all qSocks methods:\nmain (config and connection) doc global field generic Bookmark generic Dimension generic Measure generic Object generic Variable To install the extension:\nopen VSCode press Ctrl+P paste ext install qsockssnippet follow the instructions (VSCode need to be restarted in order to install the extension) thats it - just start typing qsocks and pick the method. Hope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/qsocks-snippets-extension-for-vscode/","summary":"\u003cp\u003eAt the moment \u003ca href=\"https://code.visualstudio.com\"\u003eVSCode\u003c/a\u003e is my preferred code editor (although I\u0026rsquo;m side using \u003ca href=\"https://atom.io/\"\u003eAtom\u003c/a\u003e sometimes as well). And when I\u0026rsquo;m writing something which uses \u003ca href=\"https://github.com/mindspank/qsocks\"\u003eqSocks\u003c/a\u003e I\u0026rsquo;ve always have GitHub and \u003ca href=\"http://help.qlik.com/\"\u003eQlik Sense help site\u003c/a\u003e open so I can check the available methods and what parameters need to be provided for these methods. Plus I\u0026rsquo;m a \u0026ldquo;bit\u0026rdquo; lazy and always copy/paste code from another projects I have and adapt it to the current needs.\u003c/p\u003e\n\u003cp\u003eFor these reasons I\u0026rsquo;ve made \u003ca href=\"https://marketplace.visualstudio.com/items?itemName=stefanstoichev.qsockssnippet\"\u003eVSCode snippets extension\u003c/a\u003e. The extension is used from within VSCode and include all qSocks methods as snippets. Just type \u003ccode\u003eqsocck\u003c/code\u003e and pick the method that you need.\u003c/p\u003e","title":"qSocks snippets extension for VSCode"},{"content":"One feature that is missing in Qlik Sense is to send mail notifications when task is completed (fail or success). This feature is available in QlikView Server and I\u0026rsquo;m finding it very useful.\nI\u0026rsquo;ve found 3 ways that this can be achieved in QS:\nLog files scraping QRS API log4net I\u0026rsquo;ll cover the third option in details\nLog files Every QS service have it\u0026rsquo;s own log folder where the activity is logged. Batch file can be written that monitor the Scheduler service log file and on change to scrap the content and mail the result.\nQRS API Ideally this is the most correct way to achieve mail notifications. Qlik Sense Repository Service (QRS) is a REST API service which can be consumed from any language. Notification end points can be set to visit specific url on specific event. Additional web server need to be set and running to handle the calls from QRS. The downside of this approach is that the notification is session based. Which means that if you restart your web server or QRS service is restarted all notifications are gone and need to be created again. Of course this is not a blocker and with additional logic can be handled. Personally I like this approach and will build something in the future using it (already started it here)\nlog4net Alex point few days back that QS have build in log4net functionality which can be used to trigger some events when the QRS log file is updated - send mail, append to another file, add event in windows events, write to database, send udp request etc (the full list is here).\nBasically you just need to create LocalLogConfig.xml file in the service folder which you want to monitor - C:\\ProgramData\\Qlik\\Sense\\\u0026lt;ServiceName\u0026gt;\nFor example below is what the content of LocalLogConfig.xml looks like if you want to send UDP request to UDP server for the Scheduler service:\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34;?\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;appender name=\u0026#34;NodeSchedulerLogger\u0026#34; type=\u0026#34;log4net.Appender.UdpAppender\u0026#34;\u0026gt; \u0026lt;param name=\u0026#34;remoteAddress\u0026#34; value=\u0026#34;localhost\u0026#34; /\u0026gt; \u0026lt;param name=\u0026#34;remotePort\u0026#34; value=\u0026#34;9998\u0026#34; /\u0026gt; \u0026lt;layout type=\u0026#34;log4net.Layout.PatternLayout\u0026#34;\u0026gt; \u0026lt;param name=\u0026#34;conversionpattern\u0026#34; value=\u0026#34;scheduler;%message;%property{ObjectId};%property{ObjectName};%property{Context};%property{Command};%property{Result};\u0026#34; /\u0026gt; \u0026lt;/layout\u0026gt; \u0026lt;/appender\u0026gt; \u0026lt;logger name=\u0026#34;AuditActivity.Scheduler\u0026#34;\u0026gt; \u0026lt;appender-ref ref=\u0026#34;NodeSchedulerLogger\u0026#34; /\u0026gt; \u0026lt;/logger\u0026gt; \u0026lt;/configuration\u0026gt; The above xml define new appender NodeSchedulerLogger from log4net.Appender.UdpAppender type. On new log event log4net will send udp to remoteAddress and remotePort. layout section specify the format of the request and the fields you want (in my case I wanted message, ObjectId, ObjectName, Context, Command and Result fields.\nAfter this is set I\u0026rsquo;ve build small NodeJs UDP server which parse the incoming messages and when the parser find event indicating that task is finished connection to QRS is established and using the API the script is getting the last execution script log and mail it as attachment.\nRepo: qlik-sense-log4net\nI\u0026rsquo;ll try and move with the server part as quickly as possible. Also will include log4net \u0026ldquo;recepies\u0026rdquo; in the repo itself and probably will make separate blog post only for them.\nNear Future I\u0026rsquo;m having a bold plan to extend the NodeJs server part. This will include GUI part where different scenarios will be available to setup.\nFor example:\nSetup mail recipients for all tasks Setup mail recipients per single taks or group of tasks or for tasks with specific custom property Custom mail messages based on the tasks Somewhere in the future (just an ideas) Setup notifications based not only on tasks but also on events from the other services Setup notifications on new/updated documents. For example mail to users which can access a stream when app is added or updated in the stream More log4net intergrations Very good practical usage of log4net approach can be found here. Using Qlik Sense Proxy Service logs Alex has build this web site which show global Qlik Branch users acitivity using the logs from Qlik Sense Proxy Service (QPS).\nAlso you can check Alex\u0026rsquo;s example for using log4net with QPS\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/qlik-sense-tasks-notifications/","summary":"\u003cp\u003eOne feature that is missing in Qlik Sense is to send mail notifications when task is completed (fail or success). This feature is available in QlikView Server and I\u0026rsquo;m finding it very useful.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve found 3 ways that this can be achieved in QS:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eLog files scraping\u003c/li\u003e\n\u003cli\u003eQRS API\u003c/li\u003e\n\u003cli\u003elog4net\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eI\u0026rsquo;ll cover the third option in details\u003c/p\u003e\n\u003ch2 id=\"log-files\"\u003eLog files\u003c/h2\u003e\n\u003cp\u003eEvery QS service have it\u0026rsquo;s own log folder where the activity is logged. Batch file can be written that monitor the \u003ccode\u003eScheduler\u003c/code\u003e service log file and on change to scrap the content and mail the result.\u003c/p\u003e","title":"Qlik Sense Mail Notifications"},{"content":"Recently we had data issue and few documents was affected by this. We notified the users by adding textboxes in these documents with a warning an explanation. But while this particular data issue was still active 2-3 more data issues was found and editing the text boxes was not quite an option.\nWe didn\u0026rsquo;t had enough space in the main sheet there was no guarantee that the users will navigate straight to the main sheet editing all the documents, saving and publish them again was a bit slow process since the apps was few GB each in size For these reasons I\u0026rsquo;ve made a small QV extension that can show notifications which are setup in the server part of this solution\nServer Setup Clone the Github Repo. Navigate to the folder where the file is unzipped and just npm install to downnload the required packages.\nYou can edit the port on which the server is listening. Just edit the port section in config.js file inside Server folder. The default port is 8080\nServer Usage After the server is started just navigate to the main page. There you will find simple interface where notifications can be added or edited.\nAvailable fields:\nDocument - in which document(s) the notification should be shown. If more than one document is specified please separate them with ; Short Description - the actual text of the notification Detailed description - will be shown in notification detail page (when \u0026ldquo;Details\u0026rdquo; button in the notification is pressed) Enabled - if the notification is active or not Valid to - after this date and time the notification will not be shown (will still be active though) Extension Setup After the extension is installed (download the latest qar file from the releases section) navigate to C:\\Users\\USERNAME\\AppData\\Local\\QlikTech\\QlikView\\Extensions\\Document\\notify and open Script.js in any text editor.\nEdit serverUrl and txBoxId values to match your environment (you can leave txBoxId as it is. See below why this variable is needed)\nExtension Usage I haven\u0026rsquo;t found a way to get the qv document name from within document extension API. Because of this to get the notifications create new textbox and add the following expression in it =DocumentName() Change the textbox id to Popup (or to whatever id is specified in the txBoxId variable in extension Script.js file). Put this textbox somewhere where is not visible (do not hide it with show/hide condition because it will not be calculated)\nIf you want to change the style of the extension edit the styles.css file. Be aware that this change will affect all notifications in all documents\nAdd the extension to the documents Settings --\u0026gt; Document properties --\u0026gt; Extensions\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/qlikview-notify/","summary":"\u003cp\u003eRecently we had data issue and few documents was affected by this. We notified the users by adding textboxes in these documents with a warning an explanation. But while this particular data issue was still active 2-3 more data issues was found and editing the text boxes was not quite an option.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWe didn\u0026rsquo;t had enough space in the main sheet\u003c/li\u003e\n\u003cli\u003ethere was no guarantee that the users will navigate straight to the main sheet\u003c/li\u003e\n\u003cli\u003eediting all the documents, saving and publish them again was a bit slow process since the apps was few GB each in size\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFor these reasons I\u0026rsquo;ve made a small QV extension that can show notifications which are setup in the server part of this solution\u003c/p\u003e","title":"Qlikview Notify"},{"content":"Part of one of my projects was to reload app on the fly. The whole process is working fine (more on this in a different post) but wanted to show the reload progress to the user.\nFor this purpose we can use GetProgress() method from Qlik Sense Engine API\nThe code below uses qsocks and shows how to get the reload progress from NodeJS app:\nqsocks.Connect(qsConfig).then(function(global) { global.productVersion() .then(function(version) { return console.log('*** Connected to QS Engine Service') }) .then(function() { return global.openDoc('DOCUMENT ID') }) .then(function(doc) { var reloaded = null; // when the app is finished to reload doc.doReload().then(function() { reloaded = true; console.log('Reloaded'); }); var persistentProgress = ''; var progress = setInterval(function() { if (reloaded != true) { global.getProgress(0).then(function(msg) { if (msg.qPersistentProgress) { persistentProgress = msg.qPersistentProgress; console.log(msg.qPersistentProgress + ' \u0026lt;-- ' + msg.qTransientProgress) } else { if (msg.qTransientProgress) { console.log(persistentProgress + ' \u0026lt;-- ' + msg.qTransientProgress) } } }) } else { clearInterval(progress) } }, 500); // get the reload progress every 500ms }) }) Hope you like it!\nStefan\n","permalink":"https://sstoichev.eu/post/getprogress-qlik-sense/","summary":"\u003cp\u003ePart of one of my projects was to reload app on the fly. The whole process is working fine (more on this in a different post) but wanted to show the reload progress to the user.\u003c/p\u003e\n\u003cp\u003eFor this purpose we can use GetProgress() method from Qlik Sense Engine API\u003c/p\u003e\n\u003cp\u003eThe code below uses qsocks and shows how to get the reload progress from NodeJS app:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eqsocks.Connect(qsConfig).then(function(global) {\n    global.productVersion()\n        .then(function(version) {\n            return console.log('*** Connected to QS Engine Service')\n        })\n        .then(function() {\n            return global.openDoc('DOCUMENT ID')\n        })\n        .then(function(doc) {\n\n            var reloaded = null; // when the app is finished to reload\n            doc.doReload().then(function() {\n                reloaded = true;\n                console.log('Reloaded');\n            });\n\n            var persistentProgress = '';\n\n            var progress = setInterval(function() {\n\n                if (reloaded != true) {\n                    global.getProgress(0).then(function(msg) {\n                        if (msg.qPersistentProgress) {\n                            persistentProgress = msg.qPersistentProgress;\n                            console.log(msg.qPersistentProgress + ' \u0026lt;-- ' + msg.qTransientProgress)\n                        } else {\n                            if (msg.qTransientProgress) {\n                                console.log(persistentProgress + ' \u0026lt;-- ' + msg.qTransientProgress)\n                            }\n                        }\n                    })\n                } else {\n                    clearInterval(progress)\n                }\n            }, 500); // get the reload progress every 500ms\n        })\n})\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eHope you like it!\u003c/p\u003e","title":"GetProgress() (Qlik Sense)"},{"content":"Couple of weeks back Alexander Karlsson (@mindspank) posted a tweet with idea for a new Qlik Sense extension. The extension should look like this: Basically the extension should have a rectangle for each state. The rectangles should be ordered so they will look like USA map. In each rectangle there should be two text values - top one is the state name in short format (WA, ID, MT etc.) and some value (user specified)\nThis looked simple enough so i can use it as use case and practice my d3 skills.\nOverall after I\u0026rsquo;ve managed to put the rectangles in place and \u0026ldquo;bind\u0026rdquo; them to QS hypercube there was nothing much interesting to be done. But I\u0026rsquo;ve wanted to make this extension a bit more useful and interesting.\nUseful The useful part was that the extension objects (texts and rects) can be auto sized based on the extension object size (based on the screen resolution)\nInteresting The interesting part that I\u0026rsquo;ve added option to enable d3 Lasso support. With it you can easily select group of states\nDimensions and expressions You can add 1 dimension ( the field that contains the states names) and 2 expressions:\nthe calculation that will be shown as value (optional) the color of the rectangle. If this expression is not available the color will be based on the defined option Options Various of options are available and they are mostly about the colors ,font colors and sizes.\nYou can find the extension in Qlik Branch and in Github\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/us-map-simplified-qlik-sense-extension/","summary":"\u003cp\u003eCouple of weeks back Alexander Karlsson (\u003ca href=\"https://twitter.com/mindspank\"\u003e@mindspank\u003c/a\u003e) posted a \u003ca href=\"https://twitter.com/mindspank/status/654129751519395840\"\u003etweet\u003c/a\u003e with idea for a new Qlik Sense extension.\nThe extension should look like this:\n\u003cimg alt=\"US states\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/CRPhNXWUEAAblV7.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003eBasically the extension should have a rectangle for each state. The rectangles should be ordered so they will look like USA map. In each rectangle there should be two text values - top one is the state name in short format (WA, ID, MT etc.) and some value (user specified)\u003c/p\u003e","title":"US Map Simplified (Qlik Sense Extension)"},{"content":"Recently I had a task to map set of ip addresses to country in Qlikview. I\u0026rsquo;ve found a nice dataset that can give me this link but the dataset was for ip range to country.\nFor example:\nRangeFrom , RangeTo , Country 1.0.0.1, 1.10.0.1, US The solution I\u0026rsquo;ve come up with is to use IntervalMatch() function and map the ip list with the range. But the ip addresses are not a normal numeric so I need to convert it to numeric somehow. My solution is to make the following covertion:\n1.0.0.1 --\u0026gt; 001000000100 192.168.1.1 --\u0026gt; 192168001001 ... Basically each subnet is converted to a number with leading zeros (if needed) and then all are concatenated in single number. In QV this can be done with the following script:\nnum( SubField( ip, \u0026#39;.\u0026#39;, 1), 000 ) \u0026amp; num( SubField( ip, \u0026#39;.\u0026#39;, 2), 000 ) \u0026amp; num( SubField( ip, \u0026#39;.\u0026#39;, 3), 000 ) \u0026amp; num( SubField( ipS, \u0026#39;.\u0026#39;, 4), 000 ) as ipNum If we apply the same logic and for RangeFrom and RangeTo fields then we will have the ips in number format and IntervalMtch() function can be applied.\nThe script below demonstrate this logic:\nipList: Load num( SubField( IPaddress, \u0026#39;.\u0026#39;, 1), 000 ) \u0026amp; num( SubField( IPaddress, \u0026#39;.\u0026#39;, 2), 000 ) \u0026amp; num( SubField( IPaddress, \u0026#39;.\u0026#39;, 3), 000 ) \u0026amp; num( SubField( IPaddress, \u0026#39;.\u0026#39;, 4), 000 )\tas ipNum, IPaddress ; Load distinct IPaddress From [ipList.txt] (txt, codepage is 1251, embedded labels, delimiter is \u0026#39;|\u0026#39;, msq) ; ranges: Load num( SubField( RangeFrom, \u0026#39;.\u0026#39;, 1), 000 ) \u0026amp; num( SubField( RangeFrom, \u0026#39;.\u0026#39;, 2), 000 ) \u0026amp; num( SubField( RangeFrom, \u0026#39;.\u0026#39;, 3), 000 ) \u0026amp; num( SubField( RangeFrom, \u0026#39;.\u0026#39;, 4), 000 ) as RangeFromNum, num( SubField( RangeTo, \u0026#39;.\u0026#39;, 1), 000 ) \u0026amp; num( SubField( RangeTo, \u0026#39;.\u0026#39;, 2), 000 ) \u0026amp; num( SubField( RangeTo, \u0026#39;.\u0026#39;, 3), 000 ) \u0026amp; num( SubField( RangeTo, \u0026#39;.\u0026#39;, 4), 000 ) as RangeToNum,\tRangeFrom, RangeTo, Country ; Load @1\tas RangeFrom, @2\tas RangeTo, @3\tas Country From [ip-country-range.csv] (txt, codepage is 1251, no labels, delimiter is \u0026#39;,\u0026#39;, msq) ; left join ( ipList ) IntervalMatch ( ipNum ) Load RangeFromNum, RangeToNum Resident ranges; left join ( ipList ) Load * Resident ranges; Drop Table ranges; Hope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/qlikview-ip-ip-range-mapping/","summary":"\u003cp\u003eRecently I had a task to map set of ip addresses to country in Qlikview. I\u0026rsquo;ve found a nice dataset that can give me this link but the dataset was for ip range to country.\u003c/p\u003e\n\u003cp\u003eFor example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eRangeFrom   , RangeTo      , Country\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e1.0.0.1, 1.10.0.1, US\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe solution I\u0026rsquo;ve come up with is to use \u003ccode\u003eIntervalMatch()\u003c/code\u003e function and map the ip list with the range. But the ip addresses are not a normal numeric so I need to convert it to numeric somehow.\nMy solution is to make the following covertion:\u003c/p\u003e","title":"Qlikview IP --\u003e IP Range mapping"},{"content":"We have a project in which we are working with menu navigation data (more like parent-child relation / hierarchical data).\nAlthough there are few approaches and solutions we\u0026rsquo;ve picked few visualisations that might be better to be used with our data.\nWe had our POC app build in Qlikview using only QV charts and the solutions looked ok. But we\u0026rsquo;ve decided to spend more time in this and bring some D3 charts in QV.\nAt the moment I\u0026rsquo;ve brought to live two extensions based on: Radial Tree and Zoomable Sunburst with Labels\nThe Data The data for both extensions is the same and looks like this: (notice the \u0026ldquo;-\u0026rdquo; in the root element)\nnode\t,\tparent\t,\tvalue node 1\t,\t- ,\t1 node 2\t,\tnode 1\t,\t2 node 3\t,\tnode 1\t,\t3 node 4\t,\tnode 1\t,\t4 node 5\t,\tnode 1\t,\t5 node 6\t,\tnode 2\t,\t6 node 7\t,\tnode 2\t,\t7 node 8\t,\tnode 2\t,\t8 node 9\t,\tnode 7\t,\t9 node 10\t,\tnode 7\t,\t10 node 11\t,\tnode 3\t,\t11 node 12\t,\tnode 3\t,\t12 node 13\t,\tnode 3\t,\t13 node 14\t,\tnode 3\t,\t14 node 15\t,\tnode 13\t,\t15 node 16\t,\tnode 11\t,\t16 node 17\t,\tnode 11\t,\t17 node 18\t,\tnode 11\t,\t18 node 19\t,\tnode 16\t,\t19 node 20\t,\tnode 16\t,\t20 node 21\t,\tnode 8\t,\t21 node 22\t,\tnode 8\t,\t22 node 23\t,\tnode 6\t,\t23 node 24\t,\tnode 6\t,\t24 node 25\t,\tnode 4\t,\t25 node 26\t,\tnode 4\t,\t26 node 27\t,\tnode 5\t,\t27 node 28\t,\tnode 5\t,\t28 node 29\t,\tnode 5\t,\t29 node 30\t,\tnode 1\t,\t30 ... node - the actual note text parent - parent node text value - some value associated with the node\nRadial Tree The Radial Tree extension is already available in Qlik Branch and the source code is in GitHub\nThe extension is using the parent-child relationship to create tree looking hierarchy\nI\u0026rsquo;ve tried to add as many options as I can:\nRotation - this option control how round to be the tree. 360 (degree )= full circle Diameter Show values - if \u0026ldquo;true\u0026rdquo; will show the \u0026ldquo;value\u0026rdquo; field value next to the text Circle radius - radius of the node circle in px Circle fill - color fill for the circle. Accept human readable colors (like \u0026ldquo;red\u0026rdquo;, \u0026ldquo;yellow\u0026rdquo;) as well as HEC Circle stroke - circle stroke color Circle stroke width - stroke width in px Stroke color - color of the link lines between nodes Stroke width - link width in px Font size - in px Font family Excluding the \u0026ldquo;Show value\u0026rdquo; all other options can be used from inside QV expression\nZoomable Sunburst Another visualisation (using the same data) is the Zoomable Sunburst. This specific D3 example is already available in Qlik Branch thanks to @brianwmunz for Qlik Sense.\nI\u0026rsquo;ve tried to create it but for Qlikview. The extension is still in development phase (but very close to initial release). You can get the latest source code from GitHub\nAgain I\u0026rsquo;ve tried to add as many options as possible:\nFont size Font family Color scheme - thanks to Cynthia Brewer you can pick from one of many coloring options Color Sub Scheme Opacity - transparency from 0 to 100 Zoom speed (ms) - control the speed of zoom in/out Width Height At the moment of writing of this post the below options are yet not available. But they will be in the next commit or two\nBorder color - the border color of each arc Border width - arc border width Custom color scheme - comma separated list of custom Hex coded colors The extension don\u0026rsquo;t perform selection back to QV \u0026hellip; yet. I\u0026rsquo;m still evaluating the behaviour of the extension.\nIt\u0026rsquo;s not visible in the gif above but the extension will not show the text if its too long. When level is reached where the arcs are bigger the text will be shown.\nThese two extensions was at the top of our list. I\u0026rsquo;ll try and create 1-2 more (after the sunburst is released) from the list that I personally.\nUpdate (2016-03-14) I\u0026rsquo;ve made some changes to the Zoomable Sunburst based on the users feedback:\nColor expression - the color of the elements can be controlled with regular QV expression e.g. if(value \u0026gt; 10, 'blue', 'green'). The colors can be defined in RGB or HEX format Tooltip - tooltop option is added. The text in it can be defined as QV expression as well like HTML string: node \u0026amp; '\u0026lt;br/\u0026gt; \u0026lt;b\u0026gt;' \u0026amp; sum(value) \u0026amp; '\u0026lt;b\u0026gt;' Hope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/radial-tree-and-zoomable-sunburst-qlikview-extensions/","summary":"\u003cp\u003eWe have a project in which we are working with menu navigation data (more like parent-child relation / hierarchical data).\u003c/p\u003e\n\u003cp\u003eAlthough there are few approaches and solutions we\u0026rsquo;ve picked few visualisations that might be better to be used with our data.\u003c/p\u003e\n\u003cp\u003eWe had our POC app build in Qlikview using only QV charts and the solutions looked ok. But we\u0026rsquo;ve decided to spend more time in this and bring some D3 charts in QV.\u003c/p\u003e","title":"Radial Tree and Zoomable Sunburst (Qlikview extensions) (Updated)"},{"content":"After installing Qlikview extension I\u0026rsquo;m usually forgetting to check if the extension have new versions. From time to time I\u0026rsquo;m remembering to check but this is not on very regular basis.\nLast week I\u0026rsquo;ve started a new extension (more about it in the next couple of weeks) and gave the initial version to one of my colleagues for opinion. But to mail him every time when I release new version (especially in the active development phase) will be a bit annoying. So I\u0026rsquo;ve start to think if there a was to notify him more discreet that new version is there.\nFor this reason I\u0026rsquo;ve created small script than can be incorporated in the extension, which will notify the user if new version of the extension is available.\nThe idea is that the developer setup the script, which will check for new version and if its available - create small green arrow icon in top left corner of the extension object.\nPre-requirements:\nthe extension is hosted in Github Github Releases should be used for the qar file (how to use Releases is explained here) Setup There is nothing much to setup in the script itself apart from the current extension version and the release url. This can be done by editing the following lines:\nvar version = \u0026#39;x.xx\u0026#39;; var repoReleaseUrl = \u0026#34;https://api.github.com/repos/USERNAME/REPONAME/releases\u0026#34;; var newVersionMessage = \u0026#34;New version of this extension is available!\u0026#34;; Optionally you can edit and the hover message.\nUsage The usage is in two steps:\ninclude the script in the extension code var extension_path = Qva.Remote + \u0026#34;?public=only\u0026amp;name=Extensions/MyExtension/\u0026#34;; Qva.LoadScript(extension_path + \u0026#34;checkforupdate.js\u0026#34;, function() { ... call the function that will check for updates - call the function right after the creation of the main extension div and pass the div id: Qva.AddExtension(\u0026#39;MyExtension\u0026#39;, function(){ var _this = this; var divName = _this.Layout.ObjectId.replace(\u0026#34;\\\\\u0026#34;, \u0026#34;_\u0026#34;); var ui = document.createElement(\u0026#34;div\u0026#34;); ui.setAttribute(\u0026#34;id\u0026#34;, divName); checkForUpdates(divName); // ... your extension code as usual }) The script didn\u0026rsquo;t check for new version every time the extension is refreshed. The check is made when new session is started (on open of the document for example) otherwise the script will check every time selection is made.\nScreenshots You can find the script in here (you need only the checkforupdate.js file in general)\nYou can use Github issues for any issues or questions or Gitter Hope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/check-for-updates-for-qlikview-extension/","summary":"\u003cp\u003eAfter installing Qlikview extension I\u0026rsquo;m usually forgetting to check if the extension have new versions. From time to time I\u0026rsquo;m remembering to check but this is not on very regular basis.\u003c/p\u003e\n\u003cp\u003eLast week I\u0026rsquo;ve started a new extension (more about it in the next couple of weeks) and gave the initial version to one of my colleagues for opinion. But to mail him every time when I release new version (especially in the active development phase) will be a bit annoying. So I\u0026rsquo;ve start to think if there a was to notify him more discreet that new version is there.\u003c/p\u003e","title":"Check for updates for Qlikview extension"},{"content":"In Qlikview if there is a need to test/debug something developers are just creating some textboxes or straight/pivot tables right next to the dashboard (or in another sheet). In Qlik Sense it\u0026rsquo;s bit more tricky because the free space is kinda limited and if the need to check is on top of already fixed dashboard then some free space need to be made which involve some re-arrangement which I\u0026rsquo;m not big fan of.\nBecause of the above reason I\u0026rsquo;ve made a Chrome extension that will put these request/questions in Chrome Dev Tools (Ctrl + Shift + i)\nThe extension is using qsocks to connect to Qlik Sense.\nWorkflow Qlik Sense Desktop\ninstall the extension\nopen Qlik Sense in Chrome (http://localhost:4848)\nopen an app\nnavigate to Tool \u0026ndash;\u0026gt; More Tools \u0026ndash;\u0026gt; Developer Tools (or press Ctrl + Shift + i)\nclick on \u0026ldquo;Qlik Sense Console\u0026rdquo; you will see the server address and app path (based on the url)\nif you see and QS version next to the server then the communication between the extension and QS is established\npress \u0026ldquo;Open\u0026rdquo; next to the document name\nin a second (depends on the app size) the dimensions dropdown will be populated and the expressions text area will became enabled\nchoose your dimension (or leave the \u0026ldquo;empty dimension\u0026rdquo; if you want the \u0026ldquo;QV textbox\u0026rdquo; behaviour.\ntype your expression in the text area and press \u0026ldquo;Caclulate\u0026rdquo; button\nenjoy the result\nQlik Sense Server\nOne additional step need to be made if you are using QS Server - add the extension id to the allowed origins. To do this:\nopen QS QMC Virtual Proxies double click on the Default one check \u0026ldquo;Advanced\u0026rdquo; from the right menu \u0026ldquo;Add new value\u0026rdquo; button add the extension id: ljeoadpijoacanhinddngndcalkggkmf \u0026ldquo;Apply\u0026rdquo; wait for the proxy to restart from this point onwards the steps are the same as per the desktop edition. Downloads Chrome Web Store GitHub Repo Qlik Branch Future development error messages multiple dimensions (done in v0.9.1) view calculations history and option to re-execute them (done in v0.9.1) sorting the result table (done in v0.9.1) export the results (done in v0.9.1) different visualizations? Update (v0.9.1 05/07/2015) multiple dimensions history - track each calculation execution. option to reuse them result table design is changed result table is sortable (multiple column sort is possible holding the Shift key) option to export/download the result table Screenshots Hope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/qlik/","summary":"\u003cp\u003eIn Qlikview if there is a need to test/debug something developers are just creating some textboxes or straight/pivot tables right next to the dashboard (or in another sheet). In Qlik Sense it\u0026rsquo;s bit more tricky because the free space is kinda limited and if the need to check is on top of already fixed dashboard then some free space need to be made which involve some re-arrangement which I\u0026rsquo;m not big fan of.\u003c/p\u003e","title":"Qlik Sense Console (Chrome extension)"},{"content":"Few months back I\u0026rsquo;ve needed a way to save Qlikivew table (during reload) to csv/txt file but without the field header (still cant remember the reason for this). But today I\u0026rsquo;ve saw that someone else is needing this so I\u0026rsquo;ve decided to solve this.\nThe issue If we have the following script:\nRawTable: Load * Inline [ id, value 1 , 10 2 , 20 3 , 30 ]; Store RawTable Into CSVOutput.csv (txt); The output CSVOutput.csv file will look like this:\nid,value 1,10 2,20 3,30 And what I want is the csv file to look like this:\n1,10 2,20 3,30 The solution There is no straighforward way to do this (or at least I don\u0026rsquo;t know) My idea is:\nto load only the first row from the required table \u0026ldquo;convert\u0026rdquo; all fields content into variables resident load all the rows (apart from the first row) and set column names as the variables content. For example: RawData: Load * Inline [ id,value 1,10 2,20 3,30 ]; Get the first row:\nid, value 1, 10 Loop through the values and create variable for each value. Using the data above will result in two variables: idColumn = 1 and valueColumn = 10.\nLoad the raw data excluding the first row:\nTableToExport: Load id as [\u0026#39;$(idColumn)\u0026#39;], value as [\u0026#39;$(valueColumn)\u0026#39;] Resident RawData Where RecNo() \u0026gt; 1; After executing the script below the result table will be\n1,10 --\u0026gt; header 2,20 --\u0026gt; row 3,30 --\u0026gt; row If you call Store on this table the resulted file will be a csv file without the original header.\nThe above script is simplified to demonstrate the the workflow. I\u0026rsquo;ve made a public Gist that can be used. The script will do all the work and there is only 2 variables that need to be supplied before call it:\nvNH_SourceTable - the name of the table that need to be exported vNH_OutputFile - the path of the exported file. This way you can save the script to external file and just Include (or Must_Include) it.\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/save-qlikview-table-as-csv-without-header/","summary":"\u003cp\u003eFew months back I\u0026rsquo;ve needed a way to save Qlikivew table (during reload) to csv/txt file but without the field header (still cant remember the reason for this). But today I\u0026rsquo;ve saw that someone else is needing this so I\u0026rsquo;ve decided to solve this.\u003c/p\u003e\n\u003ch2 id=\"the-issue\"\u003eThe issue\u003c/h2\u003e\n\u003cp\u003eIf we have the following script:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eRawTable:\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eLoad * Inline [\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  id, value\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  1 , 10\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  2 , 20\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  3 , 30\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eStore RawTable Into CSVOutput.csv (txt);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe output CSVOutput.csv file will look like this:\u003c/p\u003e","title":"Save Qlikview table as csv without header"},{"content":"While waiting for my new and shiny Pebble Time I\u0026rsquo;ve finally managed to spend some time and play with Pebble API. And of course one of the first questions was is it possible to make a link between Pebble and Qlikview/Qlik Sense.\nSince I\u0026rsquo;m more familiar with Qlikview QMS API I\u0026rsquo;ve started with it.\nLimitations Display - since Pebble display is black and white showing charts is kinda obsolete (will check with the color Pebble Time display later). That\u0026rsquo;s why I\u0026rsquo;ve decided to try and show only number based reports. API - Qlikview API is limited when you try and get data from qvw. The only method I\u0026rsquo;ve found was to get specific field content. But think that this is enough since I don\u0026rsquo;t need complicated data. (Qlik Sense API on other side provide more options - building calculation on the fly, get data from objects etc.) Workflow The overall idea/workflow that worked for me is:\nQlikview app(s) holding the data .net app (QV API only supports .net) which will take care of the communication with QV web server that will link Pebble requests with the .net app Pebble app that will display data Qlikview app (qvw) The only data that make sense to be used in this case is simple KPIs: Sales amount: £140, Revenue: £50 etc.\nFor this reason the QV app should contain one extra script part that will calculate this static data.\nThe values in the field should be separated by \u0026ldquo;_\u0026rdquo;.\n.net app (c#) The app is console app that accept two arguments:\nQlikview server - in format http://localhost:4799/QMS/Service Apps and fields - from which qvw files which fields data to be get. Format: ,;, (for example C:\\ProgramData\\QlikTech\\Documents\\PebbleData.qvw,PebbleData;C:\\ProgramData\\QlikTech\\Documents\\PebbleData1.qvw,PebbleData1;) More help is available if the app is started with \u0026ndash;help argument.\nPebble app The app will display the result based on the returned data and will generate the screen title and navigation on the fly.\nIf we have the following data in QV:\nSales_MTD_1426.90 Sales_Today_100.01 Sales_Yesterday_90.50 Margin_MTD_15.7 Margin_Today_10.42 Margin_Yesterday_9.15 The pebble app will create two main categories: Sales and Margin and each category, when selected, will show 3 rows (MTD, Today and Yesterday) and each row will show the corresponding value.\nPebble app configuration The app uses configuration html file which is hosted in Google Drive. This file do not store any data. It contains 3 fields that need to be populated:\nWeb server - url for node.js app Qlikview server - targeted QV server Qlikview files and fields - required QV files and fields You need to enter all 3 values every time change is made (for now). https://googledrive.com/host/0BxjGsOE_3VoOU2RPQ3BjTlBfX0E\nRepo The code is available at https://github.com/countnazgul/QlikviewToPebble.\nThis project contains all files. Nodejs and Pebble app can be found in the main folder\nScreenshots Main screen: Margin selected: Sales selected: Feature Of course Qlik Sense will be the first try. Alexander Karlsson\u0026rsquo;s QSocks (https://github.com/mindspank/qsocks) is very nice project which can be used to handle the communication between Node.js and Qlik Sense\nPebble time - at the moment Javascript is not supported for Pebble SDK 3 (for now) and I\u0026rsquo;m not a C developer so as soon Pebble.js is available will try and build something for Pebble Time. Adding color to Time clear the area for more visual integration.\nNot only Pebble - most of the other smart watches have touch displays. Navigation is only one \u0026ldquo;+\u0026rdquo; when thinking of BI/reporting integration. I will play, in the near future, with Android Wear at least.\nWarning This is just proof of concept project. Some parts might not work from first time. It\u0026rsquo;s not bad idea to be tested first against dev/test Qlikview server.\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/wearable-qlikview/","summary":"\u003cp\u003eWhile waiting for my new and shiny Pebble Time I\u0026rsquo;ve finally managed to spend some time and play with Pebble API. And of course one of the first questions was is it possible to make a link between Pebble and Qlikview/Qlik Sense.\u003c/p\u003e\n\u003cp\u003eSince I\u0026rsquo;m more familiar with Qlikview QMS API I\u0026rsquo;ve started with it.\u003c/p\u003e\n\u003ch2 id=\"limitations\"\u003eLimitations\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eDisplay - since Pebble display is black and white showing charts is kinda obsolete (will check with the color Pebble Time display later). That\u0026rsquo;s why I\u0026rsquo;ve decided to try and show only number based reports.\u003c/li\u003e\n\u003cli\u003eAPI - Qlikview API is limited when you try and get data from qvw. The only method I\u0026rsquo;ve found was to get specific field content. But think that this is enough since I don\u0026rsquo;t need complicated data. (Qlik Sense API on other side provide more options - building calculation on the fly, get data from objects etc.)\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"workflow\"\u003eWorkflow\u003c/h2\u003e\n\u003cp\u003eThe overall idea/workflow that worked for me is:\u003c/p\u003e","title":"Wearable Qlikview"},{"content":"The Technology team asked me these days is it possible to provide them with live data who are the active qlikview users and which documents are accessed. This info is visible in the Management console but since our environment is a cluster with 3 QV servers if you want to check the active users you need to constantly click around to get the info.\nSo I\u0026rsquo;ve ended making small c# console app that uses the Management API to extract this info in csv or json format and store the data in file. So now I can schedule the execution for the app and give Technology the link to the output file.\nThe app accept 3 parameters:\noutput - the path to the file, where the data will be saved. If this parameter is not provide the app will return the data straight in the console\nformat - json or csv (default is csv)\nappend - append the new data to the output file or overwrite it. true or false (default true)\nThe output data have 4 columns - timestamp, server, document and userid (in next version will try and add username column)\nIn the repo is also available example Node.js script which shows how to call the app from node script.\nGithub repo\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/get-qlikview-users-docs/","summary":"\u003cp\u003eThe Technology team asked me these days is it possible to provide them with live data who are the active qlikview users and which documents are accessed. This info is visible in the Management console but since our environment is a cluster with 3 QV servers if you want to check the active users you need to constantly click around to get the info.\u003c/p\u003e\n\u003cp\u003eSo I\u0026rsquo;ve ended making small c# console app that uses the Management API to extract this info in csv or json format and store the data in file. So now I can schedule the execution for the app and give Technology the link to the output file.\u003c/p\u003e","title":"Get Active Qlikview Users\u003c-\u003eDocs"},{"content":"I\u0026rsquo;m missing the option to present Qlik Sense listboxes in a different ways (Not that I don\u0026rsquo;t like the default one I\u0026rsquo;m just missing it)\nSo I\u0026rsquo;ve made an extension that can represent field values as Checkboxes, Radio buttons or Buttons.\nAs you can see from the screenshots below few more options are available:\nOrientation: display the list as single row or multiple rows Toggle select: if \u0026ldquo;false\u0026rdquo; the behavior of the selection will be * more like \u0026ldquo;only one selected\u0026rdquo; option in Qlikview Active style (css): style the selected value(s) with valid CSS InActive style (css): style the non selected value(s) with valid CSS You can find the source code and the zip file in the Github repo\nUpdate (2014-12-03) v1.1 \u0026ldquo;Default selected value\u0026rdquo; option is added - when field doesn\u0026rsquo;t have anything selected this value will be automatically selected. Just type the desired value (case sensitive). For example if you want to have always Month selected (lets say \u0026ldquo;Dec\u0026rdquo;) just type \u0026ldquo;Dec\u0026rdquo;.\nUse case\nOne specific use case of the extension is to mimic cycle expressions (Sense do not provide this (for now) by default). My solution is to create one simple inline table with the possible options\nset HidePrefix = \u0026#39;_\u0026#39;; _Expressions: Load * Inline [ _ExpressionId, _Expression 1, Revenue 2, Quantity 3, Unit price ]; And then use the _Expression filed as dimension in the extension. In the chart itself then I can write expression that looks like this:\n= pick(_ExpressionId , sum(Revenue) , sum(Quantity) , sum(Revenue) / sum(Quantity) ) (This approach can be used in some cases and for Dimensions)\nFor this specific usecase I can use the \u0026ldquo;Default selected value\u0026rdquo; option and will be sure that the chart always show some data and not just blank chart.\n####Screenshots\nOptions Radio buttons on multiple rows: Checkboxes on single row: Buttons on single row: Custom styles: Hope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/styled-listbox-qlik-sense-extension/","summary":"\u003cp\u003eI\u0026rsquo;m missing the option to present Qlik Sense listboxes in a different ways (Not that I don\u0026rsquo;t like the default one I\u0026rsquo;m just missing it)\u003c/p\u003e\n\u003cp\u003eSo I\u0026rsquo;ve made an extension that can represent field values as Checkboxes, Radio buttons or Buttons.\u003c/p\u003e\n\u003cp\u003eAs you can see from the screenshots below few more options are available:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eOrientation: display the list as single row or multiple rows\u003c/li\u003e\n\u003cli\u003eToggle select: if \u0026ldquo;false\u0026rdquo; the behavior of the selection will be * more like \u0026ldquo;only one selected\u0026rdquo; option in Qlikview\u003c/li\u003e\n\u003cli\u003eActive style (css): style the selected value(s) with valid CSS\u003c/li\u003e\n\u003cli\u003eInActive style (css): style the non selected value(s) with valid CSS\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eYou can find the source code and the zip file in the \u003ca href=\"https://github.com/countnazgul/Qlik-Sense-Styled-Lists-Extension\"\u003eGithub repo\u003c/a\u003e\u003c/p\u003e","title":"Styled listbox (Qlik Sense Extension) (Updated)"},{"content":"If you manage multiple Qlik servers (Qlikview or Sense) and need to access the management consoles multiple times per day you usually keep a list of servers urls somewhere (at least this is what I have). In this case opening this list every time or try to remember server url might be a bit frustrating sometimes.\nFor this reason I\u0026rsquo;ve made a little Chrome packaged app that can help a little. The app itself is pretty simple. It is possible to add all the QMC urls and the app will display the selected server url in webview control The app will also generate shortcuts for all main QMC categories (like Source Documents, User Documents, Licenses etc.)\nIn settings page is available option to specify if the server is Qlikview or Qlik Sense. Based on the setup the app will generate the proper links.\nThe urls format in settings page should be: QlikView: http://server:4780/qmc Qlik Sense: https://server/qmc\nYou can download the app from Chrome web store\nYou can find the source code in the github repo\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/qmc-viewer/","summary":"\u003cp\u003eIf you manage multiple Qlik servers (Qlikview or Sense) and need to access the management consoles multiple times per day you usually keep a list of servers urls somewhere (at least this is what I have). In this case opening this list every time or try to remember server url might be a bit frustrating sometimes.\u003c/p\u003e\n\u003cp\u003eFor this reason I\u0026rsquo;ve made a little Chrome packaged app that can help a little.\nThe app itself is pretty simple. It is possible to add all the QMC urls and the app will display the selected server url in webview control\nThe app will also generate shortcuts for all main QMC categories (like Source Documents, User Documents, Licenses etc.)\u003c/p\u003e","title":"QMC Viewer"},{"content":"I\u0026rsquo;ve started playing with Qlik Sense extensions these days and pretty much I\u0026rsquo;m happy with the process in overall.\nYou can develop extensions using Workbench (if you have Qlik Sense Desktop installed) or any text editor. But if you have Sense Server installed you need to develop directly on the server or upload your files every time a change is made.\nFrom 7-8 months I\u0026rsquo;m a proud owner of Chromebook and I\u0026rsquo;m trying to use it as many as possible. At this point I can use it as my default laptop for work.\nIn terms of Sense extensions development I\u0026rsquo;ve used it to login to the server via RDP and edit the extensions there.\nFrom few days I\u0026rsquo;m trying new approach:\nI\u0026rsquo;ve installed FTP server on the server and made my home folder to be the Sense extensions folder. Then using ShiftEdit to edit the files and on each save ShiftEdit upload the edited files.\nIn my case I\u0026rsquo;m using ShiftEdit but other tools can be used. Almost all modern text editors have FTP sync modules. I\u0026rsquo;ve tested this approach with Notepad++, Atom.io, Sublime and Adobe Brackets and the result was the same.\nIf you don\u0026rsquo;t want to use 3-rd party tools and you are ok with no using advanced text editor features you can use sFTP Client app (not free. £1.89 one time) for Chrome/Chrome OS.\nAlso found interesting tool that will watch it in the future - Zed\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/develop-qlik-sene-extension-with-chromebook/","summary":"\u003cp\u003eI\u0026rsquo;ve started playing with Qlik Sense extensions these days and pretty much I\u0026rsquo;m happy with the process in overall.\u003c/p\u003e\n\u003cp\u003eYou can develop extensions using Workbench (if you have Qlik Sense Desktop installed) or any text editor. But if you have Sense Server installed you need to develop directly on the server or upload your files every time a change is made.\u003c/p\u003e\n\u003cp\u003eFrom 7-8 months I\u0026rsquo;m a proud owner of Chromebook and I\u0026rsquo;m trying to use it as many as possible. At this point I can use it as my default laptop for work.\u003c/p\u003e","title":"Develop Qlik Sene extension with Chromebook"},{"content":"Every QlikView app have a section with available selections (usually on top or in the left side or both). This sections are static and are not visible all the time when the user scroll (if the tab contains more content which cannot fit on the visible screen).\nI was missing \u0026ldquo;fixed\u0026rdquo; selections areas in QlikView from time to time but never did anything about it.\nIn desktop/IE plugin is not possible to have it but AJAX client is a different story. And the result is FixedContent.\nFixedContent is a QlikView document extension that can change the CSS of QlikView object(s) so they appear as fixed when the page is scrolling.\nTo identify which objects need to be fixed just change their ObjectID to include \u0026ldquo;_fixed\u0026rdquo; string.\nFor example the original ObjectID TX01 should became \u0026ldquo;TX01_fixed\u0026rdquo;.\nThe extension will take care also for tabrow and the AJAX toolbar. Both toolbars will be fixed automatically for the document.\nForm the video below you can see how it behave. Since this is a web view in the desktop client the gap at the top will be filled with the AJAX toolbar when the document is open in web browser.\nMacro Helper Instead changing the ids if the required objects manually, I\u0026rsquo;ve made a macro that will change it automatically for all selected/active objects. Also there is another sub that will restore the id to original for the selected objects.\nYou can find the subs in the repo.\nDirect link to QAR file\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/fixedcontent-qlikview-extension/","summary":"\u003cp\u003eEvery QlikView app have a section with available selections (usually on top or in the left side or both). This sections are static and are not visible all the time when the user scroll (if the tab contains more content which cannot fit on the visible screen).\u003c/p\u003e\n\u003cp\u003eI was missing \u0026ldquo;fixed\u0026rdquo; selections areas in QlikView from time to time but never did anything about it.\u003c/p\u003e\n\u003cp\u003eIn desktop/IE plugin is not possible to have it but AJAX client is a different story. And the result is \u003ca href=\"https://github.com/countnazgul/Qlikview-Fixed-Content\"\u003eFixedContent\u003c/a\u003e.\u003c/p\u003e","title":"FixedContent (Qlikview Extension)"},{"content":"At every point everyone had a need to compare two texts to spot the differences between them.\nIn my case I wanted to compare two csv files. There are a lot of options how to perform this. But I wanted to be online.\nSo I\u0026rsquo;ve found jsdifflib. Small JS lib that do the job.\nI\u0026rsquo;ve took the demo page (with very small changes) and package it as Chrome App, which can be used offline.\nYou can get the app from here\nAnd thanks to cemerik for the lib!\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/text-diff-chrome-app/","summary":"\u003cp\u003eAt every point everyone had a need to compare two texts to spot the differences between them.\u003c/p\u003e\n\u003cp\u003eIn my case I wanted to compare two csv files. There are a lot of options how to perform this. But I wanted to be online.\u003c/p\u003e\n\u003cp\u003eSo I\u0026rsquo;ve found \u003ca href=\"http://cemerick.github.io/jsdifflib/demo.html\"\u003ejsdifflib\u003c/a\u003e. Small JS lib that do the job.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve took the demo page (with very small changes) and package it as Chrome App, which can be used offline.\u003c/p\u003e","title":"Text Diff (Chrome App)"},{"content":"If you have one Qlikview Access Point then you have no problems but when you have few Qlikview environments and multiple customers and want to open qv document then you are in trouble (kinda).\nOrganizing bookmarks is one solutions that helps.\nI was thinking recently how to speed up the process of open document quickly and decided to make a little Chrome extension that will help.\nThe idea of the extension is simple - define few Qlikview Access Points links and get the documents that are available. Then just search the list for document and jump in it.\nHere is the screenshot from the latest version This is the list of the documents from the official Qlikview demo site\n####Documents List\nWhen you open the extension you will see the full list of the documents (to which the logged user have access), document availability (ajax, mobile, ie) and server color (explained below).\nHovering over document will show you some additional info about the document like category, size, modified dates etc.\nBy default search is performed over various options:\nqvw file name category (if you set the document category in QMC) server name tag (explained below) ####Options\nOptions page is used to setup the list of servers.\nFor each server you need to specify\nlink - the root link in format http://server-ip-or-name username - username to login in format DOMAIN\\username password - user password Identifier - optional name tag. Can be used to filter documents Color - optional. If you have multiple Access Point defined this color will give you more visual way to see to which server the document belong. Overall options page view: Color coding the servers can help sometimes: ####Security Chrome offer two ways of storing data from extension - local and sync. Both act the same way. The difference is that data stored using \u0026ldquo;sync\u0026rdquo; storage will be synced across all your Chrome sessions.\nI\u0026rsquo;ve decided to use the \u0026ldquo;local\u0026rdquo; approach \u0026hellip; it just don\u0026rsquo;t sync the data across internet.\n####Future Nothing much left to be done actually. When I started the extension the current functionality was pretty much what I was imagined.\nFew features might be added in the future:\nBackground sync - Extension will grab the documents list every X minutes in background and when you open the extension you will have the full list instantly (more likely to implement this) Implemented More advanced search - at the moment you cannot sub filter the result after search. What I was thinking that will be nice when you type \u0026ldquo;server1\u0026rdquo; and the list is filtered if you then add \u0026ldquo;, marketing\u0026rdquo; in the search bar to filter the document for marketing category. Since im not the author of the search implementation it might be little tricky to implement this but it will be interesting :) Sorting - option to sort the documents list by name, lat modified, category etc. Implemented The extension is available in the Chrome Web Store\nIf you have any problems or questions don\u0026rsquo;t waste time and mail me :)\n####Update (2014-10-01)\nBackground page is added. The extension will check for documents every 10 min. You can check manually for new documents from Options page \u0026ndash;\u0026gt; Servers Sorting options are added. Two main sorting options are available: Title, Sever, Last Modify and Last updated Asc and Desc Fixed bug that was not loading all documents \u0026hellip; sometimes Extension is updated in the chrome web store.\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/qlikview-access-point-chrome-extension/","summary":"\u003cp\u003eIf you have one Qlikview Access Point then you have no problems but when you have few Qlikview environments and multiple customers and want to open qv document then you are in trouble (kinda).\u003c/p\u003e\n\u003cp\u003eOrganizing bookmarks is one solutions that helps.\u003c/p\u003e\n\u003cp\u003eI was thinking recently how to speed up the process of open document quickly and decided to make a little Chrome extension that will help.\u003c/p\u003e\n\u003cp\u003eThe idea of the extension is simple - define few Qlikview Access Points links and get the documents that are available. Then just search the list for document and jump in it.\u003c/p\u003e","title":"QlikView Access Point as Chrome Extension (Updated)"},{"content":"Extension update (v1.2.4):\nextension now have a button to short the current tab. The short link point directly to current tab url. Chrome web store link\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/pack-bookmarks-chrome-update-3/","summary":"\u003cp\u003eExtension update (v1.2.4):\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eextension now have a button to short the current tab.\nThe short link point directly to current tab url.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003ca href=\"https://chrome.google.com/webstore/detail/extensions-update-notifie/nlldbplhbaopldicmcoogopmkonpebjm?hl=en\"\u003eChrome web store link\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eHope you like it!\nStefan\u003c/p\u003e","title":"Pack Bookmarks (Chrome) (Update 3)"},{"content":"Just a note to myself (kinda) how to perform bulk update in MongoDB. (it took me some time to figure it out)\n// OrderId = [1,2,3,4,5,6] BulkUpdate(OrderId, function() { console.log(\u0026#39;done\u0026#39;); }); function BulkUpdate(OrderId, callback) { Model.update( { orderid : { $in: OrderId } }, { $set: { status: \u0026#39;complete\u0026#39; } }, { multi : true}).exec(callback); } Update (2014-09-16) And here is the bulk insert code example:\n// JsonArray = [{id: 1, name: test1}, {id: 1, name: test2}] Model.collection.insert(JsonArray, function(err, result) { console.log(\u0026#39;Inserted: \u0026#39; + result.length); }); Hope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/mongodb-bulk-update/","summary":"\u003cp\u003eJust a note to myself (kinda) how to perform bulk update in MongoDB. (it took me some time to figure it out)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e// OrderId = [1,2,3,4,5,6] \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eBulkUpdate(OrderId, function() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\tconsole.log(\u0026#39;done\u0026#39;);\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003efunction BulkUpdate(OrderId, callback) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Model.update(\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    { orderid : { $in: OrderId } },\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    { $set: { status: \u0026#39;complete\u0026#39; } },\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    { multi : true}).exec(callback);\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"update-2014-09-16\"\u003eUpdate (2014-09-16)\u003c/h4\u003e\n\u003cp\u003eAnd here is the bulk insert code example:\u003c/p\u003e","title":"MongoDB bulk update and insert"},{"content":"Well this was easy.\nThe extension is updated and now you have an option to \u0026ldquo;pack\u0026rdquo; and set of currently open tabs. Below is a screenshot with the new functionality:\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/pack-bookmarks-chrome-update-2/","summary":"\u003cp\u003eWell this was easy.\u003c/p\u003e\n\u003cp\u003eThe extension is updated and now you have an option to \u0026ldquo;pack\u0026rdquo; and set of currently open tabs.\nBelow is a screenshot with the new functionality:\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"alt\" loading=\"lazy\" src=\"/images/2014/Sep/OpenTabs.png\"\u003e\u003c/p\u003e\n\u003cp\u003eHope you like it!\nStefan\u003c/p\u003e","title":"Pack Bookmarks (Chrome) (Update 2)"},{"content":"Aaaaaand its done ..\nweb server is moved on more permanent host changes in the server code base is made extension is also updated - few bugs where fixed and also is added functionality to auto copy the result link to clipboard database is moved to MongoHQ/Compose If you are interested, the extension source is published here\nThe extension itself is now public and can be found here The server doesn\u0026rsquo;t have dedicated domain but can be found at 146.185.148.8. Will try to make more user friendly home page in the future.\nDuring the process of moving server part to his new host I\u0026rsquo;ve found very nice service Codeship.io. This helps me to deploy the app to the production server only with the push commit from Codio dev server \u0026hellip; time saver :) Will try and write something about Codeship.io in the future.\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/pack-bookmarks-chrome-update/","summary":"\u003cp\u003eAaaaaand its done ..\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eweb server is moved on more permanent host\u003c/li\u003e\n\u003cli\u003echanges in the server code base is made\u003c/li\u003e\n\u003cli\u003eextension is also updated - few bugs where fixed and also is added functionality to auto copy the result link to clipboard\u003c/li\u003e\n\u003cli\u003edatabase is moved to \u003ca href=\"https://www.compose.io/\"\u003eMongoHQ/Compose\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIf you are interested, the extension source is published \u003ca href=\"https://bitbucket.org/countnazgul/bookmark_pack/overview\"\u003ehere\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eThe extension itself is now public and can be found \u003ca href=\"https://chrome.google.com/webstore/detail/pack-bookmarks/behnbnpefkgolldpingmeabfnbkdjhno\"\u003ehere\u003c/a\u003e\nThe server doesn\u0026rsquo;t have dedicated domain but can be found at \u003ca href=\"http://146.185.148.8/\"\u003e146.185.148.8\u003c/a\u003e. Will try to make more user friendly home page in the future.\u003c/p\u003e","title":"Pack Bookmarks (Chrome) (Update)"},{"content":"It\u0026rsquo;s been a while since I wrote something here. A lot of personal and professional changes happened to me in this period. But this is a different story.\nBelow is another story \u0026hellip;\nThe issue Recently I had a need to send few links to friend of mine. All links was saved as Chrome Bookmarks. I\u0026rsquo;ve opened all of them and send him the all the links.\nI\u0026rsquo;ve lost some time to open all the links and then copy and paste them. So I\u0026rsquo;ve decide to make a little chrome extension and small node.js app/server that will help me in the future to simplify this process.\nThe extension give you option to select few bookmarks and \u0026ldquo;pack\u0026rdquo; them. The server part will get the urls and will return a short link from goo.gl, which will lead to a simple web page where the bookmarks links will be \u0026ldquo;un-packed\u0026rdquo;.\nThe solution The Extension In terms of functionality the extension is pretty much ready. There might be need of few design changes in near future. And after the server part is moved to more reliable place I\u0026rsquo;ll need to update the link for server communication.\nThe Server Again in terms of functionality the server is ready. 3 methods are available:\nnew - to create new \u0026ldquo;pack\u0026rdquo; link get - display the links in the \u0026ldquo;package\u0026rdquo; and actually the goo.gl link is leading to this page custom - option to \u0026ldquo;pack\u0026rdquo; link manually without the Chrome extension The Future The Extension One feature that can be added to the extension is to have the same functionality and for opened tabs.\nThe Server A lot of things need to be added to the server part.\nMost needed is design. At the moment there is not design \u0026hellip; literally :) After the design part I\u0026rsquo;m thinking to make change in the server code - build REST API and make the server part to self consume the API. Also to enable the API for 3rd party users (if somebody is interested) Change the data storage - at the moment whole data is stored in file storage. Need to move it to proper MongoDB db When above changes are made I\u0026rsquo;ll move the server part on more reliable server. At the moment is just a dev box in Codio. Be aware that the server is in development for the moment and migh not work all the time And of course publish the code for the extension and server (so the real developers have a chance to have a good laugh after see it :) )\nThe extension is published for anyone with link: https://chrome.google.com/webstore/detail/pack-bookmarks/behnbnpefkgolldpingmeabfnbkdjhno\nAnd below is a screenshot from the extension itself:\nThats it for now.\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/pack-bookmarks-chrome/","summary":"\u003cp\u003eIt\u0026rsquo;s been a while since I wrote something here. A lot of personal and professional  changes happened to me in this period. But this is a different story.\u003c/p\u003e\n\u003cp\u003eBelow is another story \u0026hellip;\u003c/p\u003e\n\u003ch3 id=\"the-issue\"\u003eThe issue\u003c/h3\u003e\n\u003cp\u003eRecently I had a need to send few links to friend of mine. All links was saved as Chrome Bookmarks. I\u0026rsquo;ve opened all of them and send him the all the links.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve lost some time to open all the links and then copy and paste them. So I\u0026rsquo;ve decide to make a little chrome extension and small node.js app/server that will help me in the future to simplify this process.\u003c/p\u003e","title":"Pack Bookmarks (Chrome)"},{"content":"Until now I didn\u0026rsquo;t find a good way to re-use code in Qlikview. I\u0026rsquo;ve tried few ways to achieve it but was never totally happy with the result.\nI\u0026rsquo;m using Gist from time to time but a lot of my gists are private (most of them are project specifics) and for this reason I was thinking that it\u0026rsquo;s impossible to use Gist snippets from inside Qlikview.\nBut recently I\u0026rsquo;ve found that private gists are not so private after all. If you know the exact url the gist is displayed even when you\u0026rsquo;re not log in.\nIf you follow this link you will find one test snipped that is actually private.\nFrom the picture bellow you will see that the snipped is private\nUsing this \u0026ldquo;workaround\u0026rdquo; it\u0026rsquo;s very easy to include any snippet in Qlikview script:\n$(Include=https://gist.github.com/countnazgul/63c43a8bbff17ab139fd/raw/df48d0c602fd54fe7ba1f0777af161871378949c/test); Wish everybody Happy New Year! Stefan\n","permalink":"https://sstoichev.eu/post/qlikview-and-gist/","summary":"\u003cp\u003eUntil now I didn\u0026rsquo;t find a good way to re-use code in Qlikview. I\u0026rsquo;ve tried few ways to achieve it but was never totally happy with the result.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m using \u003ca href=\"\"\u003eGist\u003c/a\u003e from time to time but a lot of my gists are private (most of them are project specifics) and for this reason I was thinking that it\u0026rsquo;s impossible to use Gist snippets from inside Qlikview.\u003c/p\u003e\n\u003cp\u003eBut recently I\u0026rsquo;ve found that private gists are not so private after all. If you know the exact url the gist is displayed even when you\u0026rsquo;re not log in.\u003c/p\u003e","title":"Qlikview and Gist"},{"content":"My previous post was about little tool which is capable to show and export CPU and RAM usage of a specific Windows process.\nI\u0026rsquo;ve used this tool to grab resource usage of QlikView during reload. Combining the exported csv file and QlikView log file I was able to build qvw file that shows resources per script part. So I was able to see which script part(s) grab the most of resources and try to optimize it.\nHere is a few screenshots from the qvw file:\n####Overall With the CPU/RAM sliders you can set the amount above which the values will be marked as above threshold. The imput boxes are used to specify the location of the csv and log files. ####CPU/RAM usage per time ####CPU/RAM usage per code part id ####Code parts time execution The table give you an info about each part time execution, cpu, ram/ram% usage. Also the colors indicate the high usage parts. The script is on the right part. Selectind some partId will show you the exact code. So sorting and filtering the table will show you the parts that might need optimization.\nNeed to clear the script first and will publish the qvw file.\nHope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/qlikview-script-resources/","summary":"\u003cp\u003eMy \u003ca href=\"__GHOST_URL__/process-cpuram-usage/\"\u003eprevious post\u003c/a\u003e was about little tool which is capable to show and export CPU and RAM usage of a specific Windows process.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve used this tool to grab resource usage of QlikView during reload. Combining the exported csv file and QlikView log file I was able to build qvw file that shows resources per script part. So I was able to see which script part(s) grab the most of resources and try to optimize it.\u003c/p\u003e","title":"QlikView script resources"},{"content":"Recentyl I\u0026rsquo;ve needed a way to watch CPU and RAM usage of a speciffic Windows process real-time and also to export the result. I\u0026rsquo;ve made a quich search but finally decided to write it by myself.\nHere is the image of the final result:\nThe only setting that need to be made is to enter the PID of the monitored process (and press \u0026ldquo;Start\u0026rdquo; ofc)\nThe tool will grab CPU/RAM usage of the process each second and will display the results in a grid view and in two basic line charts (one for CPU and one for RAM usage)\nAfter the monitoring process is over press the \u0026ldquo;Stop\u0026rdquo; button. After this the \u0026ldquo;Export\u0026rdquo; button will became available.\nYou can grab the tool from here\nUPDATED: New version is uploaded. App will export also column with the total RAM available on the machine.This column is not visible in the GUI.\n###How to find process PID To find the process PID:\nopen Windows Task Manager (right click on taskbar and select \u0026ldquo;Start Task Manager\u0026rdquo;) View \u0026ndash;\u0026gt; Select Columns Check (if it\u0026rsquo;s not checked already) \u0026ldquo;PID (Process Identifier)\u0026rdquo; you will see new column inside the process list - PID Hope you like it! Stefan\n","permalink":"https://sstoichev.eu/post/process-cpuram-usage/","summary":"\u003cp\u003eRecentyl I\u0026rsquo;ve needed a way to watch CPU and RAM usage of a speciffic Windows process real-time and also to export the result. I\u0026rsquo;ve made a quich search but finally decided to write it by myself.\u003c/p\u003e\n\u003cp\u003eHere is the image of the final result:\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/2013/Oct/PID_Resources.png\"\u003e\u003c/p\u003e\n\u003cp\u003eThe only setting that need to be made is to enter the PID of the monitored process (and press \u0026ldquo;Start\u0026rdquo; ofc)\u003c/p\u003e\n\u003cp\u003eThe tool will grab CPU/RAM usage of the process each second and will display the results in a grid view and in two basic line charts (one for CPU and one for RAM usage)\u003c/p\u003e","title":"Process CPU/RAM Usage"},{"content":"TBA\n","permalink":"https://sstoichev.eu/about/","summary":"about","title":"About"}]