Work Search Record CSV Upload

Adds a CSV Upload Button

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         Work Search Record CSV Upload
// @namespace    https://brightentompkins.com
// @version      0.1
// @description  Adds a CSV Upload Button
// @author       vantaboard <brightenqtompkins@gmail.com>
// @match        https://uio.edd.ca.gov/UIO/Pages/ExternalUser/Certification/FormCCA4581RegularDUAWorkSearchRecord.aspx
// @icon         https://www.google.com/s2/favicons?sz=64&domain=ca.gov
// @license      MIT
// @grant        none
// ==/UserScript==

function CSVToArray(strData, strDelimiter = ',') {
    const objPattern = new RegExp(
        `(\\${strDelimiter}|\\r?\\n|\\r|^)(?:"([^"]*(?:""[^"]*)*)"|([^"\\${strDelimiter}\\r\\n]*))`,
        'gi'
    );

    const arrData = [[]];
    let arrMatches = null;

    while ((arrMatches = objPattern.exec(strData))) {
        const strMatchedDelimiter = arrMatches[1];
        if (
            strMatchedDelimiter.length &&
            strMatchedDelimiter !== strDelimiter
        ) {
            arrData.push([]);
        }

        let strMatchedValue;
        if (arrMatches[2]) {
            strMatchedValue = arrMatches[2].replace(new RegExp('""', 'g'), '"');
        } else {
            strMatchedValue = arrMatches[3];
        }

        arrData[arrData.length - 1].push(strMatchedValue);
    }

    arrData.pop();

    return arrData;
}

function getLabel(input) {
    let element = input;
    while (element) {
        if (element.tagName === 'LABEL') {
            return element.innerText;
        }

        if (element.querySelector('label')) {
            return [...element.querySelectorAll('label')].slice(-1)[0]
                .innerText;
        }

        element = element.parentElement;
    }
}

function fill(input, value) {
    console.log(`Setting ${getLabel(input).trim()} to ${value}`);
    input.value = value;
    input.dispatchEvent(new Event('change'));
}

function flatArray(...args) {
    const arr = [];

    args.forEach((arg) => {
        if (!arg) {
            return;
        }

        Array.isArray(arg) ? arr.push(...arg) : arr.push(arg);
    });

    return arr;
}

function wrapInputId(
    token,
    suffix,
    prefix = [
        'contentMain',
        'contentMain',
        'ucRegularDUA4581WorkSearchRecordV2',
        'frmFormWorkSearchInformation',
    ],
    postToken = 'ctl00',
    delimiter = '_'
) {
    return `#${flatArray(prefix, token, postToken, suffix).join(delimiter)}`;
}

const inputs = [
    {
        regex: new RegExp(/date.+contact/i),
        formatter: (value) =>
            new Intl.DateTimeFormat('en-US', {
                year: 'numeric',
                month: '2-digit',
                day: '2-digit',
            }).format(new Date(value)),
        suffix: 'txtDatePicker',
        token: 'prtDateOfContact',
    },
    {
        regex: new RegExp(/type.+work|work.+type/i),
        suffix: 'txtValue',
        token: 'prtTypeOfWork',
    },
    {
        regex: new RegExp(/employer.+agency|agency.+employer/i),
        suffix: 'txtValue',
        token: 'prtEmployerAgencyName',
    },
    {
        regex: new RegExp(/contact.+type|type.+contact/i),
        formatter: (value) =>
            ({
                Mail: 6656,
                Fax: 6657,
                Email: 6658,
                'In-Person': 6659,
                Online: 6660,
                Phone: 6661,
            }[String(value).trim()]),
        suffix: 'ddlValue',
        token: 'prtContactType',
    },
    {
        regex: new RegExp(/outcome.+contact|contact.+outcome/i),
        formatter: (value) =>
            ({
                Applied: 6662,
                'No Decision': 6663,
                Hired: 6664,
                'Not Hiring': 6665,
                Pending: 6666,
                Interviewed: 6667,
                'Interview Date Set': 6668,
                'No response from employer': 6669,
            }[String(value).trim()]),
        suffix: 'ddlValue',
        token: 'prtOutcomeWorkInquiry',
    },
    {
        regex: new RegExp(/person.+contacted|contacted.+person/i),
        suffix: 'txtValue',
        token: 'prtWorkSearchPersonContacted',
    },
    {
        regex: new RegExp(/email|website|url/i),
        suffix: 'txtValue',
        token: 'prtWebSiteURLEmailContact',
    },
    {
        regex: new RegExp(/phone|fax/i),
        suffix: 'txtValue',
        token: 'prtPhoneFaxNumber',
    },
    {
        regex: new RegExp(/address.+1|1.+address/i),
        suffix: 'txtValue',
        token: 'prtAddress1',
    },
    {
        regex: new RegExp(/address.+2|2.+address/i),
        suffix: 'txtValue',
        token: 'prtAddress2',
    },
    {
        regex: new RegExp(/city/i),
        suffix: 'txtValue',
        token: 'prtCity',
    },
    {
        regex: new RegExp(/state|province/i),
        suffix: 'txtValue',
        token: 'prtOtherState',
    },
    {
        regex: new RegExp(/postal|zip/i),
        suffix: 'txtValue',
        token: 'prtPostalCode',
    },
    {
        regex: new RegExp(/country/i),
        formatter: (value) =>
            ({
                Afghanistan: 329,
                Akrotiri: 377,
                Albania: 405,
                Algeria: 406,
                Andorra: 407,
                Angola: 450,
                Anguilla: 378,
                Antarctica: 341,
                'Antigua and Barbuda': 283,
                Argentina: 360,
                Armenia: 408,
                Aruba: 478,
                'Ashmore and Cartier Islands': 268,
                Australia: 361,
                Austria: 409,
                Azerbaijan: 342,
                'Bahamas, The': 319,
                Bahrain: 410,
                Bangladesh: 343,
                Barbados: 379,
                'Bassas da India': 297,
                Belarus: 411,
                Belgium: 412,
                Belize: 451,
                Benin: 479,
                Bermuda: 413,
                Bhutan: 452,
                Bolivia: 414,
                'Bosnia and Herzegovina': 275,
                Botswana: 380,
                'Bouvet Island': 311,
                Brazil: 453,
                'British Indian Ocean Territory': 267,
                'British Virgin Islands': 276,
                Brunei: 454,
                Bulgaria: 381,
                'Burkina Faso': 320,
                Burma: 480,
                Burundi: 415,
                Cambodia: 382,
                Cameroon: 383,
                Canada: 448,
                'Cape Verde': 344,
                'Cayman Islands': 303,
                'Central African Republic': 270,
                Chad: 505,
                Chile: 481,
                China: 482,
                'Christmas Island': 291,
                'Clipperton Island': 287,
                'Cocos (Keeling) Islands': 273,
                Colombia: 384,
                Comoros: 416,
                'Congo, Democratic Republic of the': 262,
                'Congo, Republic of the': 277,
                'Cook Islands': 321,
                'Coral Sea Islands': 288,
                'Costa Rica': 345,
                // eslint-disable-next-line quotes
                "Cote d'Ivoire": 312,
                Croatia: 417,
                Cuba: 506,
                Cyprus: 455,
                'Czech Republic': 304,
                Denmark: 418,
                Dhekelia: 385,
                Djibouti: 386,
                Dominica: 387,
                'Dominican Republic': 286,
                Ecuador: 419,
                Egypt: 483,
                'El Salvador': 330,
                'Equatorial Guinea': 289,
                Eritrea: 420,
                Estonia: 421,
                Ethiopia: 388,
                'Europa Island': 313,
                'Falkland Islands (Islas Malvinas)': 263,
                'Faroe Islands': 314,
                Fiji: 507,
                Finland: 422,
                France: 456,
                'French Guiana': 315,
                'French Polynesia': 292,
                'French Southern and Antarctic Lands': 261,
                Gabon: 484,
                'Gambia, The': 331,
                'Gaza Strip': 346,
                Georgia: 423,
                Germany: 424,
                Ghana: 485,
                Gibraltar: 362,
                'Glorioso Islands': 293,
                Greece: 457,
                Greenland: 363,
                Grenada: 425,
                Guadeloupe: 347,
                Guam: 508,
                Guatemala: 364,
                Guernsey: 389,
                Guinea: 458,
                'Guinea-Bissau': 316,
                Guyana: 459,
                Haiti: 486,
                'Heard Island and McDonald Islands': 264,
                'Holy See (Vatican City)': 274,
                Honduras: 390,
                'Hong Kong': 365,
                Hungary: 426,
                Iceland: 427,
                India: 487,
                Indonesia: 366,
                Iran: 509,
                Iraq: 510,
                Ireland: 428,
                'Isle of Man': 332,
                Israel: 460,
                Italy: 488,
                Jamaica: 429,
                'Jan Mayen': 367,
                Japan: 489,
                Jersey: 461,
                Jordan: 462,
                'Juan de Nova Island': 284,
                Kazakhstan: 348,
                Kenya: 490,
                Kiribati: 391,
                'Korea, North': 322,
                'Korea, South': 323,
                Kuwait: 463,
                Kyrgyzstan: 349,
                Laos: 511,
                Latvia: 464,
                Lebanon: 430,
                Lesotho: 431,
                Liberia: 432,
                Libya: 491,
                Liechtenstein: 317,
                Lithuania: 368,
                Luxembourg: 350,
                Macau: 492,
                Macedonia: 369,
                Madagascar: 351,
                Malawi: 465,
                Malaysia: 392,
                Maldives: 393,
                Mali: 512,
                Malta: 493,
                'Marshall Islands': 294,
                Martinique: 352,
                Mauritania: 353,
                Mauritius: 370,
                Mayotte: 433,
                Mexico: 449,
                'Micronesia, Federated States of': 266,
                Moldova: 434,
                Monaco: 466,
                Mongolia: 394,
                Montserrat: 354,
                Morocco: 435,
                Mozambique: 355,
                Namibia: 436,
                Nauru: 494,
                'Navassa Island': 305,
                Nepal: 495,
                Netherlands: 333,
                'Netherlands Antilles': 281,
                'New Caledonia': 318,
                'New Zealand': 334,
                Nicaragua: 371,
                Niger: 496,
                Nigeria: 437,
                Niue: 513,
                'Norfolk Island': 306,
                'Northern Mariana Islands': 271,
                Norway: 467,
                Oman: 514,
                Pakistan: 395,
                Palau: 497,
                Panama: 468,
                'Papua New Guinea': 295,
                'Paracel Islands': 298,
                Paraguay: 396,
                Peru: 515,
                Philippines: 335,
                'Pitcairn Islands': 296,
                Poland: 469,
                Portugal: 397,
                'Puerto Rico': 336,
                Qatar: 498,
                Reunion: 438,
                Romania: 439,
                Russia: 470,
                Rwanda: 471,
                'S Georgia and S Sandwich Islands': 260,
                'Saint Helena': 324,
                'Saint Kitts and Nevis': 278,
                'Saint Lucia': 337,
                'Saint Pierre and Miquelon': 269,
                'Saint Vincent and the Grenadines': 265,
                Samoa: 499,
                'San Marino': 356,
                'Sao Tome and Principe': 279,
                'Saudi Arabia': 325,
                Senegal: 440,
                'Serbia and Montenegro': 280,
                Seychelles: 357,
                'Sierra Leone': 326,
                Singapore: 372,
                Slovakia: 398,
                Slovenia: 399,
                'Solomon Islands': 299,
                Somalia: 441,
                'South Africa': 327,
                Spain: 500,
                'Spratly Islands': 300,
                'Sri Lanka': 373,
                Sudan: 501,
                Suriname: 400,
                Svalbard: 401,
                Swaziland: 374,
                Sweden: 472,
                Switzerland: 338,
                Syria: 502,
                Taiwan: 473,
                Tajikistan: 358,
                Tanzania: 402,
                Thailand: 403,
                'Timor-Leste': 339,
                Togo: 516,
                Tokelau: 442,
                Tonga: 503,
                'Trinidad and Tobago': 285,
                'Tromelin Island': 301,
                Tunisia: 443,
                Turkey: 474,
                Turkmenistan: 328,
                'Turks and Caicos Islands': 272,
                Tuvalu: 475,
                Uganda: 476,
                Ukraine: 444,
                'United Arab Emirates': 282,
                'United Kingdom': 307,
                'United States': 310,
                Uruguay: 445,
                Uzbekistan: 359,
                Vanuatu: 446,
                Venezuela: 375,
                Vietnam: 447,
                'Virgin Islands': 308,
                'Wake Island': 340,
                'Wallis and Futuna': 290,
                'West Bank': 376,
                'Western Sahara': 309,
                Yemen: 504,
                Zambia: 477,
                Zimbabwe: 404,
            }[String(value).trim()]),
        suffix: 'ddlValue',
        token: 'prtCountry',
    },
];

function inputByHeaders(headersArray) {
    const headers = headersArray.map((header) => {
        const input = inputs.find(({regex}) => regex.test(header));

        if (!input) {
            return null;
        }

        return input;
    }, []);

    return headers;
}

const wrapWeekId = (token) =>
    wrapInputId(
        token,
        '',
        [
            'contentMain',
            'contentMain',
            'ucFormCCA4581RegularDUACertificationWeeks',
        ],
        ''
    );

function load(data) {
    const {headers, rows} = data;
    headers.forEach((header) => {
        if (!header) {
            return;
        }

        const {token, suffix} = header;

        const element = document.querySelector(wrapInputId(token, suffix));

        if (!element) {
            throw new Error(`Could not find element for ${token}`);
        }

        header.element = element;
    });

    const row = rows[0];
    headers.forEach((header, index) => {
        if (!header) {
            return;
        }

        const {element, formatter} = header;

        try {
            const value = formatter ? formatter(row[index]) : row[index];

            fill(element, value);
        } catch (error) {
            if (error instanceof Error && error.name === 'RangeError') {
                console.warn(
                    `Error filling ${getLabel(element)
                        .toString()
                        .trim()} with ${row[index]}`
                );
            }
            throw error;
        }
    });

    const additionalButton = document.querySelector(
        wrapInputId('prtISAdditionalWorkSerach', ['rblValue', '0'])
    );

    const nextButton = document.querySelector(
        wrapInputId('btnNext', '', ['contentMain', 'contentMain'], '')
    );

    additionalButton.click();

    nextButton.click();
}

const weekNumber = document.querySelector(wrapWeekId('lblWeekNumber'));
const weekEnd = document.querySelector(wrapWeekId('lblWeekEndDate'));
const weekHeader = weekEnd.parentElement;
weekHeader.style.display = 'flex';
weekHeader.style.alignItems = 'center';
weekNumber.style.padding = '0 0.5rem';

const csvUploadInput = document.createElement('input');
csvUploadInput.style.display = 'none';

const csvUploadIcon = document.createElement('span');
csvUploadIcon.classList.add('ca-gov-icon-file-csv');

const csvUploadText = document.createElement('span');
csvUploadText.innerText = 'Upload CSV';
csvUploadText.style.fontSize = '1rem';
csvUploadText.style.userSelect = 'none';

csvUploadInput.type = 'file';
csvUploadInput.accept = '.csv';
csvUploadInput.addEventListener('change', (event) => {
    const file = event.target.files[0];

    const reader = new FileReader();

    reader.onload = (event) => {
        const csv = event.target.result;
        const csvArray = CSVToArray(csv);

        const [headers, ...rows] = csvArray;

        if (!rows.length) {
            return;
        }

        const inputHeaders = inputByHeaders(headers);

        window.localStorage.setItem('csvArray', JSON.stringify(csvArray));

        const data = {
            headers: inputHeaders,
            rows,
        };

        load(data);
    };

    reader.readAsText(file);
});

const csvUploadLabel = document.createElement('label');
csvUploadLabel.classList.add('nav-item');
csvUploadLabel.style.marginLeft = '5px';
csvUploadLabel.style.display = 'flex';
csvUploadLabel.style.alignItems = 'center';
csvUploadLabel.appendChild(csvUploadInput);
csvUploadLabel.appendChild(csvUploadIcon);
csvUploadLabel.appendChild(csvUploadText);

weekHeader.appendChild(csvUploadLabel);

const csvArray = JSON.parse(window.localStorage.getItem('csvArray'));

if (csvArray) {
    const [headers, ...rows] = csvArray;
    rows.shift();

    if (!rows.length) {
        window.localStorage.removeItem('csvArray');
    }

    if (rows.length) {
        window.localStorage.setItem(
            'csvArray',
            JSON.stringify([headers, ...rows])
        );

        const inputHeaders = inputByHeaders(headers);

        const data = {
            headers: inputHeaders,
            rows,
        };

        load(data);
    }
}