Skip to content

Include status of all exercises #464

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions exercises/bob/BobTest.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// This file was auto-generated based on version 1.0.0 of the canonical data.
// This file was auto-generated based on version 1.1.0 of the canonical data.

module BobTest

Expand Down Expand Up @@ -41,7 +41,7 @@ let ``Using acronyms in regular speech`` () =

[<Fact(Skip = "Remove to run test")>]
let ``Forceful question`` () =
response "WHAT THE HELL WERE YOU THINKING?" |> should equal "Whoa, chill out!"
response "WHAT THE HELL WERE YOU THINKING?" |> should equal "Calm down, I know what I'm doing!"

[<Fact(Skip = "Remove to run test")>]
let ``Shouting numbers`` () =
Expand Down
14 changes: 10 additions & 4 deletions exercises/bob/Example.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ let response (input: string) =
let isQuestion = input.Trim().EndsWith "?"

match input with
| _ when isEmpty -> "Fine. Be that way!"
| _ when isYell -> "Whoa, chill out!"
| _ when isQuestion -> "Sure."
| _ -> "Whatever."
| _ when isEmpty ->
"Fine. Be that way!"
| _ when isYell && isQuestion ->
"Calm down, I know what I'm doing!"
| _ when isYell ->
"Whoa, chill out!"
| _ when isQuestion ->
"Sure."
| _ ->
"Whatever."
6 changes: 5 additions & 1 deletion exercises/book-store/BookStoreTest.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// This file was auto-generated based on version 1.0.1 of the canonical data.
// This file was auto-generated based on version 1.1.0 of the canonical data.

module BookStoreTest

Expand Down Expand Up @@ -59,3 +59,7 @@ let ``Three copies of first book and 2 each of remaining`` () =
let ``Three each of first 2 books and 2 each of remaining books`` () =
total [1; 1; 2; 2; 3; 3; 4; 4; 5; 5; 1; 2] |> should equal 75.20

[<Fact(Skip = "Remove to run test")>]
let ``Four groups of four are cheaper than two groups each of five and three`` () =
total [1; 1; 2; 2; 3; 3; 4; 5; 1; 1; 2; 2; 3; 3; 4; 5] |> should equal 102.40

14 changes: 1 addition & 13 deletions exercises/rna-transcription/RnaTranscriptionTest.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// This file was auto-generated based on version 1.0.1 of the canonical data.
// This file was auto-generated based on version 1.1.0 of the canonical data.

module RnaTranscriptionTest

Expand Down Expand Up @@ -27,15 +27,3 @@ let ``RNA complement of adenine is uracil`` () =
let ``RNA complement`` () =
toRna "ACGTGGTCTTAA" |> should equal (Some "UGCACCAGAAUU")

[<Fact(Skip = "Remove to run test")>]
let ``Correctly handles invalid input (RNA instead of DNA)`` () =
toRna "U" |> should equal None

[<Fact(Skip = "Remove to run test")>]
let ``Correctly handles completely invalid DNA input`` () =
toRna "XXX" |> should equal None

[<Fact(Skip = "Remove to run test")>]
let ``Correctly handles partially invalid DNA input`` () =
toRna "ACGTXXXCTTAA" |> should equal None

21 changes: 14 additions & 7 deletions generators/CanonicalData.fs
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,11 @@ let private updateToLatestVersion options =
Log.Information("Updated repository to latest version.");

let private downloadData options =
if options.SkipUpdateCanonicalData then
if options.CacheCanonicalData then
()
else
cloneRepository options
updateToLatestVersion options

let private readCanonicalData options exercise =
let exerciseCanonicalDataPath = Path.Combine(options.CanonicalDataDirectory, "exercises", exercise, "canonical-data.json")
File.ReadAllText(exerciseCanonicalDataPath)
updateToLatestVersion options

type CanonicalDataConverter() =
inherit JsonConverter()
Expand Down Expand Up @@ -87,7 +83,18 @@ type CanonicalDataConverter() =
override __.CanConvert(objectType: Type) = objectType = typeof<CanonicalData>

let private convertCanonicalData canonicalDataContents =
JsonConvert.DeserializeObject<CanonicalData>(canonicalDataContents, CanonicalDataConverter())
JsonConvert.DeserializeObject<CanonicalData>(canonicalDataContents, CanonicalDataConverter())

let private canonicalDataFile options exercise =
Path.Combine(options.CanonicalDataDirectory, "exercises", exercise, "canonical-data.json")

let private readCanonicalData options exercise =
canonicalDataFile options exercise
|> File.ReadAllText

let hasCanonicalData options exercise =
canonicalDataFile options exercise
|> File.Exists

let parseCanonicalData options =
downloadData options
Expand Down
102 changes: 84 additions & 18 deletions generators/Exercise.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ module Generators.Exercise
open System
open System.IO
open System.Reflection
open Newtonsoft.Json
open Newtonsoft.Json.Linq
open Humanizer
open Serilog
open Formatting
open Rendering
open CanonicalData

let private exerciseNameFromType (exerciseType: Type) = exerciseType.Name.Kebaberize()

[<AbstractClass>]
type Exercise() =
type GeneratorExercise() =

// Allow changes in canonical data
abstract member MapCanonicalData : CanonicalData -> CanonicalData
abstract member MapCanonicalDataCase : CanonicalDataCase -> CanonicalDataCase
Expand Down Expand Up @@ -56,7 +61,7 @@ type Exercise() =
abstract member UseFullMethodName : CanonicalDataCase -> bool
abstract member AdditionalNamespaces : string list

member this.Name = this.GetType().Name.Kebaberize()
member this.Name = this.GetType() |> exerciseNameFromType
member this.TestModuleName = this.GetType().Name.Pascalize() |> sprintf "%sTest"
member this.TestedModuleName = this.GetType().Name.Pascalize()

Expand All @@ -66,8 +71,6 @@ type Exercise() =
Directory.CreateDirectory(Path.GetDirectoryName(testClassPath)) |> ignore
File.WriteAllText(testClassPath, contents)

Log.Information("Generated tests for {Exercise} exercise in {TestClassPath}", this.Name, testClassPath);

member this.Regenerate(canonicalData) =
canonicalData
|> this.MapCanonicalData
Expand Down Expand Up @@ -233,23 +236,86 @@ type Exercise() =
default __.UseFullMethodName _ = false

default __.AdditionalNamespaces = []

type CustomExercise() =

member this.Name = this.GetType().Name.Kebaberize()

type MissingDataExercise = { Name: string }

type UnimplementedExercise = { Name: string }

type Exercise =
| Generator of GeneratorExercise
| Custom of CustomExercise
| MissingData of MissingDataExercise
| Unimplemented of UnimplementedExercise

let exerciseName exercise =
match exercise with
| Generator generator -> generator.Name
| Custom custom -> custom.Name
| Unimplemented unimplemented -> unimplemented.Name
| MissingData missingData -> missingData.Name

type ConfigExercise = { Slug: string }

type Config = { Exercises: ConfigExercise[] }

let private exerciseNames =
let configFilePath = "../config.json"
let configFileContents = File.ReadAllText configFilePath
let config = JsonConvert.DeserializeObject<Config>(configFileContents)

config.Exercises
|> Seq.map (fun exercise -> exercise.Slug)
|> Seq.sort
|> Seq.toList

let createExercises filteredExercise =
let private isConcreteType (ty: Type) = not ty.IsAbstract

let isConcreteExercise (exerciseType: Type) =
not exerciseType.IsAbstract && typeof<Exercise>.IsAssignableFrom(exerciseType)
let private isGeneratorExercise (ty: Type) = typeof<GeneratorExercise>.IsAssignableFrom(ty)

let isFilteredExercise exercise (exerciseType: Type) =
String.equals exercise exerciseType.Name ||
String.equals exercise (exerciseType.Name.Kebaberize())
let private isCustomExercise (ty: Type) = typeof<CustomExercise>.IsAssignableFrom(ty)

let includeExercise (exerciseType: Type) =
match filteredExercise with
| None -> isConcreteExercise exerciseType
| Some exercise -> isConcreteExercise exerciseType && isFilteredExercise exercise exerciseType
let private concreteAssemblyTypes =
Assembly.GetEntryAssembly().GetTypes()
|> Array.filter isConcreteType

let assemblyTypes = Assembly.GetEntryAssembly().GetTypes()
let private exerciseTypeByName<'T> exerciseType =
(exerciseNameFromType exerciseType, Activator.CreateInstance(exerciseType) :?> 'T)

seq { for exerciseType in assemblyTypes do
if includeExercise exerciseType then
yield Activator.CreateInstance(exerciseType) :?> Exercise }
let private exerciseTypesByName<'T> predicate =
concreteAssemblyTypes
|> Array.filter predicate
|> Array.map exerciseTypeByName<'T>
|> Map.ofArray

let private generatorExercises = exerciseTypesByName<GeneratorExercise> isGeneratorExercise

let private customExercises = exerciseTypesByName<CustomExercise> isCustomExercise

let private tryFindGeneratorExercise exerciseName =
generatorExercises
|> Map.tryFind exerciseName
|> Option.map Generator

let private tryFindCustomExercise exerciseName =
customExercises
|> Map.tryFind exerciseName
|> Option.map Custom

let private tryFindUnimplementedExercise options exerciseName =
match hasCanonicalData options exerciseName with
| true -> Unimplemented { Name = exerciseName } |> Some
| false -> None

let private createExercise options exerciseName =
tryFindGeneratorExercise exerciseName
|> Option.orElse (tryFindCustomExercise exerciseName)
|> Option.orElse (tryFindUnimplementedExercise options exerciseName)
|> Option.orElse (MissingData { Name = exerciseName } |> Some)

let createExercises options =
exerciseNames
|> List.choose (createExercise options)
Loading