Entrega iOSLab diplomado
Mix.install([
{:req, "~> 0.4.8"},
{:req_github_oauth, "~> 0.1.1"},
{:kino_explorer, "~> 0.1.11"}
])
Section
repo = "iOSLabUNAM/SwiftUIBasics"
req = Req.new(http_errors: :raise) |> ReqGitHubOAuth.attach()
defmodule GHUser do
defstruct(id: nil, name: nil, username: nil, email: nil, bio: nil, url: nil, evaluations: %{})
def parse(usr) do
%GHUser{
id: usr["id"],
name: usr["name"],
username: usr["login"],
email: usr["email"],
bio: usr["bio"],
url: usr["url"]
}
end
def fetch(req, username) do
body = Req.get!(req, url: "https://api.github.com/users/#{username}").body
parse(body)
end
end
defmodule Issue do
defstruct id: nil, title: nil
def parse(iss) do
%Issue{
id: iss["number"],
title: iss["title"]
}
end
def load_all(req, repo, max_page \\ 8) do
1..max_page
|> Task.async_stream(fn page ->
Req.get!(req, url: "https://api.github.com/repos/#{repo}/issues?page=#{page}&per_page=50").body
end)
|> Stream.map(fn {:ok, res} -> res end)
|> Enum.to_list()
|> List.flatten()
|> Enum.filter(fn iss -> is_nil(iss["closed_at"]) && is_nil(iss["pull_request"]) end)
|> Enum.map(&parse/1)
end
end
issues = Issue.load_all(req, repo)
defmodule PullRequest do
defstruct(
number: nil,
username: nil,
body: nil,
url: nil,
html_url: nil,
timeline_url: nil,
review: nil,
evaluation: 0
)
def parse(pr) do
%PullRequest{
number: pr["number"],
username: pr["user"]["login"],
body: pr["body"],
url: pr["url"],
html_url: pr["html_url"],
timeline_url: "#{pr["issue_url"]}/timeline"
}
end
def load_all(req, repo, max_page \\ 8) do
1..max_page
|> Task.async_stream(
fn page ->
Req.get!(req, url: "https://api.github.com/repos/#{repo}/pulls?page=#{page}&per_page=50").body
end,
max_concurrency: 8
)
|> Stream.map(fn {:ok, res} -> res end)
|> Enum.to_list()
|> List.flatten()
|> Enum.filter(fn pr -> is_nil(pr["closed_at"]) || !is_nil(pr["body"]) end)
|> Enum.map(&PullRequest.parse/1)
end
def fetch_reviews(req, pull_requests) do
pull_requests
|> Task.async_stream(&fetch_review(req, &1), max_concurrency: 8)
|> Enum.map(fn {:ok, res} -> res end)
end
def fetch_review(req, %PullRequest{timeline_url: timeline_url} = pr) do
comments = Req.get!(req, url: timeline_url).body
review =
comments
|> Stream.filter(fn %{"event" => event} -> event == "reviewed" end)
|> Stream.filter(fn %{"author_association" => author} -> author == "MEMBER" end)
|> Enum.map(fn rev -> rev["body"] || "" end)
|> Enum.join("\n")
|> String.trim()
pr
|> Map.put(:review, review)
|> parse_evaluation()
end
defp parse_evaluation(%PullRequest{review: review} = pr) do
if String.contains?(review, "LGTM") do
[{eval, _rem} | _] =
~r/\[(\d+)\]/
|> Regex.scan(review)
|> List.flatten()
|> tl()
|> Enum.map(&Integer.parse/1)
%PullRequest{pr | evaluation: eval}
else
pr
end
end
end
pulls = PullRequest.load_all(req, repo)
evaluated = PullRequest.fetch_reviews(req, pulls)
students =
evaluated
|> Enum.map(fn %{username: username} -> username end)
|> Enum.uniq()
|> Task.async_stream(&GHUser.fetch(req, &1))
|> Stream.map(fn {:ok, res} -> res end)
|> Enum.to_list()
|> List.flatten()
evaluation_map =
students
|> Enum.map(fn student ->
student_prs =
evaluated |> Enum.filter(fn %{username: username} -> username == student.username end)
evals =
issues
|> Enum.map(fn iss ->
{
iss.id,
Enum.find(student_prs, fn %{body: body} ->
String.contains?(body || "", "##{iss.id}")
end)
}
end)
|> Map.new()
%GHUser{student | evaluations: evals}
end)
csv =
evaluation_map
|> Enum.map(fn student ->
res =
student
|> Map.get(:evaluations)
|> Enum.map(fn {_iss_id, pr} ->
if is_nil(pr) do
""
else
"#{pr.evaluation || pr.html_url}"
end
end)
|> Enum.join(",")
"#{student.name || student.username},#{res}"
end)
|> Enum.join("\n")
hd_issues =
issues
|> Enum.map(&"##{&1.id}")
|> Enum.join(",")
Explorer.DataFrame.load_csv!("name,#{hd_issues}\n#{csv}")