Skip to content

Commit

Permalink
Merge pull request #65 from softinio/cleanup-and-event-page
Browse files Browse the repository at this point in the history
Cleanup and meetup/conference directory support
  • Loading branch information
softinio authored Oct 26, 2024
2 parents 085fad8 + de0064d commit 7e3dd12
Show file tree
Hide file tree
Showing 19 changed files with 519 additions and 33 deletions.
9 changes: 7 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ lazy val core = crossProject(JVMPlatform)
"org.typelevel" %% "cats-core" % "2.12.0",
"org.typelevel" %% "cats-effect" % "3.5.4",
"com.monovore" %% "decline-effect" % "2.4.1",
"com.github.pureconfig" %% "pureconfig-core" % "0.17.7",
"com.github.pureconfig" %% "pureconfig-generic-scala3" % "0.17.7",
"com.github.pureconfig" %% "pureconfig-cats-effect" % "0.17.7",
"com.github.pureconfig" %% "pureconfig-http4s" % "0.17.7",
"org.http4s" %% "http4s-ember-client" % "0.23.28",
"org.http4s" %% "http4s-dsl" % "0.23.28",
"co.fs2" %% "fs2-core" % "3.11.0",
Expand Down Expand Up @@ -80,8 +81,12 @@ lazy val docs = project
Favicon.internal(Root / "img/favicon-32x32.png", sizes = "32x32")
)
.site
.mainNavigation(
depth = 3
)
.site
.footer(
"<br/>\n Created by <a href=\"https://www.softinio.com\">Salar Rahmanian</a> and Contributors.\n <br/>\n <a rel=\"license\" href=\"http://creativecommons.org/licenses/by/4.0/\"><img alt=\"Creative Commons License\" style=\"border-width:0\" src=\"https://i.creativecommons.org/l/by/4.0/80x15.png\" /></a><br />The content on this site by <span xmlns:cc=\"http://creativecommons.org/ns#\" property=\"cc:attributionName\">Salar Rahmanian and contributors</span> is licensed under a <a rel=\"license\" href=\"http://creativecommons.org/licenses/by/4.0/\">Creative Commons Attribution 4.0 International License</a>.<br/> \n Made with ❤\uFE0F in San Francisco using: | <a href=\"https://typelevel.org/cats-effect/\">cats-effect</a> | | <a href=\"https://github.com/typelevel/sbt-typelevel\">sbt-typelevel</a> | | <a href=\"https://ben.kirw.in/decline/\">decline</a> | | <a href=\"https://planet42.github.io/Laika/\">Laika</a> | "
"<br/>\n Created by <a href=\"https://www.softinio.com\">Salar Rahmanian</a> and Contributors.\n <br/>\n <a rel=\"license\" href=\"http://creativecommons.org/licenses/by/4.0/\"><img alt=\"Creative Commons License\" style=\"border-width:0\" src=\"https://i.creativecommons.org/l/by/4.0/80x15.png\" /></a><br />The content on this site by <span xmlns:cc=\"http://creativecommons.org/ns#\" property=\"cc:attributionName\">Salar Rahmanian and contributors</span> is licensed under a <a rel=\"license\" href=\"http://creativecommons.org/licenses/by/4.0/\">Creative Commons Attribution 4.0 International License</a>.<br/> \n Made with ❤\uFE0F in San Francisco using: | <a href=\"https://typelevel.org/cats-effect/\">cats-effect</a> | | <a href=\"https://github.com/typelevel/sbt-typelevel\">sbt-typelevel</a> | | <a href=\"https://ben.kirw.in/decline/\">decline</a> | | <a href=\"https://typelevel.org/Laika/\">Laika</a> | "
)
}
)
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala/com/softinio/scalanews/ConfigLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ import pureconfig.*
import pureconfig.module.catseffect.syntax.*
import cats.effect.IO
import com.softinio.scalanews.algebra.Configuration
import com.softinio.scalanews.algebra.EventConfig

object ConfigLoader {
def load(filePath: String = "config.json"): IO[Configuration] = {
ConfigSource.file(filePath).loadF[IO, Configuration]()
}

def loadEventsConfig(filePath: String = "events.json"): IO[EventConfig] = {
ConfigSource.file(filePath).loadF[IO, EventConfig]()
}
}
171 changes: 171 additions & 0 deletions core/src/main/scala/com/softinio/scalanews/Events.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* Copyright 2024 Salar Rahmanian
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.softinio.scalanews

import cats.effect.*
import fs2.io.file.*

import com.softinio.scalanews.algebra.Event
import com.softinio.scalanews.algebra.EventType
import com.softinio.scalanews.algebra.Location

object Events {
private val directoryMarkdownFilePath =
Path("docs/Resources/Event_Directory.md")

private def printLocations(locations: List[Location]): String = {
locations
.map { location =>
location.state match {
case Some(state) =>
s"- ${location.city}, ${state}, ${location.country}\n"
case None => s"- ${location.city}, ${location.country}\n"
}
}
.mkString("\n")
}

private def generateEvent(event: Event): String = {
s"""
|### ${event.name}
|
|${event.description}
|
|##### Links
|
|**Meetup:** <${event.meetupUrl.getOrElse("N/A")}>
|
|**Lu.ma:** <${event.lumaUrl.getOrElse("N/A")}>
|
|**Social Media:** <${event.socialMediaUrl.getOrElse("N/A")}>
|
|**Other:** <${event.otherUrl.getOrElse("N/A")}>
|
|##### Location(s)
|
|${printLocations(event.locations)}
|""".stripMargin
}

def generateDirectory(
eventList: List[Event],
eventType: EventType
): IO[String] = {
IO.blocking {

val directory = eventList.map(generateEvent)

s"${directory.mkString("\n")}".stripMargin
}
}

def cleanEventDirectory(): IO[ExitCode] = {
for {
exists <- Files[IO].exists(directoryMarkdownFilePath)
_ <- if (exists) Files[IO].delete(directoryMarkdownFilePath) else IO.unit
} yield ExitCode.Success
}

def addTopHeader(): IO[ExitCode] = {
val header = IO.blocking {
s"""
|# Meetups and Conferences
\n
""".stripMargin
}

for {
exists <- Files[IO].exists(directoryMarkdownFilePath)
headerValue <- header
_ <- fs2.Stream
.emits(List(headerValue))
.through(fs2.text.utf8.encode)
.through(
if (exists)
Files[IO].writeAll(directoryMarkdownFilePath, Flags(Flag.Append))
else
Files[IO].writeAll(directoryMarkdownFilePath, Flags(Flag.Create))
)
.compile
.drain
} yield ExitCode.Success
}

def addHeader(eventType: EventType): IO[ExitCode] = {
val header = IO.blocking {
s"""
|## ${eventType.toString} Directory
\n
""".stripMargin
}

for {
headerValue <- header
_ <- fs2.Stream
.emits(List(headerValue))
.through(fs2.text.utf8.encode)
.through(
Files[IO].writeAll(directoryMarkdownFilePath, Flags(Flag.Append))
)
.compile
.drain
} yield ExitCode.Success
}

def addFooter(): IO[ExitCode] = {
val footer = IO.blocking {
s"""
|###### Do you run a Scala related conference or meetup? Add it to this Directory!

|See [README](https://github.com/softinio/scalanews/blob/main/README.md) for details.\n
""".stripMargin
}

for {
footerValue <- footer
_ <- fs2.Stream
.emits(List(footerValue))
.through(fs2.text.utf8.encode)
.through(
Files[IO].writeAll(directoryMarkdownFilePath, Flags(Flag.Append))
)
.compile
.drain
} yield ExitCode.Success
}

def createEventDirectory(
eventList: List[Event],
eventType: EventType
): IO[ExitCode] = {
for {
exists <- Files[IO].exists(directoryMarkdownFilePath)
directory <- generateDirectory(eventList, eventType)
_ <- fs2.Stream
.emits(List(directory))
.through(fs2.text.utf8.encode)
.through(
if (exists)
Files[IO].writeAll(directoryMarkdownFilePath, Flags(Flag.Append))
else
Files[IO].writeAll(directoryMarkdownFilePath, Flags(Flag.Create))
)
.compile
.drain
} yield ExitCode.Success
}
}
30 changes: 29 additions & 1 deletion core/src/main/scala/com/softinio/scalanews/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import cats.implicits.*
import com.monovore.decline.*
import com.monovore.decline.effect.*

import com.softinio.scalanews.algebra.EventType

object Main
extends CommandIOApp(
name = "scalanews",
Expand All @@ -40,6 +42,8 @@ object Main

private case class Blogger(directory: Boolean)

private case class Event(directory: Boolean)

private case class GenerateNextBlog(
startDate: String,
endDate: String
Expand Down Expand Up @@ -98,13 +102,21 @@ object Main
.map(Blogger.apply)
}

private val eventOpts: Opts[Event] =
Opts.subcommand("event", "Event tasks") {
Opts
.flag("directory", "create a new event directory page", short = "e")
.orFalse
.map(Event.apply)
}

private val generateNextBlogOpts: Opts[GenerateNextBlog] =
Opts.subcommand("generate", "Generate next blog") {
(startDateOps, endDateOps).mapN(GenerateNextBlog.apply)
}

override def main: Opts[IO[ExitCode]] =
(publishOpts orElse createOpts orElse generateNextBlogOpts orElse bloggerOpts)
(publishOpts orElse createOpts orElse generateNextBlogOpts orElse bloggerOpts orElse eventOpts)
.map {
case Publish(publishDate, archiveDate, archiveFolder) =>
FileHandler.publish(publishDate, archiveDate, archiveFolder)
Expand All @@ -121,5 +133,21 @@ object Main
result <- Bloggers.createBloggerDirectory(config.bloggers)
} yield result
} else IO(ExitCode.Success)
case Event(directory) =>
if (directory) {
for {
config <- ConfigLoader.loadEventsConfig()
_ <- Events.cleanEventDirectory()
_ <- Events.addTopHeader()
_ <- Events.addHeader(EventType.Meetup)
_ <- Events.createEventDirectory(config.meetups, EventType.Meetup)
_ <- Events.addHeader(EventType.Conference)
_ <- Events.createEventDirectory(
config.conferences,
EventType.Conference
)
_ <- Events.addFooter()
} yield ExitCode.Success
} else IO(ExitCode.Success)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ package com.softinio.scalanews.algebra
import java.util.Date
import org.http4s.Uri

case class Article(title: String, url: Uri, author: String, publishedDate: Date)
case class Article(
title: String,
url: Option[Uri],
author: String,
publishedDate: Date
)

object Article {
def apply(
Expand All @@ -28,7 +33,7 @@ object Article {
author: String,
publishedDate: Date
): Article = {
val parsedUrl = Uri.fromString(url).toOption.get
val parsedUrl = Uri.fromString(url).toOption
Article(title, parsedUrl, author, publishedDate)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import java.net.URI

final case class Blog(name: String, url: URI, rss: URI) derives ConfigReader
final case class Configuration(bloggers: List[Blog]) derives ConfigReader
final case class EventConfig(meetups: List[Event], conferences: List[Event])
derives ConfigReader

object Config {
given urlReader: ConfigReader[URI] = ConfigReader[String].map(URI.create)
given ConfigReader[URI] = ConfigReader[String].map(URI.create)
}
42 changes: 42 additions & 0 deletions core/src/main/scala/com/softinio/scalanews/algebra/Event.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2024 Salar Rahmanian
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.softinio.scalanews.algebra

import org.http4s.Uri
import pureconfig.*
import pureconfig.generic.derivation.default.*

enum EventType:
case Meetup, Conference

case class Location(city: String, state: Option[String], country: String)
derives ConfigReader

case class Event(
name: String,
description: String,
meetupUrl: Option[Uri],
lumaUrl: Option[Uri],
socialMediaUrl: Option[Uri],
otherUrl: Option[Uri],
locations: List[Location]
) derives ConfigReader

object Event {
given ConfigReader[Option[Uri]] =
ConfigReader[Option[String]].map(_.flatMap(Uri.fromString(_).toOption))
}
Loading

0 comments on commit 7e3dd12

Please sign in to comment.