Mturk Hourly

Record time spent working on HITs. Forked from [MTurk Worker] Dashboard Enhancer v2.0.3 by @Kadauchi.

目前為 2017-12-23 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Mturk Hourly
// @namespace    https://gf.qytechs.cn/users/11205
// @version      2.1.1
// @description  Record time spent working on HITs.  Forked from [MTurk Worker] Dashboard Enhancer v2.0.3 by @Kadauchi.
// @author       Kerek
// @include      https://worker.mturk.com/dashboard*
// @include https://worker.mturk.com/projects/*/tasks/*assignment_id=*
// @include https://worker.mturk.com/status_details/*
//@grant GM_setClipboard
// ==/UserScript==

const dashboard = new Object();

if (location.href.indexOf(`https://worker.mturk.com/dashboard`) !== -1) {

(function () {

    Object.assign(String.prototype, {
        toNumberDE () {
            return Number(this.replace(/[^0-9.]/g, ``));
        }
    });

    Object.assign(Number.prototype, {
        toNumberDE () {
            return this;
        }
    });

    const rows = document.getElementById(`dashboard-hits-overview`).getElementsByClassName(`row`);

    dashboard.hits_overview = {
        approved: rows[0].getElementsByClassName(`text-xs-right`)[0].textContent.toNumberDE(),
        pending: rows[2].getElementsByClassName(`text-xs-right`)[0].textContent.toNumberDE(),
        rejected: rows[3].getElementsByClassName(`text-xs-right`)[0].textContent.toNumberDE(),
    };
    dashboard.daily_hit_statistics_overview = {
        total: {
            submitted: 0,
            approved: 0,
            rejected: 0,
            pending: 0,
            earnings: 0,
        }
    };
    dashboard.earnings_to_date = {
        bonuses: document.getElementById(`dashboard-earnings-to-date`).getElementsByClassName(`text-xs-right`)[2].textContent.toNumberDE()
    };

    for (const row of document.getElementsByClassName(`daily_hit_statuses`)) {
        const col = row.children;
        const date = col[0].children[0].href.match(/[0-9]{4}-[0-9]{2}-[0-9]{2}/)[0];

        dashboard.daily_hit_statistics_overview[date] = new Object();

        [`submitted`, `approved`, `rejected`, `pending`, `earnings`].forEach((currentValue, index) => {
            const value = col[index + 1].textContent.toNumberDE();

            dashboard.daily_hit_statistics_overview[date][currentValue] = value;
            dashboard.daily_hit_statistics_overview.total[currentValue] += value;
        });
    }
})();

    (function allApprovedRate() {
    const overview = dashboard.hits_overview;

    const row = document.createElement(`div`);
    row.className = `row m-b-sm`;

    const col1 = document.createElement(`div`);
    col1.className = `col-xs-7`;
    row.appendChild(col1);

    const strong = document.createElement(`strong`);
    strong.textContent = `All Approved Rate`;
    col1.appendChild(strong);

    const col2 = document.createElement(`div`);
    col2.className = `col-xs-5 text-xs-right`;
    col2.textContent = `${((overview.approved + overview.pending) / (overview.approved + overview.pending + overview.rejected) * 100).toFixed(4)}%`;
    row.appendChild(col2);

    const additional = document.getElementById(`HitsOverviewAdditionalInfo`);
    additional.parentNode.insertBefore(row, additional);
})();

(function allRejectedRate() {
    const overview = dashboard.hits_overview;

    const row = document.createElement(`div`);
    row.className = `row m-b-sm`;

    const col1 = document.createElement(`div`);
    col1.className = `col-xs-7`;
    row.appendChild(col1);

    const strong = document.createElement(`strong`);
    strong.textContent = `All Rejected Rate`;
    col1.appendChild(strong);

    const col2 = document.createElement(`div`);
    col2.textContent = `${(overview.approved / (overview.approved + overview.rejected + overview.pending) * 100).toFixed(4)}%`;
    col2.className = `col-xs-5 text-xs-right`;
    row.appendChild(col2);

    const additional = document.getElementById(`HitsOverviewAdditionalInfo`);
    additional.parentNode.insertBefore(row, additional);
})();

(function fourDigitPercents() {
    const overview = dashboard.hits_overview;

    for (const row of document.getElementById(`dashboard-hits-overview`).getElementsByClassName(`row`)) {
        if (row.textContent.indexOf(`Approval Rate`) !== -1) {
            row.getElementsByClassName(`text-xs-right`)[0].textContent = `${(overview.approved / (overview.approved + overview.rejected) * 100).toFixed(4)}%`;
        }
        if (row.textContent.indexOf(`Rejection Rate`) !== -1) {
            row.getElementsByClassName(`text-xs-right`)[0].textContent = `${(overview.rejected / (overview.approved + overview.rejected) * 100).toFixed(4)}%`;
        }
    }
})();

(function rejectionsBelow99() {
    const overview = dashboard.hits_overview;

    const row = document.createElement(`div`);
    row.className = `row m-b-sm`;

    const col1 = document.createElement(`div`);
    col1.className = `col-xs-7`;
    row.appendChild(col1);

    const strong = document.createElement(`strong`);
    strong.textContent = `Rejections ≤ 99%`;
    col1.appendChild(strong);

    const col2 = document.createElement(`div`);
    col2.textContent = Math.round((overview.rejected - (0.01 * (overview.approved + overview.rejected + overview.pending))) / -0.99).toLocaleString();
    col2.className = `col-xs-5 text-xs-right`;
    row.appendChild(col2);

    const additional = document.getElementById(`HitsOverviewAdditionalInfo`);
    additional.insertBefore(row, additional.lastChild);
})();

(function rejectionsBelow95() {
    const overview = dashboard.hits_overview;

    const row = document.createElement(`div`);
    row.className = `row m-b-sm`;

    const col1 = document.createElement(`div`);
    col1.className = `col-xs-7`;
    row.appendChild(col1);

    const strong = document.createElement(`strong`);
    strong.textContent = `Rejections ≤ 95%`;
    col1.appendChild(strong);

    const col2 = document.createElement(`div`);
    col2.textContent = Math.round((overview.rejected - (0.05 * (overview.approved + overview.rejected + overview.pending))) / -0.95).toLocaleString();
    col2.className = `col-xs-5 text-xs-right`;
    row.appendChild(col2);

    const additional = document.getElementById(`HitsOverviewAdditionalInfo`);
    additional.insertBefore(row, additional.lastChild);
})();

(function totalLast45Days() {
    const table = document.querySelector(`.mturk-table.hits-statuses`);

    const row = table.insertRow();
    row.className = `daily_hit_statuses`;

    const date = row.insertCell(0);
    date.textContent = `Total`;
    date.className = `hidden-xs-down col-sm-2 col-md-2`;

    const submitted = row.insertCell(1);
    submitted.textContent = dashboard.daily_hit_statistics_overview.total.submitted;
    submitted.className = `text-xs-right col-xs-3 col-sm-2 col-md-2`;

    const approved = row.insertCell(2);
    approved.textContent = dashboard.daily_hit_statistics_overview.total.approved;
    approved.className = `text-xs-right col-xs-3 col-sm-2 col-md-2`;

    const rejected = row.insertCell(3);
    rejected.textContent = dashboard.daily_hit_statistics_overview.total.rejected;
    rejected.className = `text-xs-right col-xs-3 col-sm-2 col-md-2`;

    const pending = row.insertCell(4);
    pending.textContent = dashboard.daily_hit_statistics_overview.total.pending;
    pending.className = `text-xs-right col-xs-3 col-sm-2 col-md-2`;

    const earnings = row.insertCell(5);
    earnings.textContent = `$${dashboard.daily_hit_statistics_overview.total.earnings.toLocaleString(`en-US`, { minimumFractionDigits: 2 })}`;
    earnings.className = `text-xs-right col-xs-3 col-sm-2 col-md-2`;
})();

(function hitStatusChanges() {
    const oldDashboard = localStorage.dashboard ? JSON.parse(localStorage.dashboard) : null;
    localStorage.dashboard = JSON.stringify(dashboard);

    for (const row of document.getElementsByClassName(`daily_hit_statuses`)) {
        const col = row.children;
        const date = col[0].children[0] ? col[0].children[0].href.match(/[0-9]{4}-[0-9]{2}-[0-9]{2}/)[0] : `total`;

        [`submitted`, `approved`, `rejected`, `pending`, `earnings`].forEach((currentValue, index) => {
            const value = col[index + 1].textContent.toNumberDE();
            const oldValue = oldDashboard ? oldDashboard.daily_hit_statistics_overview[date][currentValue] : 0;

            if (value !== oldValue) {
                const change = value - oldValue;
                const changeString = change.toFixed(2).toString().replace(`.00`, ``);

                if (Math.round(change * 100) !== 0) {
                    const span = document.createElement(`span`);
                    span.textContent = change > 0 ? `+${changeString}` : changeString;
                    span.style.float = `left`;
                    span.style.fontSize = `70%`;
                    col[index + 1].appendChild(span);
                }
            }
        });
    }
})();

(function todaysActivity() {
    const container = document.createElement(`div`);
    container.className = `row m-b-xl`;

    const col = document.createElement(`div`);
    col.className = `col-xs-12`;
    container.appendChild(col);

    const h2 = document.createElement(`h2`);
    h2.className = `m-b-md`;
    h2.textContent = `Today's Activity`;
    col.appendChild(h2);

    const row = document.createElement(`div`);
    row.className = `row`;
    col.appendChild(row);

    const col2 = document.createElement(`div`);
    col2.className = `col-xs-12`;
    row.appendChild(col2);

    const border = document.createElement(`div`);
    border.className = `border-gray-lightest p-a-sm`;
    col2.appendChild(border);

    const earningsRow = document.createElement(`div`);
    earningsRow.className = `row m-b-sm`;
    border.appendChild(earningsRow);

    const earningsText = document.createElement(`div`);
    earningsText.className = `col-xs-7 col-sm-6 col-lg-7`;
    earningsRow.appendChild(earningsText);

    const earningsStrong = document.createElement(`strong`);
    earningsStrong.textContent = `Projected Earnings`;
    earningsText.appendChild(earningsStrong);

    const earningsValue = document.createElement(`div`);
    earningsValue.className = `col-xs-5 col-sm-6 col-lg-5 text-xs-right`;
    earningsValue.textContent = localStorage.todaysearnings || `$0.00`;
    earningsRow.appendChild(earningsValue);

    const bonusesRow = document.createElement(`div`);
    bonusesRow.className = `row m-b-sm`;
    border.appendChild(bonusesRow);

    const bonusesText = document.createElement(`div`);
    bonusesText.className = `col-xs-7 col-sm-6 col-lg-7`;
    bonusesRow.appendChild(bonusesText);

    const bonusesStrong = document.createElement(`strong`);
    bonusesStrong.textContent = `Bonuses`;
    bonusesText.appendChild(bonusesStrong);

    const bonusesValue = document.createElement(`div`);
    bonusesValue.className = `col-xs-5 col-sm-6 col-lg-5 text-xs-right`;
    bonusesValue.textContent = localStorage.todaysbonuses || `$0.00`;
    bonusesRow.appendChild(bonusesValue);

    const hourlyRow = document.createElement(`div`);
    hourlyRow.className = `row m-b-sm`;
    border.appendChild(hourlyRow);

    const hourlyText = document.createElement(`div`);
    hourlyText.className = `col-xs-6 col-sm-6 col-lg-6`;
    hourlyRow.appendChild(hourlyText);

    const hourlyStrong = document.createElement(`strong`);
    hourlyStrong.textContent = `Total Hourly`;
    hourlyText.appendChild(hourlyStrong);

    const hourlyTime = document.createElement(`div`);
    hourlyTime.className = `col-xs-3 col-sm-3 col-lg-3 text-xs-right`;
    hourlyTime.textContent = `00:00:00`;
    hourlyRow.appendChild(hourlyTime);

    const hourlyValue = document.createElement(`div`);
    hourlyValue.className = `col-xs-3 col-sm-3 col-lg-3 text-xs-right`;
    hourlyValue.textContent = `$0.00`;
    hourlyRow.appendChild(hourlyValue);


    const collapse = document.createElement(`div`);
    collapse.id = `TodaysActivityAdditionalInfo`;
    collapse.className = `collapse`;
    border.appendChild(collapse);

    const hr = document.createElement(`hr`);
    hr.className = `m-b-sm m-t-0`;
    collapse.appendChild(hr);

    const hr2 = document.createElement(`hr`);
    hr2.className = `m-b-sm m-t-0`;
    border.appendChild(hr2);

    const control = document.createElement(`a`);
    control.className = `collapse-more-less`;
    control.href = `#TodaysActivityAdditionalInfo`;
    control.setAttribute(`aria-controls`, `TodaysActivityAdditionalInfo`);
    control.setAttribute(`aria-expanded`, `false`);
    control.setAttribute(`data-toggle`, `collapse`);
    border.appendChild(control);

    const more = document.createElement(`span`);
    more.className = `more`;
    control.appendChild(more);

    const plus = document.createElement(`i`);
    plus.className = `fa fa-plus-circle`;
    more.appendChild(plus);

    const moreText = document.createTextNode(`\nMore\n`);
    more.appendChild(moreText);

    const less = document.createElement(`span`);
    less.className = `less`;
    control.appendChild(less);

    const minus = document.createElement(`i`);
    minus.className = `fa fa-minus-circle`;
    less.appendChild(minus);

    const lessText = document.createTextNode(`\nLess\n`);
    less.appendChild(lessText);

    const side = document.querySelector(`.col-md-push-8`);
    side.insertBefore(container, side.firstChild);

    const today = document.querySelector(`a[href^="/status_details/"]`);

    if (today.textContent === `Today`) {
        const date = today.href.match(/[0-9]{4}-[0-9]{2}-[0-9]{2}/)[0];

        if (date === localStorage.WMTD_date) {
            if (!localStorage.WMTD_bonusStart) {
                localStorage.WMTD_bonusStart = dashboard.earnings_to_date.bonuses;
            }
        }
        else {
            if (dashboard.daily_hit_statistics_overview[date].approved === 0) {
                localStorage.WMTD_bonusStart = dashboard.earnings_to_date.bonuses - dashboard.daily_hit_statistics_overview[date].earnings;
            }
            else {
                localStorage.WMTD_bonusStart = dashboard.earnings_to_date.bonuses;
            }
        }

        bonusesValue.textContent = `$${(dashboard.earnings_to_date.bonuses - localStorage.WMTD_bonusStart).toLocaleString(`en-US`, { minimumFractionDigits: 2 })}`;

        let hitLog = date === localStorage.WMTD_date ? localStorage.WMTD_hitLog ? JSON.parse(localStorage.WMTD_hitLog) : {} : {};

        async function get(page, rescan) {
            try {
                page = Number.isInteger(page) ? page : 1;

                earningsValue.textContent = `Calculating Page ${page}`;

                const fetchURL = new URL(`https://worker.mturk.com/status_details/${date}`);
                fetchURL.searchParams.append(`page_number`, page);
                fetchURL.searchParams.append(`format`, `json`);

                const response = await fetch(fetchURL, {
                    credentials: `include`
                });

                if (response.status === 429) {
                    return setTimeout(get, 2000, url, rescan);
                }

                const json = await response.json();

                for (const hit of json.results) {
                    hitLog[hit.hit_id] = hit;
                }

                const logLength = Object.keys(hitLog).length;
                const expectedLength = page.toNumberDE() * 20 - 20 + json.num_results;

                if (!rescan && logLength !== expectedLength) {
                    return get(1, true);
                }
                else {
                    localStorage.WMTD_hitLog = JSON.stringify(hitLog);
                }

                localStorage.WMTD_lastPage = page;

                if (json.results.length === 20) {
                    return get(++ page, rescan);
                }
                else if (logLength !== json.total_num_results) {
                    hitLog = new Object();
                    return get(1, true);
                }
                else {
                    let projectedEarnings = 0;
                    const reqLog = {};
                        const hourlyLog = {};

                    for (const key in hitLog) {
                        const hit = hitLog[key];

                        if (hit.status !== `Rejected`) {
                            projectedEarnings += hit.reward.amount_in_dollars;
                        }
                        if (!reqLog[hit.requester_id]){
                            reqLog[hit.requester_id] = {
                                requester_id: hit.requester_id,
                                requester_name: hit.requester_name,
                                reward: hit.reward.amount_in_dollars,
                                submitted: 1,
                            };
                        }
                        else {
                            reqLog[hit.requester_id].submitted += 1;
                            reqLog[hit.requester_id].reward += hit.reward.amount_in_dollars;
                        }
                        if (!hourlyLog[hitLog[key].requester_id]){
                                var time_data = localStorage.getItem('time_data.' + hitLog[key].hit_id.split('&')[0]);

                                if (time_data !== null){

                                    var starts = time_data.split("$#$")[3].split('?');
                                    var last_start = starts[starts.length-1];
                                    var stops = time_data.split("$#$")[4].split('?');
                                    var last_stop = stops[stops.length-1];
                                    if (last_start.length && last_stop.length){
                                        hourlyLog[hitLog[key].requester_id] = {
                                            req : hitLog[key].requester_name,
                                            intervals :[[last_start,last_stop]],
                                            totalReward : parseFloat(time_data.split("$#$")[2]),
                                        };
                                    }
                                }
                            }
                            else{
                                var time_data = localStorage.getItem('time_data.' + hitLog[key].hit_id.split('&')[0]);
                                if (time_data !== null){
                                    var starts = time_data.split("$#$")[3].split('?');
                                    var last_start = starts[starts.length-1];
                                    var stops = time_data.split("$#$")[4].split('?');
                                    var last_stop = stops[stops.length-1];
                                    if (last_start.length && last_stop.length){
                                        hourlyLog[hitLog[key].requester_id].intervals.push([last_start,last_stop]);
                                    }
                                    hourlyLog[hitLog[key].requester_id].totalReward += parseFloat(time_data.split("$#$")[2]);
                                }
                            }


                    }
                    const sort = Object.keys(reqLog).sort((a, b) => reqLog[a].reward - reqLog[b].reward);

                    const fragment = document.createDocumentFragment();

                    var total_intervals = [];
                        var total_recorded_earnings = 0;
                        for (var j = sort.length-1; j > -1; j--){
                            var dkey = sort[j];
                            var d_req = reqLog[dkey].requester_name;
                            var d_submitted = reqLog[dkey].submitted;
                            var d_reward = Number(reqLog[dkey].reward).toFixed(2);
                            var d_hourly = "N/A";
                            if (d_req !== "Bonuses" && hourlyLog[reqLog[dkey].requester_id]){
                                var intervals = hourlyLog[reqLog[dkey].requester_id].intervals;
                                var d_intervals_sum = 0;
                                for (i=0;i<intervals.length;i++){
                                    d_intervals_sum += (intervals[i][1]-intervals[i][0]);
                                }
                                var d_intervals_avg = d_intervals_sum / intervals.length;
                                var d_intervals = mergeIntervals(intervals);
                                d_intervals = combineIntervals(intervals,Math.max(2*60*1000,Math.min(10*d_intervals_avg,10*60*1000)));
                                total_intervals = total_intervals.concat(d_intervals);
                                total_recorded_earnings += hourlyLog[reqLog[dkey].requester_id].totalReward;
                                var d_time = 0;
                                for (i=0;i<d_intervals.length; i++){
                                    var s = new Date(parseInt(d_intervals[i][0]));
                                    var f = new Date(parseInt(d_intervals[i][1]));
                                    //console.log(d_req, i, s.toLocaleTimeString(),f.toLocaleTimeString());
                                    d_time += (d_intervals[i][1] - d_intervals[i][0])/(1000*60*60);
                                }
                                d_hourly = "$" + (hourlyLog[reqLog[dkey].requester_id].totalReward / d_time).toFixed(2);
                            }
                            reqLog[dkey].hourly = d_hourly;
                        }
                        total_intervals = mergeIntervals(total_intervals);
                        var total_time = 0;
                        for (i=0;i<total_intervals.length; i++){
                            var s = new Date(parseInt(total_intervals[i][0]));
                            var f = new Date(parseInt(total_intervals[i][1]));
                            //console.log(i, s.toLocaleTimeString(),f.toLocaleTimeString());
                            total_time += (total_intervals[i][1] - total_intervals[i][0])/(1000*60*60);
                        }

                        var total_hourly = '$' + (total_recorded_earnings/total_time).toFixed(2);
                        var hour = Math.floor(total_time);
                        var min = Math.floor((total_time - hour)*60);
                        var sec = Math.floor((total_time - hour - min/60)*3600);
                        var time_display = (hour>0?pad(hour,2) + ":":"") + pad(min,2) + ":" + pad(sec,2);

                        hourlyValue.textContent = total_hourly;
                        hourlyTime.textContent = time_display;

                    for (let i = sort.length - 1; i > -1; i--) {
                        const key = sort[i];
                        const requester_name = reqLog[key].requester_name;
                        const reward = `$${reqLog[key].reward.toLocaleString(`en-US`, { minimumFractionDigits: 2 })}`;
                        const submitted = reqLog[key].submitted;
                                                    const hourly = reqLog[key].hourly;


                        const reqRow = document.createElement(`div`);
                        reqRow.className = `row m-b-sm`;
                        fragment.appendChild(reqRow);

                        const requester = document.createElement(`div`);
                        requester.className = `col-xs-4`;
                        reqRow.appendChild(requester);

                        const requesterStrong = document.createElement(`strong`);
                        requesterStrong.textContent = requester_name;
                        requester.appendChild(requesterStrong);

                        const submitValue = document.createElement(`div`);
                        submitValue.className = `col-xs-2 text-xs-right`;
                        submitValue.textContent = submitted;
                        reqRow.appendChild(submitValue);

                        const rewardValue = document.createElement(`div`);
                        rewardValue.className = `col-xs-3 text-xs-right`;
                        rewardValue.textContent = reward;
                        reqRow.appendChild(rewardValue);

                            const hourlyValue = document.createElement(`div`);
                            hourlyValue.className = `col-xs-3 text-xs-right`;
                            hourlyValue.textContent = hourly;
                            reqRow.appendChild(hourlyValue);
                    }

                    collapse.appendChild(fragment);

                    earningsValue.textContent = `$${projectedEarnings.toLocaleString(`en-US`, { minimumFractionDigits: 2 })}`;
                }
            }
            catch (error) {
                earningsValue.textContent = error;
            }
        }

        get(date === localStorage.WMTD_date ? localStorage.WMTD_lastPage ? localStorage.WMTD_lastPage.toNumberDE() : 1 : 1, false);
        localStorage.WMTD_date = date;
    }
    else {
        earningsValue.textContent = `N/A`;
        bonusesValue.textContent = `N/A`;
    }
})();
}
else if (location.href.indexOf('worker.mturk.com/status_details') > -1){
$('span.reward-column:eq(0)').before('<span class="hourly-column text-xs-right column-header p-x-sm" data-reactid=".2.$header.$2">Hourly</span>');
    $('li.table-row').each(function(){
        var hitId = $(this).find('a[href*="contact_requester"]:eq(0)').attr('href').split('hit_id=')[1].split('&')[0];
                        var feedback = {opened:"N/A",submitted:"N/A", time:"N/A", hourly:"N/A"};

                        var time_data = localStorage.getItem('time_data.' + hitId);
                        if (time_data){
                    feedback = create_feedback(time_data);

                }
$(this).find('.reward-column').before('<span class="p-x-sm column hourly-column text-xs-right"><span class="hourlyCopy" title="Opened: ' + feedback.opened +'&#013;Submitted: ' + feedback.submitted +'&#013;Time: ' + feedback.time +'&#013;Hourly: ' + feedback.hourly +'">' + feedback.hourly+ '</span></span>');


    });

    $('.hourlyCopy').click(function(evt){
        evt.preventDefault();
        //GM_setClipboard($(this).attr('title'));

        GM_setClipboard('Time:' + $(this).attr('title').split('Time:')[1]);
    });
}
else if (location.href.indexOf('worker.mturk.com') > -1){
    var hit_returned = false;
    if(typeof(Storage)!=="undefined")
    {
        $('button:contains("Return")').click(function(){
            hit_returned = true;
        });
        store_data('openWorker');
        window.addEventListener('beforeunload', function(){
            store_data('closeWorker');
        });
    }
}





function create_feedback(time_data)
{

    var last_start = time_data.split("$#$")[3].split('?');
    last_start = new Date(parseInt(last_start[last_start.length - 1]));
    var last_finish = time_data.split("$#$")[4].split('?');
    last_finish = new Date(parseInt(last_finish[last_finish.length - 1]));

    //console.log(time_data, last_start, last_finish, time_data.split("$#$")[3].split('?'), time_data.split("$#$")[4].split('?'));
    var reward = time_data.split("$#$")[2];
    var time_spent = last_finish - last_start;
    var h = Math.floor(time_spent/(1000*60*60));
    var m = Math.floor((time_spent - h*1000*60*60)/(1000*60));
    var s = Math.floor((time_spent - h*1000*60*60 - m*1000*60)/(1000));
    var f = {opened:last_start.toLocaleTimeString(), submitted:last_finish.toLocaleTimeString(), time: ((h > 0 ? (pad(h,2) + ":"):"") + pad(m,2) + ":" + pad(s,2)), hourly: '$' + (reward/((last_finish-last_start)/(60*60*1000))).toFixed(2)};
    console.log(f);
    return f;
 //   return "Opened: " + last_start.toLocaleTimeString() + "<br>Submitted: " + last_finish.toLocaleTimeString()+ "<br>Time: " + (h > 0 ? (pad(h,2) + ":"):"") + pad(m,2) + ":" + pad(s,2) + "<br>Hourly: $" + (reward/((last_finish-last_start)/(60*60*1000))).toFixed(2) ;
}

function store_data(action_type)
{
        if (!hit_returned){
            var now_in_milliseconds = new Date().getTime();
            var hitReview_hitId = window.location.href.split('tasks/')[1].split('?')[0];
            console.log(action_type);

            var $data_holder = $('a:contains("HIT Details"):eq(0)').parent().attr('data-react-props');
            var requester_name = JSON.parse($data_holder).modalOptions.requesterName;
            var hit_title = JSON.parse($data_holder).modalOptions.projectTitle;
            var hit_reward = JSON.parse($data_holder).modalOptions.monetaryReward.amountInDollars;
            var open_list = "";
            var close_list = "";
            var stored_copy = localStorage.getItem('time_data.' + hitReview_hitId);
            if (stored_copy !== null){
                open_list = stored_copy.split('$#$')[3];
                close_list = stored_copy.split('$#$')[4];
            }
            if (action_type == "openWorker"){
                open_list += "?" + now_in_milliseconds;
            }
            else if (action_type == "closeWorker"){
                close_list += "?" + now_in_milliseconds;
            }
            var autoapprove_data = requester_name + "$#$" + hit_title + "$#$"+ hit_reward +"$#$" + open_list + "$#$" + close_list;
            console.log(autoapprove_data);
            localStorage.setItem('time_data.' + hitReview_hitId, autoapprove_data);

        }
}

function mergeIntervals(intervals) {
    // test if there are at least 2 intervals
    if(intervals.length <= 1)
        return intervals;

    var stack = [];
    var top   = null;

    // sort the intervals based on their start values
    intervals = intervals.sort(function (startValue, endValue) {
        if (startValue[0] > endValue[0]) {
            return 1;
        }
        if (startValue[0] < endValue[0]) {
            return -1;
        }
        return 0;
    });

    // push the 1st interval into the stack
    stack.push(intervals[0]);

    // start from the next interval and merge if needed
    for (var i = 1; i < intervals.length; i++) {
        // get the top element
        top = stack[stack.length - 1];

        // if the current interval doesn't overlap with the
        // stack top element, push it to the stack
        if (top[1] < intervals[i][0]) {
            stack.push(intervals[i]);
        }
        // otherwise update the end value of the top element
        // if end of current interval is higher
        else if (top[1] < intervals[i][1])
        {
            top[1] = intervals[i][1];
            stack.pop();
            stack.push(top);
        }
    }

    return stack;
}

function combineIntervals(intervals, padding) {
    // test if there are at least 2 intervals
    if(intervals.length <= 1)
        return intervals;

    var stack = [];
    var top = null;

    // push the 1st interval into the stack
    stack.push(intervals[0]);

    // start from the next interval and merge if needed
    for (var i = 1; i < intervals.length; i++) {
        // get the top element
        top = stack[stack.length - 1];

        // if the current interval doesn't overlap with the
        // stack top element, push it to the stack
        if ((intervals[i][0]-top[1]) > padding) {
            //  console.log("hi",(intervals[i][0]-top[1]) + padding);
            stack.push(intervals[i]);
        }
        // otherwise update the end value of the top element
        // if end of current interval is higher
        else if (top[1] < intervals[i][1])
        {
            top[1] = intervals[i][1];
            stack.pop();
            stack.push(top);
        }
    }
    //console.log("stack",stack);
    return stack;
}

function pad(n, width, z) {
    z = z || '0';
    n = n + '';
    return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}

function Remove_Old_Data(){
    var today = new Date();
    var date = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
    if (localStorage.getItem('mturkHourlyRemovalDate') != date){
        console.log('mturk hourly data older than 45 days has been cleared');

        //thanks to @ChrisTurk for the old key removal code:
        Object.keys(localStorage)
            .forEach(function(key){
            if (/^time_data\./.test(key)) {
                console.log(localStorage[key]);
                var stored = localStorage[key].split(/\?/).pop(); // grabs last timestamp from the stack
                // days * hours * min * sec * 1000 [millisec 'cause of the way its stored]
                if (($.now()-stored) > 14*24*60*60*1000) {
                    // console.log('older than 14 days');
                    localStorage.removeItem(key); //uncomment this line to delete keys
                } else {
                    //  console.log('fresh memes'); //less than 1 day old, don't delete it.
                }
            }
        });
        localStorage.setItem('mturkHourlyRemovalDate',date);
    }
}

QingJ © 2025

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