<commit_before>package models.daos.drivers

import java.text.SimpleDateFormat
import javax.inject.Inject

import com.mohiva.play.silhouette.impl.providers.OAuth2Info
import models.daos.OAuth2InfoDAO
import models.{Contribution, Repository, User, GitHubRepo}
import org.apache.http.HttpStatus
import play.api.Play
import play.api.Play.current
import play.api.libs.json.{JsArray, JsValue}
import play.api.libs.ws._

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future


class GitHubAPI @Inject()(ws: WSClient, oauthDAO: OAuth2InfoDAO) {
  val retryCount = 4
  val backoutTime = 1000l


  val gitHubApiUrl = Play.configuration.getString("gitrank.githubApiUri").getOrElse("https://api.github.com")
  val gitHubDateFormatter = new SimpleDateFormat("yyyy-mm-dd'T'hh:mm:ss'Z'")
  val githubClientId = Play.configuration.getString("silhouette.github.clientID").getOrElse("")
  val githubClientSecret = Play.configuration.getString("silhouette.github.clientSecret").getOrElse("")


  /**
   * Get the user contributions statistics since last year or since the past update
   *
   * @param repositoryName name of the repository to get the statistics from
   * @param user user to get the statistics
   * @param oAuth2Info Authentication info
   * @return Contribution to be used as an update.
   */
  //TODO: Fix retry count
  def getUserContribution(repositoryName: String, user: User, oAuth2Info: OAuth2Info, retryCount: Int = retryCount): Future[Option[Contribution]] = {
    buildGitHubReq(ws.url(gitHubApiUrl + "/repos/" + repositoryName + "/stats/contributors"), Some(oAuth2Info))
      .get()
      .flatMap(response => {
      response.status match {
        case HttpStatus.SC_ACCEPTED => {
          if (retryCount > 0) {
            Thread.sleep(backoutTime)
            getUserContribution(repositoryName, user, oAuth2Info, retryCount - 1)
          }
          else {
            Future(None)
          }
        }
        case HttpStatus.SC_OK => {
          val userContribution = response.json.as[JsArray].value
            .filter(contributor => (contributor \ "author" \ "login").as[String] == user.username)
          userContribution.length match {
            case 0 => Future(None)
            case 1 => Future(Some((userContribution.head \ "weeks").as[JsArray].value.foldRight(Contribution(0, 0, 0, None)) {
              (value: JsValue, contribution: Contribution) => {
                Contribution(
                  (value \ "w").as[Long],
                  contribution.addedLines + (value \ "a").as[Int],
                  contribution.removedLines + (value \ "d").as[Int],
                  Some("a" + (value \ "a").as[Int] + "d" + (value \ "d").as[Int])
                )
              }
            }))
          }
        }
      }

    })
  }

  /**
   * Retrieves a blank repository from GitHub.
   *
   * @param repositoryName full name of the repository to retrieve
   * @param oAuth2Info authentication information to use the api
   * @return A repository with all fields initiated except for the score and weight field that are initiated by default
   *         to 0, returns None if the repository was not found
   */
  def getRepository(repositoryName: String,
                    oAuth2Info: Option[OAuth2Info] = None,
                    retryCount: Int = retryCount): Future[Option[Repository]] = {

    buildGitHubReq(ws.url(gitHubApiUrl + "/repos/" + repositoryName + "/stats/contributors"), oAuth2Info)
      .get()
      .flatMap(response => {
      response.status match {
        case HttpStatus.SC_OK =>
          val linesAdded = response.json.as[JsArray].value.foldLeft(0)((accumulator: Int, contributor: JsValue) => {
            (contributor \ "weeks").as[JsArray].value.foldLeft(accumulator) {
              (innerAcc: Int, week: JsValue) => innerAcc + (week \ "a").as[Int]
            }
          })

          val linesDeleted = response.json.as[JsArray].value.foldLeft(0)((accumulator: Int, contributor: JsValue) => {
            (contributor \ "weeks").as[JsArray].value.foldLeft(accumulator) {
              (innerAcc: Int, week: JsValue) => innerAcc + (week \ "d").as[Int]
            }
          })

          buildGitHubReq(ws.url(gitHubApiUrl + "/repos/" + repositoryName).withQueryString(("client_id", githubClientId), ("client_secret", githubClientSecret)), oAuth2Info)
            .get()
            .map(response => {
            Some(Repository((response.json \ "id").as[Int], linesAdded, linesDeleted, 0, repositoryName, 0))
          })
        case HttpStatus.SC_ACCEPTED => {
          if (retryCount > 0) {
            Thread.sleep(backoutTime)
            getRepository(repositoryName, oAuth2Info, retryCount - 1)
          }
          else {
            Future(None)
          }
        }
        case _ => Future(None)
      }
    })

  }

  /**
   * Gets the most stared repositories from GitHub.
   *
   * @param size How many repositories do we want, should be less than a 100 (GitHub page limit)
   * @param oAuth2Info Oauth information of the current user
   * @param filters filters the result by excluding the String list from the search results
   * @return a Sequence of GitHub repositories
   */<commit_msg>Adding succesful to the futures<commit_after>package models.daos.drivers

import java.text.SimpleDateFormat
import javax.inject.Inject

import com.mohiva.play.silhouette.impl.providers.OAuth2Info
import models.daos.OAuth2InfoDAO
import models.{Contribution, Repository, User, GitHubRepo}
import org.apache.http.HttpStatus
import play.api.Play
import play.api.Play.current
import play.api.libs.json.{JsArray, JsValue}
import play.api.libs.ws._

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future


class GitHubAPI @Inject()(ws: WSClient, oauthDAO: OAuth2InfoDAO) {
  val retryCount = 4
  val backoutTime = 1000l


  val gitHubApiUrl = Play.configuration.getString("gitrank.githubApiUri").getOrElse("https://api.github.com")
  val gitHubDateFormatter = new SimpleDateFormat("yyyy-mm-dd'T'hh:mm:ss'Z'")
  val githubClientId = Play.configuration.getString("silhouette.github.clientID").getOrElse("")
  val githubClientSecret = Play.configuration.getString("silhouette.github.clientSecret").getOrElse("")


  /**
   * Get the user contributions statistics since last year or since the past update
   *
   * @param repositoryName name of the repository to get the statistics from
   * @param user user to get the statistics
   * @param oAuth2Info Authentication info
   * @return Contribution to be used as an update.
   */
  def getUserContribution(repositoryName: String, user: User, oAuth2Info: OAuth2Info, retryCount: Int = retryCount): Future[Option[Contribution]] = {
    buildGitHubReq(ws.url(gitHubApiUrl + "/repos/" + repositoryName + "/stats/contributors"), Some(oAuth2Info))
      .get()
      .flatMap(response => {
      response.status match {
        case HttpStatus.SC_ACCEPTED => {
          if (retryCount > 0) {
            Thread.sleep(backoutTime)
            getUserContribution(repositoryName, user, oAuth2Info, retryCount - 1)
          }
          else {
            Future.successful(None)
          }
        }
        case HttpStatus.SC_OK => {
          val userContribution = response.json.as[JsArray].value
            .filter(contributor => (contributor \ "author" \ "login").as[String] == user.username)
          userContribution.length match {
            case 0 => Future.successful(None)
            case 1 => Future.successful(Some((userContribution.head \ "weeks").as[JsArray].value.foldRight(Contribution(0, 0, 0, None)) {
              (value: JsValue, contribution: Contribution) => {
                Contribution(
                  (value \ "w").as[Long],
                  contribution.addedLines + (value \ "a").as[Int],
                  contribution.removedLines + (value \ "d").as[Int],
                  Some("a" + (value \ "a").as[Int] + "d" + (value \ "d").as[Int])
                )
              }
            }))
          }
        }
      }

    })
  }

  /**
   * Retrieves a blank repository from GitHub.
   *
   * @param repositoryName full name of the repository to retrieve
   * @param oAuth2Info authentication information to use the api
   * @return A repository with all fields initiated except for the score and weight field that are initiated by default
   *         to 0, returns None if the repository was not found
   */
  def getRepository(repositoryName: String,
                    oAuth2Info: Option[OAuth2Info] = None,
                    retryCount: Int = retryCount): Future[Option[Repository]] = {

    buildGitHubReq(ws.url(gitHubApiUrl + "/repos/" + repositoryName + "/stats/contributors"), oAuth2Info)
      .get()
      .flatMap(response => {
      response.status match {
        case HttpStatus.SC_OK =>
          val linesAdded = response.json.as[JsArray].value.foldLeft(0)((accumulator: Int, contributor: JsValue) => {
            (contributor \ "weeks").as[JsArray].value.foldLeft(accumulator) {
              (innerAcc: Int, week: JsValue) => innerAcc + (week \ "a").as[Int]
            }
          })

          val linesDeleted = response.json.as[JsArray].value.foldLeft(0)((accumulator: Int, contributor: JsValue) => {
            (contributor \ "weeks").as[JsArray].value.foldLeft(accumulator) {
              (innerAcc: Int, week: JsValue) => innerAcc + (week \ "d").as[Int]
            }
          })

          buildGitHubReq(ws.url(gitHubApiUrl + "/repos/" + repositoryName).withQueryString(("client_id", githubClientId), ("client_secret", githubClientSecret)), oAuth2Info)
            .get()
            .map(response => {
            Some(Repository((response.json \ "id").as[Int], linesAdded, linesDeleted, 0, repositoryName, 0))
          })
        case HttpStatus.SC_ACCEPTED => {
          if (retryCount > 0) {
            Thread.sleep(backoutTime)
            getRepository(repositoryName, oAuth2Info, retryCount - 1)
          }
          else {
            Future.successful(None)
          }
        }
        case _ => Future.successful(None)
      }
    })

  }

  /**
   * Gets the most stared repositories from GitHub.
   *
   * @param size How many repositories do we want, should be less than a 100 (GitHub page limit)
   * @param oAuth2Info Oauth information of the current user
   * @param filters filters the result by excluding the String list from the search results
   * @return a Sequence of GitHub repositories
   */
{'fraction_non_alphanumeric': 0.11590887427318654, 'fraction_numerical': 0.007053665046230102, 'mean_word_length': 2.6005490734385726, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 39, 'https://': 2, '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': '2750989', 'n_tokens_mistral': 3152, 'n_tokens_neox': 2996, 'n_words': 722}