Code viewer for World: DEB.ai.TE
/** 
 * CA318 Practical Project
 * Calem McGlynn  -  21420022
 * Joseph McNaney -  21371476
 * 
 * This code is broken up into two parts.
 *  - Frontend code 
 *  - Backend code
 * 
 * All code functionality is indicated with 
 * 
 * ///// Code purpose /////
 *      [code]
 * ///// Code purpose /////
 * 
 * Where code purpose indicates what the code inside is doing
 * 
 * 
 * Please Enter your OpenAI API key in the provided text box
 * 
 * 
 * The API calls start at 
 * //////On Submit Button Click//////
 * 
**/ 

var apiKey = "";


//////////////////////////////////Header and Input Row///////////////////////////////////////////////
const header = document.createElement('header');
header.style.backgroundColor = '#333';
header.style.padding = '10px';
header.style.margin = '-10px';
header.style.marginBottom = '10px';
header.style.textAlign = 'center';
header.style.color = 'white';
header.style.fontSize = '30px';
header.appendChild(document.createElement('h1').appendChild(document.createTextNode('DEB.ai.TE')));

const apiInput = document.createElement('input');
apiInput.type = 'text';
apiInput.minLength = '5';
apiInput.maxLength = '500';
apiInput.size = '70';
apiInput.placeholder = 'Enter API key...';

const apiInputButton = document.createElement('button');
apiInputButton.type = 'button';
apiInputButton.style.marginLeft = '10px';
apiInputButton.appendChild(document.createTextNode('Submit API key'));

const apiRow = document.createElement('div');
apiRow.style.display = 'flex';
apiRow.style.justifyContent = 'center';
apiRow.style.padding = '10px';
apiRow.appendChild(apiInput);
apiRow.appendChild(apiInputButton);

const input = document.createElement('input');
input.type = 'text';
input.minLength = '5';
input.maxLength = '500';
input.size = '70';
input.placeholder = 'Enter prompt...';

const inputButton = document.createElement('button');
inputButton.type = 'button';
inputButton.style.marginLeft = '10px';
inputButton.appendChild(document.createTextNode('Send Statement'));


const inputRow = document.createElement('div');
inputRow.style.display = 'flex';
inputRow.style.justifyContent = 'center';
inputRow.style.padding = '10px';
inputRow.appendChild(input);
inputRow.appendChild(inputButton);

//////////////////////////////////Header and Input Row///////////////////////////////////////////////


const main = document.createElement('main');
main.style.display = 'flex';
main.style.justifyContent = 'space-around';
main.style.padding = '20px';



/////////////////////////////Center Columns///////////////////////////////////////////////////////////
// Set style for center columns
function setCenterColumnStyle(column) {
  column.style.flex = '1';
  column.style.padding = '10px';
  column.style.border = '1px solid #ddd';
  column.style.margin = '10px';
  column.style.minHeight = '600px';
  column.style.maxWidth = '30%';
}

// Create "For" column
const column1 = document.createElement('div');
column1.className = 'column';
column1.id = 'forColumn';
setCenterColumnStyle(column1);
const forText = document.createElement('div');
forText.id = 'forText';
forText.appendChild(document.createElement('p'));
column1.appendChild(document.createElement('h2')); 
column1.querySelector('h2').innerHTML = 'Agree';
column1.appendChild(forText);

// Create "Against" column
const column2 = document.createElement('div');
column2.className = 'column';
column2.id = 'againstColumn';
setCenterColumnStyle(column2);
const againstText = document.createElement('div');
againstText.id = 'againstText';
againstText.appendChild(document.createElement('p'));
column2.appendChild(document.createElement('h2'));
column2.querySelector('h2').innerHTML = 'Disagree';
column2.appendChild(againstText);
/////////////////////////////Center Columns///////////////////////////////////////////////////////////


/////////////////////////////Side Columns///////////////////////////////////////////////////////////
function setSideColumnStyle(column) {
  column.style.flex = '0.5';
  column.style.padding = '10px';
  column.style.border = '1px solid #ddd';
  column.style.margin = '10px';
}

const factCheckColumn = document.createElement('div');
factCheckColumn.className = 'column';
factCheckColumn.id = 'factCheckColumn';
setSideColumnStyle(factCheckColumn);  
factCheckColumn.appendChild(document.createElement('h3'));
factCheckColumn.querySelector('h3').innerHTML = 'Fact Check Column';

const dictionaryColumn = document.createElement('div');
dictionaryColumn.className = 'column';
dictionaryColumn.id = 'dictionaryColumn';
dictionaryColumn.style.flex = '0.5';
dictionaryColumn.style.padding = '10px';
dictionaryColumn.style.border = '1px solid #ddd';
dictionaryColumn.style.margin = '10px';
setSideColumnStyle(dictionaryColumn);
dictionaryColumn.appendChild(document.createElement('h3'));
dictionaryColumn.querySelector('h3').innerHTML = 'Dictionary Column';
/////////////////////////////Side Columns///////////////////////////////////////////////////////////



/////////////////////////Hover Text Style////////////////////////////////////////////////////
// Add event listener "Mouseover" to the text
forText.addEventListener("mouseover", hoverEffect);
againstText.addEventListener('mouseover', hoverEffect);
// Add event listener "Mouseout"to the text
forText.addEventListener("mouseout", undoHoverEffect);
againstText.addEventListener('mouseout', undoHoverEffect);



function hoverEffect(event) {
  event.target.style.cursor = 'pointer';
  if (event.target.classList.contains('clickable')) {
    event.target.style.textDecoration = 'underline';
  }
}

function undoHoverEffect(event) {
  if (event.target.classList.contains('clickable')) {
    event.target.style.textDecoration = 'none';
  }
}
/////////////////////////Hover Text Style////////////////////////////////////////////////////



main.appendChild(factCheckColumn);
main.appendChild(column1);
main.appendChild(column2);
main.appendChild(dictionaryColumn);

document.body.appendChild(header);
document.body.appendChild(apiRow);
document.body.appendChild(inputRow);
document.body.appendChild(main);



/////////////////////////On Submit api Click////////////////////////////////////////////////////
apiInputButton.addEventListener('mouseup', submitAPIKey);

function submitAPIKey() {
    apiKey = apiInput.value;
    console.log(apiKey);

    apiRow.innerHTML = "<h5> API Key Entered </h5>";
}





/////////////////////////On Submit Button Click////////////////////////////////////////////////////
inputButton.addEventListener('mouseup', updateColumns);

// Clear center columns
function clearCenterColumns() {
  forText.innerHTML = 'loading...';
  againstText.innerHTML = 'loading...';
}


// change to add input from gpt api call
function updateColumns() {
  // Clear columns before adding anything
  clearCenterColumns();
  if (apiKey == "") {
      forText.innerHTML = "please enter APIkey";
      againstText.innerHTML = "please enter APIKey";
      return false;
  }


  const openaiEndpoint = 'https://api.openai.com/v1/chat/completions';
  const themodel = 'gpt-3.5-turbo';
  theprompt = input.value;

    // Agree AI
  var forData = {
    "model": themodel,
    "temperature": 0.7,
    "messages": [
      {
        "role": "user",
        "content": theprompt
      },
      {
        "role": "system",
        "content": "You are part of a debate team. You are to play the role of promoter of faith. You must agree with every prompt for the sake of the debate. Do not mention that you are playing promotor of faith. Simply agree with the prompt. Give reasons."
      }
    ]
  };

    // Disagree AI
  var againstData = {
    "model": themodel,
    "temperature": 0.7,
    "messages": [
      {
        "role": "user",
        "content": theprompt
      },
      {
        "role": "system",
        "content": "You are part of a debate team. You are to play the role of devil's advocate. You must disagree with every prompt for the sake of the debate. Do not mention that you are playing devil's advocate. Simply disagree with the prompt. Give reasons."
      }
    ]
  };

    // MH edit
        console.log ( theprompt );
       console.log ( "You are part of a debate team. You are to play the role of promoter of faith. You must agree with every prompt for the sake of the debate. Do not mention that you are playing promotor of faith. Simply agree with the prompt. Give reasons." );
       console.log ( "You are part of a debate team. You are to play the role of devil's advocate. You must disagree with every prompt for the sake of the debate. Do not mention that you are playing devil's advocate. Simply disagree with the prompt. Give reasons." );

    
    // Fetch the Agree AI response
  function forPrompt() {
    fetch(openaiEndpoint, {
        method: "POST",
        headers:
        {
            "Content-Type": "application/json",
            "Authorization": "Bearer " + apiKey
        },
        body: JSON.stringify(forData)
    })
    .then(response => response.json())
    .then(data => {
        console.log(data);
        var theresponse = data.choices[0].message.content;
        console.log(theresponse);
        const inputString = theresponse.split(' ');
        // Remove loading text
        forText.innerHTML = '';
        inputString.map( (word) => {
          forText.innerHTML += `<span class="clickable">${word} </span>`;
        })
    })
    .catch((error) => {
        console.error('Error:', error);
        forText.innerHTML = `Something went wrong`;
    });
  }


    // Fetch the Disagree AI response
  function againstPrompt() {
    fetch(openaiEndpoint, {
        method: "POST",
        headers:
        {
            "Content-Type": "application/json",
            "Authorization": "Bearer " + apiKey
        },
        body: JSON.stringify(againstData)
    })
    .then(response => response.json())
    .then(data => {
        console.log(data);
        var theresponse = data.choices[0].message.content;
        console.log(theresponse);
        const inputString = theresponse.split(' ');
        // Remove loading text
        againstText.innerHTML = '';
        inputString.map( (word) => {
          againstText.innerHTML += `<span class="clickable">${word} </span>`;
        })
    })
    .catch((error) => {
        console.error('Error:', error);
        againstText.innerHTML = `Something went wrong`;
    });
  }

  forPrompt();
  againstPrompt();
  
  // This is needed so the fucntion waits for the API call to finish
  setInterval(() => {}, 2000);
}


/////////////////////////On Submit Button Click////////////////////////////////////////////////////








////////////////////////////Dictionary API Call///////////////////////////////////////////////

forText.addEventListener('click', getDefinition);
againstText.addEventListener('click', getDefinition);

function getDefinition(event) {
      // Check if the clicked element has the "clickable" class
      if (event.target.classList.contains('clickable')) {
          // Call the dictionary API with the clicked word
          const word = event.target.innerText;
          const wordCleaned = word.replace(/[^a-zA-Z]/g, '');
          var apiUrl = `https://api.dictionaryapi.dev/api/v2/entries/en/${wordCleaned}`;
          dictionaryColumn.innerHTML = '';
          // Fetch the API response
          fetch(apiUrl)
              .then(response => response.json())
              .then(data => {
                // Check if the API response is an array and not empty
                if (Array.isArray(data) && data.length > 0) {
                  showDefinition(data); // Display the definitions in the column
                }
                else {
                  dictionaryColumn.innerHTML += `<p>No definitions found for the word '${wordCleaned}'.</p>`;
                }
              })
              .catch(error => {
                  console.error('Error fetching API:', error);
              });
      }
};


function showDefinition(data) {
    // Iterate through each entry and print definitions
    data.forEach(entry => {
        dictionaryColumn.innerHTML += `<h4>Word: ${entry.word}</h4>`;
        entry.meanings.forEach(meaning => {
            dictionaryColumn.innerHTML += `<h5>Part of Speech: ${meaning.partOfSpeech}</h5>`;
            meaning.definitions.slice(0,4).forEach(definition => {
                dictionaryColumn.innerHTML += `<p>Definition: ${definition.definition}</p>`;
            });
        });
        dictionaryColumn.innerHTML += `<br>`;
    });
};

////////////////////////////Dictionary API Call///////////////////////////////////////////////





////////////////////////////Fact Check API Call///////////////////////////////////////////////

var wikiCheckEndpoint = "https://nli.wmflabs.org/fact_checking_aggregated/?claim=";


column1.addEventListener('mouseup', getFactCheck);
column2.addEventListener('mouseup', getFactCheck);

function getFactCheck() {
  var highlightedText = window.getSelection().toString();
  if (highlightedText != "") {
    factCheckColumn.innerHTML = '';
    factCheckColumn.innerHTML = `<h3>Fact Checking: <p> ${highlightedText} </p> </h3>`;
    factCheckColumn.querySelector('p').style.fontWeight = 'normal';
    factCheckColumn.appendChild(document.createElement('h5'));
    factCheckColumn.querySelector('h5').innerHTML = `<h5>Loading...</h5>`;

    callWikiCheckAPI(highlightedText);
    
  }
}

function callWikiCheckAPI(claim) {

  // Clean claim to be checked
  claimCleaned = claim.replace(/ /g, '%20');
  // Fetch the API response
  fetch(wikiCheckEndpoint + claimCleaned, {
    method: "GET",
        headers:
        {
            "Content-Type": "application/json",
        },
      })
    .then(response => response.json())
    .then(data => {
      console.log(data);
      // Inconclusive result
      if (data.predicted_label.match("NOT ENOUGH INFO")){
        factCheckColumn.querySelector('h5').innerHTML = `<h5>Result: Inconclusive</h5>`;
        factCheckColumn.innerHTML += `<p>Not enough info</p>`;
      }
      // Factual result
      else if (data.predicted_label.match("SUPPORTS")){
        factCheckColumn.querySelector('h5').innerHTML = `<h5>Result: Factual</h5>`;
        printSupportingEvidence(data.predicted_evidence);
      }
      // Inaccurate result
      else if (data.predicted_label.match("REFUTES")){
        factCheckColumn.querySelector('h5').innerHTML = `<h5>Result: Inaccurate</h5>`;
        printSupportingEvidence(data.predicted_evidence);
      }
    })

    // Incase of error
    .catch(error => {
      console.error('Error fetching API:', error);
      factCheckColumn.querySelector('h5').innerHTML = `<h5>ERROR OBTAINING API RESULT</h5>`;

    });

    // This is needed so the fucntion waits for the API call to finish
    setTimeout(() => {}, 1000);
}


function printSupportingEvidence(data) {
  factCheckColumn.innerHTML += `<h4>Supporting Evidence</h4>`;
  data.forEach(evidence => {
    factCheckColumn.innerHTML += `<h4> - Article Title: ${evidence[0].replace(/_/g, " ")} </h4>`;
    factCheckColumn.innerHTML += `<p> ${evidence[1]}</p>`;
  });
}

////////////////////////////Fact Check API Call///////////////////////////////////////////////