Skip to content

Commit

Permalink
feat: auto generate badges on homepage (#5)
Browse files Browse the repository at this point in the history
## Features

- Database and Configuration:
  - Use DATABASE_URL if available, otherwise use config (c95f4fd)

- Badge Management:
  - Always center badges in display (6b0ef20)
  - Filter and remove expired badges (d2064ce)
  - Send badges to frontend (f0d1548)
  - Iterate over badges (aa8f6e9)
- Implement logic to fetch JSON of all user badges from Credly (3d4be31)

- HTTP Requests:
  - Add HTTPoison for handling HTTP requests (0dc1318)

## Documentation

- Blog:
  - Add documentation for fetching posts (71307f3)
  • Loading branch information
RemoteRabbit authored Nov 8, 2024
1 parent 3fe1dc8 commit 7835e8b
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 73 deletions.
5 changes: 0 additions & 5 deletions config/prod.exs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import Config

# Note we also include the path to a cache manifest
# containing the digested version of static files. This
# manifest is generated by the `mix assets.deploy` task,
# which you should run after static files are built and
# before starting your production server.
config :portfolio, PortfolioWeb.Endpoint, cache_static_manifest: "priv/static/cache_manifest.json"

# Configures Swoosh API Client
Expand Down
45 changes: 44 additions & 1 deletion lib/portfolio/blog/post.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,50 @@ defmodule Portfolio.Blog.Post do
|> validate_required([:title, :slug, :context, :published_at])
end

@doc """
Fetches blog posts from a GitHub repository.
This function retrieves the contents of a specified directory in a GitHub repository,
filters out non-Markdown files, decodes the Base64-encoded content, parses the content,
filters out unpublished posts, and sorts the posts by date in descending order.
## Parameters
- `owner` (binary): The owner (user or organization) of the GitHub repository.
- `repo` (binary): The name of the GitHub repository.
- `path` (binary): The path to the directory containing the blog posts. Defaults to "posts/".
## Returns
- A list of maps representing the parsed blog posts, sorted by date in descending order.
Each map contains the following keys:
- `:title` (binary): The title of the blog post.
- `:date` (Date): The date of the blog post.
- `:content` (binary): The content of the blog post.
- `:status` (binary): The status of the blog post (e.g., "published", "draft").
- `:path` (binary): The path to the Markdown file in the GitHub repository.
## Examples
iex> MyModule.fetch_posts("owner", "repo")
[
%{
title: "My First Post",
date: ~D[2023-04-01],
content: "This is the content of my first post.",
status: "published",
path: "posts/my-first-post.md"
},
%{
title: "Another Post",
date: ~D[2023-03-15],
content: "This is the content of another post.",
status: "published",
path: "posts/another-post.md"
}
]
"""
def fetch_posts(owner, repo, path \\ "posts/") do
case Tentacat.Contents.find(owner, repo, path) do
{200, contents, _} ->
Expand Down Expand Up @@ -70,4 +114,3 @@ defmodule Portfolio.Blog.Post do
end
end
end

36 changes: 36 additions & 0 deletions lib/portfolio/credly.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
defmodule Portfolio.Credly do
def fetch_data do
headers = [{"Accept", "application/json"}]

case HTTPoison.get(
"https://www.credly.com/users/tristan-jahnke/badges",
headers
) do
{:ok,
%HTTPoison.Response{
status_code: 200,
body: body
}} ->
data = Jason.decode!(body)
filtered_data = filter_expired_badges(data["data"])
{:ok, %{"data" => filtered_data}}

{:error, _} ->
{:error, "Failed to get Credly badges."}
end
end

defp filter_expired_badges(badges) do
Enum.filter(badges, fn badge ->
expires_at = badge["expires_at"]

if expires_at == nil or
NaiveDateTime.compare(NaiveDateTime.from_iso8601!(expires_at), NaiveDateTime.utc_now()) ==
:gt do
true
else
false
end
end)
end
end
14 changes: 14 additions & 0 deletions lib/portfolio/repo.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
defmodule Portfolio.Repo do
require Logger

use Ecto.Repo,
otp_app: :portfolio,
adapter: Ecto.Adapters.Postgres

def init(_type, config) do
database_url = System.get_env("DATABASE_URL")

if database_url == nil do
Logger.debug("$DATABASE_URL not set, using config")
{:ok, config}
else
Logger.debug("Configuring database using $DATABASE_URL")
{:ok, Keyword.put(config, :url, database_url)}
end
end
end
8 changes: 7 additions & 1 deletion lib/portfolio_web/controllers/page_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ defmodule PortfolioWeb.PageController do
- The rendered home page.
"""
def home(conn, _params) do
render(conn, :home)
case Portfolio.Credly.fetch_data() do
{:ok, active_badges} ->
render(conn, :home, badges: active_badges)

{:error, _message} ->
render(conn, :home, badges: nil)
end
end

@doc """
Expand Down
83 changes: 18 additions & 65 deletions lib/portfolio_web/controllers/page_html/home.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -21,73 +21,26 @@
Certifications!
</h1>
</div>
<div class="flex flex-wrap -m-4 text-center">
<div class="p-4 md:w-1/5 sm:w-1/2 w-full">
<div class="px-4 py-6 flex items-center justify-center rounded-lg">
<div
data-iframe-width="250"
data-iframe-height="250"
data-share-badge-id="beda3bac-baed-4800-93ac-dab37e109c66"
data-share-badge-host="https://www.credly.com"
>
</div>
<script type="text/javascript" async src="//cdn.credly.com/assets/utilities/embed.js">
</script>
</div>
</div>
<div class="p-4 md:w-1/5 sm:w-1/2 w-full">
<div class="px-4 py-6 flex items-center justify-center rounded-lg">
<div
data-iframe-width="250"
data-iframe-height="250"
data-share-badge-id="67017628-d340-4419-9c4f-d714a19609ec"
data-share-badge-host="https://www.credly.com"
>
</div>
<script type="text/javascript" async src="//cdn.credly.com/assets/utilities/embed.js">
</script>
</div>
</div>
<div class="p-4 md:w-1/5 sm:w-1/2 w-full">
<div class="px-4 py-6 flex items-center justify-center rounded-lg">
<div
data-iframe-width="250"
data-iframe-height="250"
data-share-badge-id="952e8274-87ba-4026-a16a-dfe577957352"
data-share-badge-host="https://www.credly.com"
>
</div>
<script type="text/javascript" async src="//cdn.credly.com/assets/utilities/embed.js">
</script>
</div>
</div>
<div class="p-4 md:w-1/5 sm:w-1/2 w-full">
<div class="px-4 py-6 flex items-center justify-center rounded-lg">
<div
data-iframe-width="250"
data-iframe-height="250"
data-share-badge-id="d7e7486f-b75d-4ed0-8eee-eefad911be32"
data-share-badge-host="https://www.credly.com"
>
</div>
<script type="text/javascript" async src="//cdn.credly.com/assets/utilities/embed.js">
</script>
</div>
</div>
<div class="p-4 md:w-1/5 sm:w-1/2 w-full">
<div class="px-4 py-6 flex items-center justify-center rounded-lg">
<div
data-iframe-width="250"
data-iframe-height="250"
data-share-badge-id="e9ea8291-0756-4a4c-9079-ea93efd8f670"
data-share-badge-host="https://www.credly.com"
>

<%= if @badges do %>
<div class="flex flex-wrap justify-center -m-4 text-center">
<%= for badge <- @badges["data"] do %>
<div class="p-4 md:w-1/5 sm:w-1/2 w-full">
<div class="px-4 py-6 flex items-center justify-center rounded-lg">
<div
data-iframe-width="250"
data-iframe-height="250"
data-share-badge-id={badge["id"]}
data-share-badge-host="https://www.credly.com"
>
</div>
</div>
</div>
<script type="text/javascript" async src="//cdn.credly.com/assets/utilities/embed.js">
</script>
</div>
<% end %>
</div>
</div>
<script type="text/javascript" async src="//cdn.credly.com/assets/utilities/embed.js">
</script>
<% end %>
</div>
</section>

Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ defmodule Portfolio.MixProject do
{:makeup_html, ">= 0.0.0", only: :dev, runtime: false},
{:phoenix_ecto, "~> 4.4"},
{:ecto_sql, "~> 3.10"},
{:postgrex, ">= 0.0.0"}
{:postgrex, ">= 0.0.0"},
{:httpoison, "~> 2.0"}
]
end

Expand Down

0 comments on commit 7835e8b

Please sign in to comment.