<commit_before>package org.openforis.sepal.sshgateway

import com.ocpsoft.pretty.time.Duration
import com.ocpsoft.pretty.time.PrettyTime
import com.ocpsoft.pretty.time.units.*
import org.slf4j.Logger
import org.slf4j.LoggerFactory

import java.text.DecimalFormat
import java.text.SimpleDateFormat

import static org.openforis.sepal.sshgateway.AsciiTable.*
import static org.openforis.sepal.sshgateway.Style.*

class Interactive {
    private static final Logger LOG = LoggerFactory.getLogger(this)
    private static final int CONFIRM_WHEN_LESS_THAN_HOURS = 10
    private final SepalClient sepalClient
    private final SshSessionCommand sessionCommand

    Interactive(String username, String sepalEndpoint, File privateKey, File output, String password) {
        sepalClient = new SepalClient(username, sepalEndpoint, password)
        sessionCommand = new SshSessionCommand(privateKey, output)
        if (!privateKey.exists())
            throw new IllegalArgumentException("Non-existing private key: $privateKey")
        if (!privateKey.canRead())
            throw new IllegalArgumentException("Non-readable private key: $privateKey")
    }

    void start() {
        def sandboxInfo = sepalClient.loadSandboxInfo()
        sandboxInfo.instanceTypes = sandboxInfo.instanceTypes.findAll { it.tag }

        printUsageBudget(sandboxInfo)
        BudgetChecker.assertWithinBudget(sandboxInfo)
        promptCreate(sandboxInfo)
    }

    private printUsageBudget(info) {
        println("Instance spending:".padRight(19) +
            format("${budget(info.spending.monthlyInstanceSpending, info.spending.monthlyInstanceBudget)} USD", PURPLE_INTENSE))
        println("Storage spending:".padRight(19) +
            format("${budget(info.spending.monthlyStorageSpending, info.spending.monthlyStorageBudget)} USD", PURPLE_INTENSE))
        println("Storage used:".padRight(19) +
            format("${budget(info.spending.storageUsed, info.spending.storageQuota)} GB", PURPLE_INTENSE))
        println()
    }

    private String budget(Number spending, Number budget) {
        def percentage = Math.round(100 * spending / budget)
        def decimalFormat = new DecimalFormat('##,###.##')
        return "${percentage.toString().padLeft(3)}% of ${decimalFormat.format(budget)}"
    }

    private void joinSelected(Map sandboxInfo, selection) {
        def selectedSessionIndex = (selection as int) - 1
        joinSession(sandboxInfo.sessions[selectedSessionIndex])
    }

    private void joinSession(Map session) {
        if (session.status == 'STARTING')
            print '\nSession is starting. This might start a new server, which would take several minutes.\nPlease wait...'
        def joinedSession = sepalClient.joinSession(session) {
            print '.'
        }
        if (session.status == 'STARTING')
            println('\n')
        sessionCommand.write(joinedSession)
    }

    private void promptCreate(Map sandboxInfo) {
        def types = sandboxInfo.instanceTypes as List<Map>
        def rows = instanceTypeRows(types)
        if (!sandboxInfo.sessions.empty) {
            rows.addAll(sessionRows(sandboxInfo.sessions))
        }
        print(new AsciiTable(rows))
        println()
        if (sandboxInfo.sessions) {
            println("Enter ${highlight('Type')}, ${highlight('ID')}, or ${highlight('ID')}+${highlight('s')}.")
            println()
            println("Examples:")
            println("  ${highlight('t1')}    start a t1 instance")
            println("  ${highlight('1')}     join session #1")
            println("  ${highlight('1s')}    stop session #1")
        } else {
            println("Enter ${highlight('Type')} of instance to start.")
            println()
            println("Example:")
            println("  ${highlight('t1')} - start a t1 instance")
        }
        println()
        readSelection(sandboxInfo)
    }

    private List sessionRows(List<Map> sessions) {
        def rows = [
            th([
                td(value: 'Active sessions', colSpan: 4, styles: [BOLD, GREEN])
            ]),
            th([
                td(value: 'ID', styles: [BOLD], align: 'right'),
                td(value: 'Type', styles: [BOLD]),
                td(value: 'Time', styles: [BOLD], align: 'right'),
                td(value: 'USD', styles: [BOLD], align: 'right')
            ])
        ]
        def options = sessions.withIndex().collect { Map session, i ->
            tr([
                td(value: i + 1, styles: [YELLOW_INTENSE]),
                td(value: session.instanceType.tag),
                td(value: timeSinceCreation(session), align: 'right'),<commit_msg>Re-formatted terminal interface.<commit_after>package org.openforis.sepal.sshgateway

import com.ocpsoft.pretty.time.Duration
import com.ocpsoft.pretty.time.PrettyTime
import com.ocpsoft.pretty.time.units.*
import org.slf4j.Logger
import org.slf4j.LoggerFactory

import java.text.DecimalFormat
import java.text.SimpleDateFormat

import static org.openforis.sepal.sshgateway.AsciiTable.*
import static org.openforis.sepal.sshgateway.Style.*

class Interactive {
    private static final Logger LOG = LoggerFactory.getLogger(this)
    private static final int CONFIRM_WHEN_LESS_THAN_HOURS = 10
    private final SepalClient sepalClient
    private final SshSessionCommand sessionCommand

    Interactive(String username, String sepalEndpoint, File privateKey, File output, String password) {
        sepalClient = new SepalClient(username, sepalEndpoint, password)
        sessionCommand = new SshSessionCommand(privateKey, output)
        if (!privateKey.exists())
            throw new IllegalArgumentException("Non-existing private key: $privateKey")
        if (!privateKey.canRead())
            throw new IllegalArgumentException("Non-readable private key: $privateKey")
    }

    void start() {
        def sandboxInfo = sepalClient.loadSandboxInfo()
        sandboxInfo.instanceTypes = sandboxInfo.instanceTypes.findAll { it.tag }

        printUsageBudget(sandboxInfo)
        BudgetChecker.assertWithinBudget(sandboxInfo)
        promptCreate(sandboxInfo)
    }

    private printUsageBudget(info) {
        println("Instance spending:".padRight(19) +
            format("${budget(info.spending.monthlyInstanceSpending, info.spending.monthlyInstanceBudget)} USD", PURPLE_INTENSE))
        println("Storage spending:".padRight(19) +
            format("${budget(info.spending.monthlyStorageSpending, info.spending.monthlyStorageBudget)} USD", PURPLE_INTENSE))
        println("Storage used:".padRight(19) +
            format("${budget(info.spending.storageUsed, info.spending.storageQuota)} GB", PURPLE_INTENSE))
        println()
    }

    private String budget(Number spending, Number budget) {
        def percentage = Math.round(100 * spending / budget)
        def decimalFormat = new DecimalFormat('##,###.##')
        return "${percentage.toString().padLeft(3)}% of ${decimalFormat.format(budget)}"
    }

    private void joinSelected(Map sandboxInfo, selection) {
        def selectedSessionIndex = (selection as int) - 1
        joinSession(sandboxInfo.sessions[selectedSessionIndex])
    }

    private void joinSession(Map session) {
        if (session.status == 'STARTING')
            print '\nSession is starting. This might start a new server, which would take several minutes.\nPlease wait...'
        def joinedSession = sepalClient.joinSession(session) {
            print '.'
        }
        if (session.status == 'STARTING')
            println('\n')
        sessionCommand.write(joinedSession)
    }

    private void promptCreate(Map sandboxInfo) {
        def types = sandboxInfo.instanceTypes as List<Map>
        def rows = instanceTypeRows(types)
        if (!sandboxInfo.sessions.empty) {
            rows.addAll(sessionRows(sandboxInfo.sessions))
        }
        print(new AsciiTable(rows))
        println()
        if (sandboxInfo.sessions) {
            println("Enter ${highlight('Type')}, ${highlight('ID')}, or ${highlight('ID')}+${highlight('s')}.")
            println()
            println("Examples:")
            println("  ${highlight('t1')}    start a t1 instance")
            println("  ${highlight('1')}     join session #1")
            println("  ${highlight('1s')}    stop session #1")
        } else {
            println("Enter ${highlight('Type')} of instance to start.")
            println()
            println("Example:")
            println("  ${highlight('t1')}    start a t1 instance")
        }
        println()
        readSelection(sandboxInfo)
    }

    private List sessionRows(List<Map> sessions) {
        def rows = [
            th([
                td(value: 'Active sessions', colSpan: 4, styles: [BOLD, GREEN])
            ]),
            th([
                td(value: 'ID', styles: [BOLD], align: 'right'),
                td(value: 'Type', styles: [BOLD]),
                td(value: 'Time', styles: [BOLD], align: 'right'),
                td(value: 'USD', styles: [BOLD], align: 'right')
            ])
        ]
        def options = sessions.withIndex().collect { Map session, i ->
            tr([
                td(value: i + 1, styles: [YELLOW_INTENSE]),
                td(value: session.instanceType.tag),
                td(value: timeSinceCreation(session), align: 'right'),
{'fraction_non_alphanumeric': 0.1195069667738478, 'fraction_numerical': 0.0053590568060021436, 'mean_word_length': 2.7057188244638604, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '143929', 'n_tokens_mistral': 2724, 'n_tokens_neox': 2553, 'n_words': 581}