<commit_before>package com.automatatutor.lib

import java.net.HttpURLConnection
import java.net.URL
import scala.xml.Elem
import scala.xml.NodeSeq
import scala.xml.Text
import scala.xml.Null
import scala.xml.TopScope
import scala.xml.Node
import scala.xml.UnprefixedAttribute
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.IOException
import scala.xml.XML
import net.liftweb.util.Props
import com.automatatutor.model.User

class SOAPConnection(val url : URL) {
    def wrapSOAPEnvelope(body : NodeSeq) : NodeSeq = {
      <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    	<soap:Body> { body } </soap:Body>
      </soap:Envelope>
    }
    
	def callMethod(namespace : String, methodName : String,  arguments : Map[String,Node]) : NodeSeq = {
	  val connection : HttpURLConnection = url.openConnection().asInstanceOf[HttpURLConnection]
	  
	  connection.setDoOutput(true)
	  connection.addRequestProperty("Content-Type", "text/xml; charset=utf-8")
	  connection.addRequestProperty("SOAPAction", '"' + namespace + "/" + methodName + '"')
	  
	  // Note the entry._2 : _*, which basically takes a Seq[A] apart so we pass the entries to Elem as varargs
	  val argumentsXml = arguments.map(entry => Elem(null, entry._1, Null, TopScope, true, entry._2 : _*)).toSeq
	  val soapBody = Elem(null, methodName, new UnprefixedAttribute("xmlns", namespace + '/', Null), TopScope, true, argumentsXml : _*)
	  
	  val requestXml = wrapSOAPEnvelope(soapBody)
	  val requestRaw = requestXml.toString
	  
	  val outputStream = connection.getOutputStream()
	  outputStream.write(requestRaw.getBytes())
	  
	  try {
          if(connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
            return NodeSeq.Empty
          } else {
            val returnRaw = scala.io.Source.fromInputStream(connection.getInputStream()).mkString
            // Strip four levels of wrapping from the result (soap:Envelope, soap:Body, Response, Result)
            val returnXml = XML.loadString(returnRaw) \ "_" \ "_" \ "_" \ "_"
            return returnXml
          }
	  } catch {
	    case exception : Exception => Text(scala.io.Source.fromInputStream(connection.getErrorStream()).mkString)
	  }
	}
}

object GraderConnection {
	val serverUrlString = Props.get("grader.url") openOrThrowException "URL of grader not specified"
	val serverUrl = new URL(serverUrlString)
	val soapConnection = new SOAPConnection(serverUrl)
	
	val namespace = Props.get("grader.methodnamespace") openOrThrowException "Namespace of grader methods not specified"	
	
	// DFA
	
	def getDfaFeedback(correctDfaDescription : String, attemptDfaDescription : String, maxGrade : Int) : (Int, NodeSeq) = {
	  val arguments = Map[String, Node](
	      "dfaCorrectDesc" -> XML.loadString(correctDfaDescription),
	      "dfaAttemptDesc" -> XML.loadString(attemptDfaDescription),
	      "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)),
	      "feedbackLevel" -> Elem(null, "feedbackLevel", Null, TopScope, true, Text("Hint")),
	      "enabledFeedbacks" -> Elem(null, "enabledFeedbacks", Null, TopScope, true, Text("ignored")));
	  
	  val responseXml = soapConnection.callMethod(namespace, "ComputeFeedbackXML", arguments)
	  
	  return ((responseXml \ "grade").text.toInt, (responseXml \ "feedString" \ "ul" \ "li"))<commit_msg>Refactor SOAPConnection<commit_after>package com.automatatutor.lib

import java.net.HttpURLConnection
import java.net.URL
import scala.xml.Elem
import scala.xml.NodeSeq
import scala.xml.Text
import scala.xml.Null
import scala.xml.TopScope
import scala.xml.Node
import scala.xml.UnprefixedAttribute
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.IOException
import scala.xml.XML
import net.liftweb.util.Props
import com.automatatutor.model.User

class SOAPConnection(val url : URL) {
    def wrapSOAPEnvelope(body : NodeSeq) : NodeSeq = {
      <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    	<soap:Body> { body } </soap:Body>
      </soap:Envelope>
    }
    
	def callMethod(namespace : String, methodName : String,  arguments : Map[String,Node]) : NodeSeq = {
	  def buildConnection : HttpURLConnection = {
      val connection = url.openConnection().asInstanceOf[HttpURLConnection]
      
      val methodNameWithNamespace = namespace + "/" + methodName
      
      connection.setDoOutput(true)
      connection.addRequestProperty("Content-Type", "text/xml; charset=utf-8")
      connection.addRequestProperty("SOAPAction", '"' + methodNameWithNamespace + '"')
      
      return connection
	  }
	  
	  def buildRequestBody = {
      def convertArgumentsToXml = {
        def buildXmlNode(label:String, children: NodeSeq) = {
          val prefix = null
          val attributes = Null
          val scope = TopScope
          val minimizeEmpty = true

          Elem(prefix, label, attributes, scope, true, children : _*)
        }

        def buildXmlForSoapArgument(argName: String, argVal: NodeSeq) = buildXmlNode(argName, argVal)

        arguments.map({ case (argName, argVal) => buildXmlForSoapArgument(argName, argVal) } ).toSeq
      }

      def wrapInSoapBody(payload: Seq[Node]) = {
        val prefix = null
        val xmlnsAttribute = new UnprefixedAttribute("xmlns", namespace + '/', Null)
        val scope = TopScope
        val minimizeEmpty = true

        Elem(prefix, methodName, xmlnsAttribute, scope, minimizeEmpty, payload : _*)
      }
      wrapInSoapBody(convertArgumentsToXml)
	  }

	  val connection = buildConnection

	  def buildRequest = {
      val soapBody = buildRequestBody
      val requestXml = wrapSOAPEnvelope(soapBody)
      requestXml.toString
	  }
	  val requestRaw = buildRequest
	  
	  connection.getOutputStream().write(requestRaw.getBytes())

    def responseIsOk = connection.getResponseCode() != HttpURLConnection.HTTP_OK
    def getReturnAsString = scala.io.Source.fromInputStream(connection.getInputStream()).mkString
    def getErrorAsString = scala.io.Source.fromInputStream(connection.getErrorStream()).mkString
	  
	  try {
      if(responseIsOk) {
        return NodeSeq.Empty
      } else {
        def stripWrappingFromResponse(response : NodeSeq) = {
          // There are four levels of wrapping around the result: "soap:Envelope", "soap:Body", "Response", "Result")
          response \ "_" \ "_" \ "_" \ "_"
        }
        

        val returnRaw = getReturnAsString
        val returnWithXmlWrapping = XML.loadString(returnRaw)
        stripWrappingFromResponse(returnWithXmlWrapping)
      }
	  } catch {
	    case exception : Exception => Text(getErrorAsString)
	  }
	}
}

object GraderConnection {
	val serverUrlString = Props.get("grader.url") openOrThrowException "URL of grader not specified"
	val serverUrl = new URL(serverUrlString)
	val soapConnection = new SOAPConnection(serverUrl)
	
	val namespace = Props.get("grader.methodnamespace") openOrThrowException "Namespace of grader methods not specified"	
	
	// DFA
	
	def getDfaFeedback(correctDfaDescription : String, attemptDfaDescription : String, maxGrade : Int) : (Int, NodeSeq) = {
	  val arguments = Map[String, Node](
	      "dfaCorrectDesc" -> XML.loadString(correctDfaDescription),
	      "dfaAttemptDesc" -> XML.loadString(attemptDfaDescription),
	      "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)),
	      "feedbackLevel" -> Elem(null, "feedbackLevel", Null, TopScope, true, Text("Hint")),
	      "enabledFeedbacks" -> Elem(null, "enabledFeedbacks", Null, TopScope, true, Text("ignored")));
	  
	  val responseXml = soapConnection.callMethod(namespace, "ComputeFeedbackXML", arguments)
	  
	  return ((responseXml \ "grade").text.toInt, (responseXml \ "feedString" \ "ul" \ "li"))
{'fraction_non_alphanumeric': 0.11521603005635567, 'fraction_numerical': 0.0031308703819661866, 'mean_word_length': 3.711504424778761, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 30}, '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': '6437176', 'n_tokens_mistral': 2479, 'n_tokens_neox': 2351, 'n_words': 506}