Friday, 10 July 2026

Enviri JD for BI

 Job Description:

  • Experience working closely with business users/leaders.
  • Well-versed in requirement gathering and can lead business users in requirement gathering sessions.
  • Engage with stakeholders to define KPIs and reporting needs
  • Should have the ability to transform the requirements into a BI visualization.
  • Develop BI solutions with enterprise architecture
  • Drive innovation and adoption of best practices
  • Work closely with BI developers, Data Engineers, and Analysts in developing interactive and decision-making Power BI visualizations.

Key Responsibilities:

Client Engagement & Requirement Gathering:

  • Collaborate directly with clients and business stakeholders to understand and document BI requirements.
  • Conduct workshops and discovery sessions to define KPIs, reporting needs, and success criteria.

Solution Design & Proof of Concept Development:

  • Translate business requirements into intuitive and impactful BI visualizations.
  • Develop the proof of concepts (PoCs) to validate solution approaches and demonstrate value to stakeholders.
  • Present PoCs and prototypes to clients for feedback and refinement.

Technical Strategy & Governance:

  • Develop BI solutions with enterprise architecture and data governance standards.
  • Drive adoption of best practices in data modeling, visualization, and performance optimization.

Team Player:

  • Foster a culture of innovation, collaboration, and continuous improvement.

Technical Skills:

  • Advanced Power BI Development: - Expertise in building interactive dashboards and reports. Strong command of Power Query and DAX (including advanced functions).
  • Data Modeling & ETL: - Ability to design and optimize complex data models. Experience with data transformation pipelines.
  • SQL Proficiency: - Writing and debugging complex SQL queries. Performance tuning using SQL Server/Oracle.
  • Performance Optimization: - Use of tools like DAX Studio and SQL Profiler for report tuning.
  • Version Control & Deployment: - Git/Azure DevOps for version control for managing BI assets.
  • Agile/Scrum Practices: - Experience working in Agile environments


Education:

  • Bachelor's in Engineering (or) Technology /Master's in Computer Science Applications/Information Systems from a recognized College/University.
  • Experience
  • 6 To 10 years of experience in a BI reporting environment. 2 years in a lead role working closely with Business stakeholders and leading requirement gathering sessions.

Knowledge and Skills:

  • Hands-on experience in SQL, PL/SQL, Packages/Procedures.
  • Strong knowledge of DAX Studio, Tabular Editor, Power BI Report Builder, and ALM Tool Kit.
  • Good knowledge of Azure Cloud Computing

Preferred:

  • Certification in Microsoft PL-300, Microsoft AZ-900 and Microsoft DP-900.
  • Should have knowledge Software development life cycle.

Script download chat Gpt chat converstaion

 Note:copy below script code and paste in console window for which chat gpt chat history you want.then enter.


 (async () => {
  "use strict";

  const SCROLL_STEP_RATIO = 1.8;
  const MAX_SCROLL_PASSES = 220;
  const IDLE_PASSES_TO_STOP = 4;
  const PDF_LIB_URL = "https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js";

  function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  function createProgressPanel() {
    const panel = document.createElement("div");
    panel.style.position = "fixed";
    panel.style.top = "18px";
    panel.style.right = "18px";
    panel.style.zIndex = "2147483647";
    panel.style.width = "320px";
    panel.style.background = "#ffffff";
    panel.style.border = "1px solid #d8dee8";
    panel.style.borderRadius = "14px";
    panel.style.boxShadow = "0 12px 30px rgba(15, 23, 42, 0.18)";
    panel.style.padding = "14px";
    panel.style.font = '13px/1.4 "Segoe UI", Arial, sans-serif';
    panel.style.color = "#111827";

    const title = document.createElement("div");
    title.textContent = "Loading conversation...";
    title.style.fontWeight = "700";

    const status = document.createElement("div");
    status.textContent = "Preparing fast auto-scroll";
    status.style.marginTop = "6px";
    status.style.color = "#4b5563";

    const cancel = document.createElement("button");
    cancel.type = "button";
    cancel.textContent = "Cancel";
    cancel.style.marginTop = "12px";
    cancel.style.border = "1px solid #cbd5e1";
    cancel.style.background = "#f8fafc";
    cancel.style.borderRadius = "8px";
    cancel.style.padding = "6px 12px";
    cancel.style.cursor = "pointer";

    panel.appendChild(title);
    panel.appendChild(status);
    panel.appendChild(cancel);
    document.body.appendChild(panel);

    const state = { cancelled: false };
    cancel.addEventListener("click", () => {
      state.cancelled = true;
      status.textContent = "Cancelling...";
      cancel.disabled = true;
    });

    return {
      isCancelled: () => state.cancelled,
      update: (text) => {
        status.textContent = text;
      },
      done: () => {
        panel.remove();
      }
    };
  }

  function loadScript(url) {
    return new Promise((resolve, reject) => {
      const existing = Array.from(document.querySelectorAll("script")).find((s) => s.src === url);
      if (existing && window.html2pdf) {
        resolve();
        return;
      }

      const script = document.createElement("script");
      script.src = url;
      script.async = true;
      script.onload = () => resolve();
      script.onerror = () => reject(new Error(`Failed to load script: ${url}`));
      document.head.appendChild(script);
    });
  }

  function chooseExportFormat() {
    const raw = (prompt("Export format? Type WORD or PDF", "WORD") || "WORD").trim().toLowerCase();
    return raw === "pdf" ? "pdf" : "word";
  }

  function chooseExportScope() {
    const help = [
      "Export scope?",
      "1 = Complete conversation",
      "2 = Single ChatGPT response (latest)",
      "3 = Selected messages (highlight text first)",
      "4 = ChatGPT responses only"
    ].join("\n");
    const raw = (prompt(help, "1") || "1").trim().toLowerCase();

    if (raw === "2" || raw === "single") {
      return "single";
    }
    if (raw === "3" || raw === "selected") {
      return "selected";
    }
    if (raw === "4" || raw === "assistant" || raw === "chatgpt") {
      return "assistant";
    }
    return "all";
  }

  function getSelectionMessageNodes() {
    const selection = window.getSelection();
    if (!selection || !selection.rangeCount || selection.isCollapsed) {
      return [];
    }

    const range = selection.getRangeAt(0);
    const all = Array.from(document.querySelectorAll("[data-message-author-role]"));
    return all.filter((node) => {
      try {
        return range.intersectsNode(node);
      } catch (_error) {
        return false;
      }
    });
  }

  function normalizeText(text) {
    return (text || "").replace(/\r\n/g, "\n").trim();
  }

  function cleanFileName(name) {
    return (name || "chatgpt-chat")
      .replace(/[\\/:*?"<>|]/g, "-")
      .replace(/\s+/g, " ")
      .trim()
      .slice(0, 80);
  }

  function normalizePossibleTitle(value) {
    return (value || "")
      .replace(/\s+/g, " ")
      .replace(/\s*-\s*ChatGPT\s*$/i, "")
      .trim();
  }

  function pickBestTitle(candidates) {
    const cleaned = candidates
      .map((x) => normalizePossibleTitle(x))
      .filter((x) => x && x.length > 1 && !/^chatgpt$/i.test(x));

    if (!cleaned.length) {
      return "chatgpt-chat";
    }

    // Prefer longer descriptive titles over short/generic labels.
    cleaned.sort((a, b) => b.length - a.length);
    return cleaned[0];
  }

  function resolveConversationTitle() {
    const titleCandidates = [];

    // Browser title is usually the most reliable conversation title.
    titleCandidates.push(document.title || "");

    const metaOg = document.querySelector('meta[property="og:title"]')?.getAttribute("content") || "";
    const metaTwitter = document.querySelector('meta[name="twitter:title"]')?.getAttribute("content") || "";
    titleCandidates.push(metaOg, metaTwitter);

    const selectorCandidates = [
      '[data-testid="conversation-title"]',
      'a[aria-current="page"]',
      'nav [aria-current="page"]',
      'main header h1',
      'header h1'
    ];

    selectorCandidates.forEach((selector) => {
      const el = document.querySelector(selector);
      if (el) {
        titleCandidates.push(el.textContent || "", el.getAttribute("title") || "");
      }
    });

    return pickBestTitle(titleCandidates);
  }

  function escapeHtml(input) {
    return (input || "")
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/\"/g, "&quot;")
      .replace(/'/g, "&#39;");
  }

  function blobToDataUrl(blob) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result);
      reader.onerror = () => reject(new Error("Failed to read image blob."));
      reader.readAsDataURL(blob);
    });
  }

  async function imageUrlToDataUrl(url) {
    if (!url) {
      throw new Error("Image URL is empty.");
    }

    if (url.startsWith("data:")) {
      return url;
    }

    const response = await fetch(url, { credentials: "include" });
    if (!response.ok) {
      throw new Error(`Image fetch failed with ${response.status}`);
    }

    const blob = await response.blob();
    return blobToDataUrl(blob);
  }

  function imageElementToDataUrl(imgEl) {
    try {
      if (!imgEl || !imgEl.naturalWidth || !imgEl.naturalHeight) {
        return null;
      }
      const canvas = document.createElement("canvas");
      canvas.width = imgEl.naturalWidth;
      canvas.height = imgEl.naturalHeight;
      const ctx = canvas.getContext("2d");
      if (!ctx) {
        return null;
      }
      ctx.drawImage(imgEl, 0, 0);
      return canvas.toDataURL("image/png");
    } catch (_error) {
      return null;
    }
  }

  function getImageSource(imgEl) {
    if (!imgEl) {
      return "";
    }
    return (
      imgEl.currentSrc ||
      imgEl.getAttribute("src") ||
      imgEl.getAttribute("data-src") ||
      imgEl.getAttribute("data-original") ||
      ""
    );
  }

  function getScrollableContainer() {
    const all = Array.from(document.querySelectorAll("div, main, section"));
    const candidates = all.filter((el) => {
      const style = window.getComputedStyle(el);
      const canScroll = /(auto|scroll)/.test(style.overflowY);
      return canScroll && el.scrollHeight > el.clientHeight + 120;
    });

    candidates.sort((a, b) => b.scrollHeight - a.scrollHeight);
    return candidates[0] || document.scrollingElement || document.documentElement;
  }

  function textHash(input) {
    const str = input || "";
    let hash = 0;
    for (let i = 0; i < str.length; i += 1) {
      hash = (hash << 5) - hash + str.charCodeAt(i);
      hash |= 0;
    }
    return String(hash);
  }

  function getStableMessageId(node) {
    return (
      node.getAttribute("data-message-id") ||
      node.getAttribute("data-testid") ||
      node.getAttribute("id") ||
      ""
    );
  }

  function getNodeFingerprint(node) {
    const role = (node.getAttribute("data-message-author-role") || "").toLowerCase();
    const stableId = getStableMessageId(node);
    const text = normalizeText(node.innerText).replace(/\s+/g, " ");
    const shortText = text.slice(0, 1000);
    const imageCount = node.querySelectorAll("img").length;
    return `${role}|id:${stableId}|txt:${textHash(shortText)}|img:${imageCount}`;
  }

  function collectVisibleMessages(store, sequenceState) {
    const visibleNodes = Array.from(document.querySelectorAll("[data-message-author-role]"));
    visibleNodes.forEach((node) => {
      const key = getNodeFingerprint(node);
      const existing = store.get(key);
      if (!existing) {
        store.set(key, {
          key,
          node,
          firstSeen: sequenceState.value
        });
        sequenceState.value += 1;
      } else {
        existing.node = node;
      }
    });
  }

  async function collectEntireConversation(progress) {
    const container = getScrollableContainer();
    const messagesMap = new Map();

    if (!container) {
      return [];
    }

    // Start at top and collect strictly while moving top -> bottom.
    progress.update("Phase 1/1: Jumping to top...");
    container.scrollTop = 0;
    await sleep(140);

    const step = Math.max(200, Math.floor(container.clientHeight * SCROLL_STEP_RATIO));

    // Collect while sweeping top -> bottom so ordering remains stable.
    progress.update("Phase 1/1: Sweeping top to bottom...");
    const sequenceState = { value: 0 };
    let idlePasses = 0;
    let previousCount = -1;
    let previousScroll = -1;

    for (let pass = 0; pass < MAX_SCROLL_PASSES; pass += 1) {
      if (progress.isCancelled()) {
        return [];
      }

      collectVisibleMessages(messagesMap, sequenceState);

      if (pass % 8 === 0) {
        progress.update(`Phase 1/1: Sweeping... ${messagesMap.size} messages captured`);
      }

      const currentCount = messagesMap.size;
      if (currentCount === previousCount) {
        idlePasses += 1;
      } else {
        idlePasses = 0;
      }
      previousCount = currentCount;

      const beforeTop = container.scrollTop;
      container.scrollTop = Math.min(container.scrollHeight, beforeTop + step);
      await sleep(40);

      const atBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 4;
      const stuck = container.scrollTop === previousScroll;
      previousScroll = container.scrollTop;

      if (atBottom) {
        collectVisibleMessages(messagesMap, sequenceState);
        await sleep(100);
        collectVisibleMessages(messagesMap, sequenceState);
        if (idlePasses >= IDLE_PASSES_TO_STOP || stuck) {
          break;
        }
      }
    }

    const collected = Array.from(messagesMap.values());
    collected.sort((a, b) => a.firstSeen - b.firstSeen);

    return collected.map((x) => x.node);
  }

  function getMessageContentNode(messageNode) {
    const candidates = [
      ".markdown",
      ".prose",
      ".whitespace-pre-wrap",
      "[data-testid='conversation-turn-content']"
    ];

    for (const selector of candidates) {
      const el = messageNode.querySelector(selector);
      if (el && normalizeText(el.innerText)) {
        return el;
      }
    }

    return messageNode;
  }

  async function buildMessagePayload(messageNode) {
    const contentNode = getMessageContentNode(messageNode);
    const text = normalizeText(contentNode.innerText);

    const clonedContent = contentNode.cloneNode(true);
    const clonedImages = Array.from(clonedContent.querySelectorAll("img"));
    const sourceImages = Array.from(contentNode.querySelectorAll("img"));

    await Promise.all(
      clonedImages.map(async (img, index) => {
        const srcImg = sourceImages[index];
        const src = getImageSource(srcImg) || getImageSource(img);

        if (!src) {
          img.remove();
          return;
        }

        try {
          const dataUrl = await imageUrlToDataUrl(src);
          img.setAttribute("src", dataUrl);
        } catch (_error) {
          const drawnData = imageElementToDataUrl(srcImg || img);
          if (drawnData) {
            img.setAttribute("src", drawnData);
          } else {
            // Keep original source as fallback if inlining fails.
            img.setAttribute("src", src);
          }
        }

        img.removeAttribute("srcset");
        img.removeAttribute("sizes");
        img.setAttribute("loading", "eager");
      })
    );

    const html = clonedContent.innerHTML && clonedContent.innerHTML.trim().length
      ? clonedContent.innerHTML
      : escapeHtml(contentNode.innerText).replace(/\n/g, "<br>");

    return { text, html };
  }

  function filterMessagesByScope(messageNodes, scope) {
    if (scope === "assistant") {
      return messageNodes.filter(
        (node) => (node.getAttribute("data-message-author-role") || "").toLowerCase() === "assistant"
      );
    }

    if (scope === "single") {
      const latestAssistant = [...messageNodes]
        .reverse()
        .find((node) => (node.getAttribute("data-message-author-role") || "").toLowerCase() === "assistant");
      return latestAssistant ? [latestAssistant] : [];
    }

    if (scope === "selected") {
      const selectedNodes = getSelectionMessageNodes();
      if (selectedNodes.length) {
        return selectedNodes;
      }
      alert("No selected messages found. Please highlight message text first. Exporting complete conversation instead.");
      return messageNodes;
    }

    return messageNodes;
  }

  function buildMetaLabel(scope) {
    if (scope === "assistant") {
      return "ChatGPT responses only";
    }
    if (scope === "single") {
      return "Single ChatGPT response";
    }
    if (scope === "selected") {
      return "Selected messages";
    }
    return "Complete conversation";
  }

  function scopeSuffix(scope) {
    if (scope === "assistant") {
      return "assistant-only";
    }
    if (scope === "single") {
      return "single-response";
    }
    if (scope === "selected") {
      return "selected";
    }
    return "full-chat";
  }

  async function exportAsPdf(htmlDoc, fileNameBase) {
    await loadScript(PDF_LIB_URL);
    if (!window.html2pdf) {
      throw new Error("PDF engine did not load.");
    }

    const holder = document.createElement("div");
    holder.style.position = "fixed";
    holder.style.left = "-100000px";
    holder.style.top = "0";
    holder.style.width = "1280px";
    holder.style.background = "#ffffff";
    holder.innerHTML = htmlDoc;
    document.body.appendChild(holder);

    const target = holder.querySelector("body") || holder;
    const opt = {
      margin: [8, 8, 8, 8],
      filename: `${fileNameBase}.pdf`,
      image: { type: "jpeg", quality: 0.98 },
      html2canvas: { scale: 2, useCORS: true, allowTaint: true },
      jsPDF: { unit: "mm", format: "a4", orientation: "portrait" },
      pagebreak: { mode: ["css", "legacy"] }
    };

    await window.html2pdf().set(opt).from(target).save();
    holder.remove();
  }

  function exportAsWord(htmlDoc, fileNameBase) {
    const blob = new Blob([htmlDoc], { type: "application/msword;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `${fileNameBase}.doc`;
    document.body.appendChild(a);
    a.click();
    a.remove();
    URL.revokeObjectURL(url);
    return a.download;
  }

  const exportFormat = chooseExportFormat();
  const exportScope = chooseExportScope();

  const progress = createProgressPanel();
  const collectedNodes = await collectEntireConversation(progress);
  progress.done();

  if (!collectedNodes.length) {
    alert("Export cancelled or no messages collected.");
    return;
  }

  const messageNodes = filterMessagesByScope(collectedNodes, exportScope);

  if (!messageNodes.length) {
    alert("No messages matched your export scope.");
    return;
  }

  const entriesRaw = await Promise.all(
    messageNodes.map(async (node) => {
      const roleRaw = (node.getAttribute("data-message-author-role") || "").toLowerCase();
      const role = roleRaw === "assistant" ? "ChatGPT" : roleRaw === "user" ? "You" : "System";
      const payload = await buildMessagePayload(node);
      return { role, text: payload.text, html: payload.html, roleRaw };
    })
  );

  const entries = entriesRaw.filter((x) => x.text.length > 0);

  if (!entries.length) {
    alert("Messages were found, but text content could not be extracted.");
    return;
  }

  const title = cleanFileName(resolveConversationTitle());

  const now = new Date();
  const stamp = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(
    now.getDate()
  ).padStart(2, "0")}`;
  const scopeName = buildMetaLabel(exportScope);
  const fileNameBase = `${title}_${scopeSuffix(exportScope)}_${stamp}`;

  const messagesHtml = entries
    .map((e) => {
      const roleClass = e.roleRaw === "user" ? "user" : e.roleRaw === "assistant" ? "assistant" : "system";
      return `
        <article class="msg ${roleClass}">
          <div class="role">${escapeHtml(e.role)}</div>
          <div class="content">${e.html}</div>
        </article>
      `;
    })
    .join("\n");

  const htmlDoc = `<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>${escapeHtml(title)}</title>
  <style>
    :root {
      --page-bg: #f3f6fb;
      --ink: #1f2937;
      --muted: #5b6470;
      --card: #ffffff;
      --user-accent: #2864dc;
      --assistant-accent: #16a085;
      --system-accent: #7a5ccf;
      --code-bg: #f1f3f6;
      --code-ink: #1a1f2a;
      --line: #dde2ea;
    }

    * { box-sizing: border-box; }

    body {
      margin: 0;
      color: var(--ink);
      font: 15px/1.6 "Segoe UI", Tahoma, Arial, sans-serif;
      background:
        radial-gradient(circle at 10% 0%, #ffffff 0%, #eaf0ff 35%, transparent 60%),
        radial-gradient(circle at 100% 10%, #e8fff8 0%, transparent 35%),
        var(--page-bg);
      padding: 28px;
    }

    .sheet {
      max-width: 900px;
      margin: 0 auto;
      background: var(--card);
      border: 1px solid var(--line);
      border-radius: 18px;
      box-shadow: 0 12px 35px rgba(12, 22, 44, 0.08);
      padding: 28px;
    }

    .head h1 {
      margin: 0;
      font-size: 26px;
      line-height: 1.25;
    }

    .meta {
      margin-top: 6px;
      color: var(--muted);
      font-size: 13px;
    }

    .meta + .meta {
      margin-top: 2px;
    }

    .timeline {
      margin-top: 22px;
      display: grid;
      gap: 14px;
    }

    .msg {
      border: 1px solid var(--line);
      border-left-width: 6px;
      border-radius: 14px;
      padding: 14px 16px;
      background: #fff;
    }

    .msg.user { border-left-color: var(--user-accent); background: #f8fbff; }
    .msg.assistant { border-left-color: var(--assistant-accent); }
    .msg.system { border-left-color: var(--system-accent); background: #fbfaff; }

    .role {
      font-size: 12px;
      letter-spacing: 0.05em;
      text-transform: uppercase;
      font-weight: 700;
      color: var(--muted);
      margin-bottom: 8px;
    }

    .content h1, .content h2, .content h3, .content h4 {
      margin: 0.8em 0 0.35em;
      line-height: 1.3;
    }

    .content h1 { font-size: 24px; }
    .content h2 { font-size: 21px; }
    .content h3 { font-size: 18px; }
    .content h4 { font-size: 16px; }

    .content p { margin: 0.4em 0 0.75em; }
    .content ul, .content ol { margin: 0.4em 0 0.9em 1.4em; }
    .content li { margin: 0.22em 0; }
    .content blockquote {
      margin: 0.8em 0;
      border-left: 4px solid #c5d4f2;
      padding: 0.3em 0.8em;
      color: #3e4b63;
      background: #f8fbff;
    }

    .content pre {
      margin: 0.8em 0;
      background: var(--code-bg);
      color: var(--code-ink);
      border: 1px solid #d7dde7;
      border-radius: 12px;
      padding: 12px 14px;
      overflow: auto;
      white-space: pre-wrap;
      word-break: break-word;
      font: 13px/1.5 Consolas, "Courier New", monospace;
    }

    .content code {
      background: #eef2f8;
      border: 1px solid #d8dfe9;
      border-radius: 6px;
      padding: 1px 5px;
      font: 12px Consolas, "Courier New", monospace;
    }

    .content pre code {
      background: transparent;
      border: none;
      border-radius: 0;
      padding: 0;
      font: inherit;
    }

    .content table {
      border-collapse: collapse;
      margin: 0.8em 0;
      width: 100%;
    }

    .content img {
      display: block;
      max-width: 100%;
      height: auto;
      margin: 0.7em 0;
      border: 1px solid #d7dde7;
      border-radius: 10px;
    }

    .content th,
    .content td {
      border: 1px solid var(--line);
      padding: 6px 8px;
      text-align: left;
      vertical-align: top;
    }
  </style>
</head>
<body>
  <main class="sheet">
    <header class="head">
      <h1>${escapeHtml(title)}</h1>
      <div class="meta">Exported on ${escapeHtml(now.toLocaleString())}</div>
      <div class="meta">Scope: ${escapeHtml(scopeName)} | Format: ${escapeHtml(exportFormat.toUpperCase())}</div>
    </header>
    <section class="timeline">
      ${messagesHtml}
    </section>
  </main>
</body>
</html>`;

  let downloadName = "";
  if (exportFormat === "pdf") {
    await exportAsPdf(htmlDoc, fileNameBase);
    downloadName = `${fileNameBase}.pdf`;
  } else {
    downloadName = exportAsWord(htmlDoc, fileNameBase);
  }

  const container = getScrollableContainer();
  if (container) {
    container.scrollTop = container.scrollHeight;
  }

  console.log(`Export complete: ${downloadName}`);
  alert(`Exported ${entries.length} messages to ${downloadName}`);
})();

Enviri JD for BI

  Job Description: Experience working closely with business users/leaders. Well-versed in requirement gathering and can lead business users ...