KIITConnect Data Management

Adds a settings menu for downloading data, uploading data, and downloading a file

  1. // ==UserScript==
  2. // @name KIITConnect Data Management
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.6
  5. // @description Adds a settings menu for downloading data, uploading data, and downloading a file
  6. // @author Bibek
  7. // @match https://www.kiitconnect.com/*
  8. // @grant none
  9. // @icon https://www.kiitconnect.com/favicon.ico
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. const filenameDivSelector = '.flex.w-full.pt-10.md\\:pt-0.pb-6.text-base.mt-5.md\\:py-10.md\\:my-0.font-bold.md\\:text-2xl.justify-center.items-center span.pl-2';
  17.  
  18. // Ensure the filename div is detected
  19. function ensureFilenameDivLoaded(callback) {
  20. const retryInterval = 100; // Check every 100ms
  21. const maxRetries = 50; // Limit retries to avoid infinite loop
  22. let retries = 0;
  23.  
  24. const interval = setInterval(() => {
  25. const filenameDiv = document.querySelector(filenameDivSelector);
  26. if (filenameDiv) {
  27. clearInterval(interval);
  28. callback(filenameDiv);
  29. } else if (++retries >= maxRetries) {
  30. clearInterval(interval);
  31. console.error('Filename div not found within the retry limit.');
  32. }
  33. }, retryInterval);
  34. }
  35.  
  36. // Function to handle PYQS functionality
  37. function executePYQsCode() {
  38. // alert("Now looking for PYQs.");
  39. console.log('Executing PYQS functionality...');
  40. // Your existing PYQS logic here
  41. // Function to create and insert the settings icon and menu
  42. function createSettingsMenu() {
  43. // Create settings icon
  44. const settingsIcon = document.createElement('img');
  45. settingsIcon.src = 'https://cdn-icons-png.flaticon.com/512/2697/2697990.png';
  46. settingsIcon.alt = 'Settings';
  47. settingsIcon.style.width = '40px';
  48. settingsIcon.style.cursor = 'pointer';
  49. settingsIcon.style.position = 'fixed';
  50. settingsIcon.style.bottom = '20px';
  51. settingsIcon.style.right = '20px';
  52. settingsIcon.style.zIndex = '1000';
  53.  
  54. // Create menu container
  55. const menuContainer = document.createElement('div');
  56. menuContainer.style.display = 'none';
  57. menuContainer.style.flexDirection = 'column';
  58. menuContainer.style.position = 'fixed';
  59. menuContainer.style.bottom = '70px';
  60. menuContainer.style.right = '10px';
  61. menuContainer.style.backgroundColor = '#041d35';
  62. menuContainer.style.backdropFilter = 'blur(5px)';
  63. menuContainer.style.padding = '10px';
  64. menuContainer.style.border = '1px solid #143267';
  65. menuContainer.style.borderRadius = '50px';
  66. menuContainer.style.zIndex = '1000';
  67.  
  68. // Toggle menu visibility on settings icon click
  69. settingsIcon.addEventListener('click', () => {
  70. menuContainer.style.display = menuContainer.style.display === 'none' ? 'flex' : 'none';
  71. });
  72.  
  73. // Create download CSV icon
  74. const downloadCSVIcon = document.createElement('img');
  75. downloadCSVIcon.src = 'https://cdn-icons-png.flaticon.com/512/9153/9153957.png';
  76. downloadCSVIcon.alt = 'Download CSV';
  77. downloadCSVIcon.style.width = '40px';
  78. downloadCSVIcon.style.cursor = 'pointer';
  79. downloadCSVIcon.style.marginBottom = '10px';
  80. downloadCSVIcon.addEventListener('click', downloadCSVData);
  81.  
  82. // Create upload icon (opens Google Form)
  83. const uploadIcon = document.createElement('img');
  84. uploadIcon.src = 'https://cdn-icons-png.flaticon.com/512/10152/10152423.png';
  85. uploadIcon.alt = 'Upload';
  86. uploadIcon.style.width = '40px';
  87. uploadIcon.style.cursor = 'pointer';
  88. uploadIcon.style.marginBottom = '10px';
  89. uploadIcon.addEventListener('click', () => {
  90. window.open('https://forms.gle/sb4uEPXWmhCvRcBG6', '_blank');
  91. });
  92.  
  93. // Create download file icon
  94. const downloadFileIcon = document.createElement('img');
  95. downloadFileIcon.src = 'https://cdn-icons-png.flaticon.com/512/1091/1091007.png';
  96. downloadFileIcon.alt = 'Download File';
  97. downloadFileIcon.style.width = '40px';
  98. downloadFileIcon.style.cursor = 'pointer';
  99. downloadFileIcon.addEventListener('click', () => {
  100. window.open('https://drive.google.com/drive/folders/11FZF-h8tmulA9LgLLV9E4s2LbGD_Rf7iI5BwCl35WdZ-vfbuQzDZcGgP1IwiwkRVJ-rK0uoU?usp=sharing', '_blank');
  101. });
  102.  
  103. // Append icons to menu container
  104. menuContainer.appendChild(downloadCSVIcon);
  105. menuContainer.appendChild(uploadIcon);
  106. menuContainer.appendChild(downloadFileIcon);
  107.  
  108. // Append settings icon and menu container to document body
  109. document.body.appendChild(settingsIcon);
  110. document.body.appendChild(menuContainer);
  111. }
  112.  
  113. // Function to scrape data and download as CSV
  114. function downloadCSVData() {
  115. // Retrieve the filename from the specific div
  116. const filenameDiv = document.querySelector('.flex.w-full.pt-10.md\\:pt-0.pb-6.text-base.mt-5.md\\:py-10.md\\:my-0.font-bold.md\\:text-2xl.justify-center.items-center span.pl-2');
  117. const filename = filenameDiv ? filenameDiv.textContent.trim() : 'subject_data';
  118.  
  119. // Initialize an array to hold all subjects with their respective rows
  120. const allSubjectsData = [];
  121.  
  122. // Select all subject name elements
  123. const subjectNameElements = document.querySelectorAll('.py-3.md\\:py-5.relative.rounded-md.text-\\[12px\\].md\\:text-2xl.font-bold.text-center.border.border-slate-500.text-gray-300');
  124.  
  125. // Iterate over each subject name element
  126. subjectNameElements.forEach(subjectElement => {
  127. // Extract the subject name
  128. const subjectName = subjectElement.textContent.trim().replace(/^Syllabus\s*/, '');
  129.  
  130. // Select the table rows within this subject
  131. const rows = subjectElement.nextElementSibling.querySelectorAll('tbody.text-white.text-base tr');
  132.  
  133. // Iterate over each row within the subject
  134. rows.forEach(row => {
  135. // Extract row data
  136. const pyqYear = row.querySelector('td.whitespace-nowrap.px-6.font-bold.py-4.text-slate-300')?.textContent.trim() || '';
  137. const pyqType = row.querySelector('td.whitespace-nowrap.px-6.text-gray-400.font-bold.hidden.md\\:block.py-4')?.textContent.trim() || '';
  138.  
  139. const pyqLinkElement = row.querySelector('td.whitespace-nowrap.px-6.py-4 a.font-bold.text-cyan-500');
  140. const pyqLink = pyqLinkElement ? pyqLinkElement.href : '';
  141. const pyqName = pyqLinkElement ? pyqLinkElement.textContent.trim() : '';
  142.  
  143. // Extract the last 33 characters of the question code from the link
  144. // const questionCode = pyqLink ? pyqLink.match(/academic\/view\/([a-zA-Z0-9_-]{33})\?/)[1] : '';
  145. const questionCode = pyqLink ? (pyqLink.match(/([a-zA-Z0-9_-]{33})\?/)?.[1] || '') : '';
  146.  
  147. const pyqSolutionLinkElement = row.querySelector('td.whitespace-nowrap.px-6.py-4.font-bold.text-gray-400 a.text-cyan-500');
  148. const pyqSolutionLink = pyqSolutionLinkElement ? pyqSolutionLinkElement.href : '';
  149. const solutionTextElement = row.querySelector('td.whitespace-nowrap.px-6.py-4.font-bold.text-gray-400 span');
  150. const solution = pyqSolutionLinkElement
  151. ? pyqSolutionLinkElement.textContent.trim()
  152. : (solutionTextElement ? solutionTextElement.textContent.trim() : 'Not Available');
  153.  
  154. // Get the additional solutionStatus text
  155. const solutionStatusText = row.querySelector('td.whitespace-nowrap.px-6.py-4.font-bold.text-gray-400')?.textContent.trim() || '';
  156.  
  157. const solutionCode = pyqSolutionLink ? (pyqSolutionLink.match(/([a-zA-Z0-9_-]{33})\?/)?.[1] || '') : ''; // new line
  158.  
  159. // Add row data to the allSubjectsData array with subject name included
  160. allSubjectsData.push({
  161. subject: subjectName,
  162. year: pyqYear,
  163. type: pyqType,
  164. pyqName: pyqName,
  165. pyqLink: pyqLink,
  166. questionCode: questionCode,// Add question code here
  167. solutionLink: pyqSolutionLink,
  168. solution: solution,
  169. solutionStatus: solutionStatusText,
  170. solutionCode: solutionCode,
  171. });
  172. });
  173. });
  174.  
  175. // Function to convert JSON to CSV format
  176. function convertToCSV(data) {
  177. const headers = [
  178. 'Subject',
  179. 'Year',
  180. 'Type',
  181. 'PYQ Name',
  182. 'PYQ Link',
  183. 'Question Code',
  184. 'Solution Link',
  185. 'Solution',
  186. 'Solution Status',
  187. 'solutionCode',
  188. 'Question Link', // New column header
  189. 'Solution Link' // New column header
  190. ];
  191.  
  192. const rows = data.map(row => [
  193. row.subject,
  194. row.year,
  195. row.type,
  196. row.pyqName,
  197. row.pyqLink,
  198. row.questionCode,
  199. row.solutionLink,
  200. row.solution,
  201. row.solutionStatus,
  202. row.solutionCode,
  203.  
  204. // Question Link
  205. row.questionCode
  206. ? `=HYPERLINK("https://drive.google.com/file/d/${row.questionCode}/edit", "${row.year} ${row.pyqName}")`.replace(/"/g, '""')
  207. : 'Not Available',
  208. // Solution Link
  209. row.solutionCode
  210. ? `=HYPERLINK("https://drive.google.com/file/d/${row.solutionCode}/edit", "${row.year} ${row.pyqName} Solution")`.replace(/"/g, '""')
  211. : 'Not Available'
  212.  
  213. ]);
  214.  
  215. // Convert rows to CSV format
  216. return [headers.join(','), ...rows.map(row => row.map(cell => `"${cell}"`).join(','))].join('\n');
  217. }
  218.  
  219.  
  220.  
  221.  
  222. // Convert the data to CSV format
  223. const csvData = convertToCSV(allSubjectsData);
  224.  
  225. // Create a Blob from the CSV data and trigger the download
  226. const blob = new Blob([csvData], { type: 'text/csv' });
  227. const url = URL.createObjectURL(blob);
  228. const a = document.createElement('a');
  229. a.href = url;
  230. a.download = `${filename}.csv`;
  231. a.style.display = 'none';
  232. document.body.appendChild(a);
  233. a.click();
  234. document.body.removeChild(a);
  235. URL.revokeObjectURL(url); // Clean up the URL object
  236. }
  237.  
  238. // Run the script only on the specified page
  239. if (window.location.href.startsWith('https://www.kiitconnect.com/')) {
  240. createSettingsMenu();
  241. }
  242. }
  243.  
  244. // Function to handle NOTES functionality
  245. function executeNOTESCode() {
  246. // alert("Now looking for NOTES.");
  247. console.log('Executing NOTES functionality...');
  248. // Your existing NOTES logic here
  249. // Function to create and insert the settings icon and menu
  250. function createSettingsMenu() {
  251. // Create settings icon
  252. const settingsIcon = document.createElement('img');
  253. settingsIcon.src = 'https://cdn-icons-png.flaticon.com/512/2697/2697990.png';
  254. settingsIcon.alt = 'Settings';
  255. settingsIcon.style.width = '40px';
  256. settingsIcon.style.cursor = 'pointer';
  257. settingsIcon.style.position = 'fixed';
  258. settingsIcon.style.bottom = '20px';
  259. settingsIcon.style.right = '20px';
  260. settingsIcon.style.zIndex = '1000';
  261.  
  262. // Create menu container
  263. const menuContainer = document.createElement('div');
  264. menuContainer.style.display = 'none';
  265. menuContainer.style.flexDirection = 'column';
  266. menuContainer.style.position = 'fixed';
  267. menuContainer.style.bottom = '70px';
  268. menuContainer.style.right = '10px';
  269. menuContainer.style.backgroundColor = '#041d35';
  270. menuContainer.style.backdropFilter = 'blur(5px)';
  271. menuContainer.style.padding = '10px';
  272. menuContainer.style.border = '1px solid #143267';
  273. menuContainer.style.borderRadius = '50px';
  274. menuContainer.style.zIndex = '1000';
  275.  
  276. // Toggle menu visibility on settings icon click
  277. settingsIcon.addEventListener('click', () => {
  278. menuContainer.style.display = menuContainer.style.display === 'none' ? 'flex' : 'none';
  279. });
  280.  
  281. // Create download CSV icon
  282. const downloadCSVIcon = document.createElement('img');
  283. downloadCSVIcon.src = 'https://cdn-icons-png.flaticon.com/512/9153/9153957.png';
  284. downloadCSVIcon.alt = 'Download CSV';
  285. downloadCSVIcon.style.width = '40px';
  286. downloadCSVIcon.style.cursor = 'pointer';
  287. downloadCSVIcon.style.marginBottom = '10px';
  288. downloadCSVIcon.addEventListener('click', downloadCSVData);
  289.  
  290. // Create upload icon (opens Google Form)
  291. const uploadIcon = document.createElement('img');
  292. uploadIcon.src = 'https://cdn-icons-png.flaticon.com/512/10152/10152423.png';
  293. uploadIcon.alt = 'Upload';
  294. uploadIcon.style.width = '40px';
  295. uploadIcon.style.cursor = 'pointer';
  296. uploadIcon.style.marginBottom = '10px';
  297. uploadIcon.addEventListener('click', () => {
  298. window.open('https://forms.gle/sb4uEPXWmhCvRcBG6', '_blank');
  299. });
  300.  
  301. // Create download file icon
  302. const downloadFileIcon = document.createElement('img');
  303. downloadFileIcon.src = 'https://cdn-icons-png.flaticon.com/512/1091/1091007.png';
  304. downloadFileIcon.alt = 'Download File';
  305. downloadFileIcon.style.width = '40px';
  306. downloadFileIcon.style.cursor = 'pointer';
  307. downloadFileIcon.addEventListener('click', () => {
  308. window.open('https://drive.google.com/drive/folders/11FZF-h8tmulA9LgLLV9E4s2LbGD_Rf7iI5BwCl35WdZ-vfbuQzDZcGgP1IwiwkRVJ-rK0uoU?usp=sharing', '_blank');
  309. });
  310.  
  311. // Append icons to menu container
  312. menuContainer.appendChild(downloadCSVIcon);
  313. menuContainer.appendChild(uploadIcon);
  314. menuContainer.appendChild(downloadFileIcon);
  315.  
  316. // Append settings icon and menu container to document body
  317. document.body.appendChild(settingsIcon);
  318. document.body.appendChild(menuContainer);
  319. }
  320.  
  321.  
  322.  
  323. function downloadCSVData() {
  324. // Retrieve the filename from the specific div
  325. const filenameDiv = document.querySelector('.flex.w-full.pt-10.md\\:pt-0.pb-6.text-base.mt-5.md\\:py-10.md\\:my-0.font-bold.md\\:text-2xl.justify-center.items-center span.pl-2');
  326. const filename = filenameDiv ? filenameDiv.textContent.trim() : 'data_export';
  327.  
  328. // Initialize an array to hold all subjects with their respective rows
  329. const allSubjectsData = [];
  330.  
  331. // Select all subject name elements
  332. const subjectNameElements = document.querySelectorAll('.py-3.md\\:py-5.relative.rounded-md.text-\\[12px\\].md\\:text-2xl.font-bold.text-center.border.border-slate-500.text-gray-300');
  333.  
  334. // Iterate over each subject name element
  335. subjectNameElements.forEach(subjectElement => {
  336. // Extract the subject name and remove "Syllabus"
  337. const subjectName = subjectElement.textContent.trim().replace(/^Syllabus\s*/, '');
  338.  
  339. // Select all topics under this subject
  340. const topics = subjectElement.parentElement.querySelectorAll('.text-cyan-500 h1');
  341. const topicLinks = subjectElement.parentElement.querySelectorAll('.cursor-pointer.py-3[href]');
  342.  
  343. topics.forEach((topicElement, index) => {
  344. const topicName = topicElement.textContent.trim();
  345. const topicLink = topicLinks[index]?.href || '';
  346. const topicCode = topicLink ? (topicLink.match(/([a-zA-Z0-9_-]{33})(?=\?)/)?.[1] || '') : '';
  347.  
  348. // Add topic data to the allSubjectsData array with subject name included
  349. allSubjectsData.push({
  350. subject: subjectName,
  351. topic: topicName,
  352. topicLink: topicLink,
  353. topicCode: topicCode,
  354. });
  355. });
  356. });
  357.  
  358. // Function to convert JSON to CSV format
  359. function convertToCSV(data) {
  360. const headers = ['Subject', 'Topic Name', 'Topic Link', 'Topic Code', 'Note Link'];
  361. const rows = data.map(row => [
  362. row.subject,
  363. row.topic,
  364. row.topicLink,
  365. row.topicCode,
  366. `=HYPERLINK("https://drive.google.com/file/d/${row.topicCode}/edit", "${row.topic}")`.replace(/"/g, '""') // Escape double quotes
  367. ]);
  368.  
  369. return [headers.join(','), ...rows.map(row => row.map(cell => `"${cell}"`).join(','))].join('\n');
  370. }
  371.  
  372. // Convert the data to CSV format
  373. const csvData = convertToCSV(allSubjectsData);
  374.  
  375. // Create a Blob from the CSV data and trigger the download
  376. const blob = new Blob([csvData], { type: 'text/csv' });
  377. const url = URL.createObjectURL(blob);
  378. const a = document.createElement('a');
  379. a.href = url;
  380. a.download = `${filename}.csv`;
  381. a.style.display = 'none';
  382. document.body.appendChild(a);
  383. a.click();
  384. document.body.removeChild(a);
  385. URL.revokeObjectURL(url); // Clean up the URL object
  386. }
  387.  
  388. // Run the script only on the specified page
  389. if (window.location.href.startsWith('https://www.kiitconnect.com/')) {
  390. createSettingsMenu();
  391. }
  392. }
  393.  
  394. // Check and execute functionality based on filename div content
  395. function checkAndExecute(filenameDiv) {
  396. const textContent = filenameDiv.textContent.trim().toUpperCase();
  397. console.log('Filename text detected:', textContent);
  398.  
  399. if (textContent.includes('PYQS')) {
  400. executePYQsCode();
  401. } else if (textContent.includes('NOTES')) {
  402. executeNOTESCode();
  403. } else {
  404. console.warn('Unrecognized filename text:', textContent);
  405. }
  406. }
  407.  
  408. // Observe changes in the DOM and recheck
  409. function observeChanges(filenameDiv) {
  410. const observer = new MutationObserver(() => {
  411. console.log('Filename text content changed, rechecking...');
  412. checkAndExecute(filenameDiv);
  413. });
  414.  
  415. observer.observe(filenameDiv, {
  416. characterData: true,
  417. subtree: true,
  418. childList: true
  419. });
  420.  
  421. console.log('MutationObserver is now watching for changes.');
  422. }
  423.  
  424. // Initialize script
  425. window.addEventListener('load', () => {
  426. ensureFilenameDivLoaded((filenameDiv) => {
  427. checkAndExecute(filenameDiv);
  428. observeChanges(filenameDiv);
  429. });
  430. });
  431. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址