// ---
// jupyter:
//   jupytext:
//     text_representation:
//       extension: .fs
//       format_name: light
//       format_version: '1.5'
//       jupytext_version: 1.14.4
//   kernelspec:
//     display_name: .NET (F#)
//     language: F#
//     name: .net-fsharp
// ---

// [this doc on github](https://github.com/dotnet/interactive/tree/master/samples/notebooks/fsharp/Samples)
//
// # dotnet/fsharp Github Dashboard <img src ="https://upload.wikimedia.org/wikipedia/commons/thumb/3/38/Jupyter_logo.svg/207px-Jupyter_logo.svg.png" width="60px" alt="dotnet bot in space" align ="right">
//
// ### Add NuGet package references

// +
#i "nuget:https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json" 
#i "nuget:https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" 

#r "nuget:Octokit, 0.32.0"
#r "nuget:NodaTime, 2.4.6"
#r "nuget: XPlot.Plotly.Interactive, 3.0.4"

open Octokit
open NodaTime
open NodaTime.Extensions
open XPlot.Plotly
// -

// ### Setup
//
// Create a GitHub public API client

let organization = "dotnet"
let repositoryName = "fsharp"
let options = ApiOptions()
let gitHubClient = GitHubClient(ProductHeaderValue("notebook"))

// [Generate a user token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line) to get rid of public [api](https://github.com/octokit/octokit.net/blob/master/docs/getting-started.md) throttling policies for anonymous users 

// +
// let tokenAuth = Credentials("YOUR-TOKEN-HERE")
// gitHubClient.Credentials <- tokenAuth

// +
let today = SystemClock.Instance.InUtc().GetCurrentDate()

let startOfTheMonth = today.With(DateAdjusters.Month(1))

let startOfTheYear = LocalDate(today.Year, 1, 1).AtMidnight()

let since t = Nullable(DateTimeOffset(t))
// -

// Query GitHub for : 
// - Issues created this month
// - Issues closed this month
// - Every issue this year

// +
let createdIssuesRequest =
    RepositoryIssueRequest(
        Since = since (startOfTheMonth.ToDateTimeUnspecified()),
        Filter = IssueFilter.Created)

let closedIssuesRequest =
    RepositoryIssueRequest(
                Since = since (startOfTheMonth.ToDateTimeUnspecified()),
                State = ItemStateFilter.Closed)

let thisYearIssuesRequest =
    RepositoryIssueRequest(
        Since = since (startOfTheYear.ToDateTimeUnspecified()),
        State = ItemStateFilter.All)
// -

// Start pulling data via the GitHub API

// +
let createdThisMonthTask() =
    gitHubClient.Issue.GetAllForRepository(organization, repositoryName, createdIssuesRequest)
    |> Async.AwaitTask

let closedThisMonthTask() =
    gitHubClient.Issue.GetAllForRepository(organization, repositoryName, closedIssuesRequest)
    |> Async.AwaitTask

let thisYearIssuesTask() =
    gitHubClient.Issue.GetAllForRepository(organization, repositoryName, thisYearIssuesRequest)
    |> Async.AwaitTask

let results =
    [| createdThisMonthTask(); closedThisMonthTask(); thisYearIssuesTask() |]
    |> Async.Parallel
    |> Async.RunSynchronously

let createdThisMonth = results.[0]
let closedThisMonth = results.[1]
let thisYearIssues = results.[2]
// -

// Group open and closed issues by month

// +
let openSoFar =
    createdThisMonth
    |> Seq.sortBy (fun i -> i.CreatedAt)
    |> Seq.filter (fun i -> i.State.StringValue = "open")

let openByMonthOfCreation =
    openSoFar
    |> Seq.groupBy (fun i -> {| Year = i.CreatedAt.Year; Month = i.CreatedAt.Month |})
    |> Seq.map (fun (key, issues) -> {| Date = key; Count = Seq.count issues |})
    
let closedSoFar =
    thisYearIssues
    |> Seq.sortBy (fun i -> i.CreatedAt)
    |> Seq.filter (fun i -> i.State.StringValue = "closed")

let closedByMonthOfClosure =
    closedSoFar
    |> Seq.groupBy (fun i -> {| Year = i.ClosedAt.Value.Year; Month = i.ClosedAt.Value.Month |})
    |> Seq.map (fun (key, issues) ->  {| Date = key; Count = Seq.count issues |})

let openCountByMonth =
    let mutable runningTotal = thisYearIssues.Count
    
    closedSoFar
    |> List.ofSeq
    |> List.groupBy (fun i -> {| Year = i.CreatedAt.Year; Month = i.CreatedAt.Month |})
    |> List.map (fun (key, issues) ->
                   let dataPoint = {| Date = key; Count = Seq.count issues |}
                   dataPoint)
// -

// Show issues opened this month grouped by day 

// +
let createdThisMonthByDay =
    createdThisMonth
    |> Seq.groupBy (fun i -> DateTime(i.CreatedAt.Year,i.CreatedAt.Month, i.CreatedAt.Day))
    |> Seq.map (fun (date, issues) -> (date, issues.Count()))

createdThisMonthByDay
|> Chart.Line
|> Chart.WithTitle("Daily created issues over the past year")
// -

// Show open issues, in descending order. Limit to 10 to save screen space.

openSoFar
|> Seq.map (fun i -> {| CreatedAt = i.CreatedAt; Title = i.Title; State = i.State.StringValue; Number = i.Number |})
|> Seq.sortByDescending (fun d -> d.CreatedAt)
|> Seq.take 10 // Limiting the output to 10 here!

// Let's see what issues still opened, grouped by month, looks like

openByMonthOfCreation
|> Seq.map (fun g -> (DateTime(g.Date.Year, g.Date.Month, 1), g.Count))
|> Chart.Line
|> Chart.WithTitle("Issues still opened, grouped by month")

// Now let's look at idle vs active issues.

// +
let idleByMonth =
    openSoFar
    |> Seq.filter (fun i -> i.Comments = 0)
    |> Seq.groupBy (fun i -> DateTime(i.CreatedAt.Year, i.CreatedAt.Month, 1))
    |> Seq.map(fun (key, issues) -> {| Date = key; Count = Seq.count issues |})

let activeByMonth =
    openSoFar
    |> Seq.filter (fun i -> i.Comments > 0)
    |> Seq.groupBy (fun i -> DateTime(i.CreatedAt.Year, i.CreatedAt.Month, 1))
    |> Seq.map (fun (key, issues) -> {| Date = key; Count = Seq.count issues |})

let idleSeries =
    Graph.Scattergl(
        name = "Idle",
        y = (idleByMonth |> Seq.map (fun g -> g.Count)),
        x = (idleByMonth |> Seq.map (fun g -> g.Date)))

let activeSeries =
    Graph.Scattergl(
        name = "Active",
        y = (activeByMonth |> Seq.map (fun g -> g.Count)),
        x = (activeByMonth |> Seq.map (fun g -> g.Date)))

[idleSeries; activeSeries]
|> Chart.Plot
|> Chart.WithTitle("Idle and active open issue report")
// -

// Now let's generate a report for the whole year.

// +
let openDataPoints =
    openByMonthOfCreation
    |> Seq.map (fun g -> {| Date = DateTime(g.Date.Year, g.Date.Month, 1); Count = g.Count |})
    |> Seq.sortBy (fun d -> d.Date)

let closedDataPoints =
    closedByMonthOfClosure
    |> Seq.map (fun g -> {| Date = DateTime(g.Date.Year, g.Date.Month, 1); Count = g.Count |})
    |> Seq.sortBy (fun d -> d.Date)

let openCountByMonthDataPoints =
    openCountByMonth
    |> Seq.map (fun g -> {| Date = DateTime(g.Date.Year, g.Date.Month, 1); Count = g.Count |})
    |> Seq.sortBy (fun d -> d.Date)

let openSeries =
    Graph.Scattergl(
        name = "Created",
        x = (openDataPoints |> Seq.map (fun g -> g.Date)),
        y = (openDataPoints |> Seq.map (fun g -> g.Count)))

let closeSeries =
    Graph.Scattergl(
        name = "Closed",
        x = (closedDataPoints |> Seq.map (fun g -> g.Date)),
        y = (closedDataPoints |> Seq.map (fun g -> g.Count)))

let stillOpenSeries =
    Graph.Scattergl(
        name = "Open",
        x = (openCountByMonthDataPoints |> Seq.map (fun g -> g.Date)),
        y = (openCountByMonthDataPoints |> Seq.map (fun g -> g.Count)))

[openSeries; closeSeries; stillOpenSeries]
|> Chart.Plot
|> Chart.WithTitle("Issue report for the current year")
// -

// ### How many times has dotnet/fsharp been forked?

let forks =
    async {
        return!
            gitHubClient.Repository.Forks.GetAll(organization, repositoryName)
            |> Async.AwaitTask
    } |> Async.RunSynchronously

// +
let forksCreatedByMonth =
    forks
    |> Seq.groupBy (fun f -> DateTime(f.CreatedAt.Year, f.CreatedAt.Month,  f.CreatedAt.Day))
    |> Seq.map (fun (key, issues) -> {| Date = key; Count = Seq.count issues |})
    |> Seq.sortBy (fun g -> g.Date)

let forksLastUpdateByMonth =
    forks
    |> Seq.groupBy (fun f -> DateTime(f.UpdatedAt.Year, f.UpdatedAt.Month,  f.UpdatedAt.Day))
    |> Seq.map (fun (key, issues) -> {| Date = key; Count = Seq.count issues |})
    |> Seq.sortBy (fun g -> g.Date)

let forkCountByMonth =
    forksCreatedByMonth
    |> Seq.sortBy (fun g -> g.Date)
    |> Seq.map (fun g -> {| Date = g.Date; Count = g.Count |})

let forkCreationSeries =
    Graph.Scattergl(
        name = "created by month",
        x = (forksCreatedByMonth |> Seq.map (fun g -> g.Date)),
        y = (forksCreatedByMonth |> Seq.map (fun g -> g.Count)))

let forkTotalSeries =
    Graph.Scattergl(
        name = "running total",
        x = (forkCountByMonth |> Seq.map (fun g -> g.Date)),
        y = (forkCountByMonth |> Seq.map (fun g -> g.Count)))

let forkUpdateSeries =
    Graph.Scattergl(
        name = "last updated by month",
        x = (forksLastUpdateByMonth |> Seq.map (fun g -> g.Date)),
        y = (forksLastUpdateByMonth |> Seq.map (fun g -> g.Count)))

[forkCreationSeries; forkTotalSeries; forkUpdateSeries]
|> Chart.Plot
|> Chart.WithTitle("Fork activity")

StarcoderdataJupyterScriptsDedupFiltered