Skip to content
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

log-levels concept exercise #270

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
12 changes: 12 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@
"booleans"
],
"status": "wip"
},
{
"slug": "log-levels",
"name": "Log Levels",
"uuid": "6c85ef11-cd2d-4718-9f75-84c467afb2f9",
"concepts": [
"strings"
],
"prerequisites": [
"lists"
],
"status": "wip"
}
],
"practice": [
Expand Down
1 change: 1 addition & 0 deletions exercises/concept/log-levels/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Hints
51 changes: 51 additions & 0 deletions exercises/concept/log-levels/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Instructions

In this exercise you'll be processing log-lines.

Each log line is a string formatted as follows: `"[<LEVEL>]: <MESSAGE>"`.

There are three different log levels:

- `INFO`
- `WARNING`
- `ERROR`

You have three tasks, each of which will take a log line and ask you to do something with it.

## 1. Get message from a log line

Implement the `message` function to return a log line's message:

```R
message("[ERROR]: Invalid operation")
# => "Invalid operation"
```

Any leading or trailing white space should be removed:

```R
message("[WARNING]: Disk almost full\r\n")
# => "Disk almost full"
```

## 2. Get log level from a log line

Implement the `log_level` function to return a log line's log level, which should be returned in lowercase:

```R
log_level("[ERROR]: Invalid operation")
# => "error"
```

## 3. Reformat a log line

Implement the `reformat` function that reformats the log line, putting the message first and the log level after it in parentheses:

```R
reformat("[INFO]: Operation completed")
# => "Operation completed (info)"
```

## Note

Please complete this exercise using ony R core functions. You may find internet references to the `stringr` package and functions with a `str_` prefix. Like most third-party packages, this is not available within Exercism.
70 changes: 70 additions & 0 deletions exercises/concept/log-levels/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Introduction

R makes no distinction between strings and characters, so `"A"` is just a one-character string.

To find the length of a string, use `nchar()`.
Using `length()` will return the length of the underlying `vector` of strings, typically 1 as in this case:

```R
> x <- "Some string"
> length(x) # length of underlying vector
[1] 1
> nchar(x) # length of string
[1] 11
````

## Constructing strings

To concatenate several strings we have `paste()`.
The default separator is a space, so override this if necessary.

```R
> paste("Mon", "Tue", "Wed", sep = "")
[1] "MonTueWed"
```

Numbers will be converted to strings as necessary.

There is also `sprintf()`, taken directly from C with identical syntax.
This allows precise formatting of the output string.
At its simplest just use `%s` as a placeholder for each string you want to interpolate, then add the value(s) as comma-separated parameters.

```R
> sprintf("Hello, %s, nice to see you", "Carol")
[1] "Hello, Carol, nice to see you"
```

## String parts

For a substring whose position is known:

```R
> x <- "Some string"
> substr(x, 3, 6)
[1] "me s"
```

The start and end indices are both inclusive.

To split on some separator, such as space:

```R
> strsplit(x, " ")[[1]]
[1] "Some" "string"
```

One thing to beware of is that this (like several string functions) returns a `list`.
If you have not yet unlocked that concept, just add `[[1]]` in double brackets to get the vector in the first element of the list.
Details will become clearer later in the syllabus.

## Whitespace

Leading and/or trailing whitespace can be removed with `trimws()`:

```R
> s <- " messy string "
> trimws(s)
[1] "messy string"
```

There is a `which = "left"` (or "right") parameter to only trim one end.
11 changes: 11 additions & 0 deletions exercises/concept/log-levels/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"authors": ["colinleach"],
"contributors": [],
"files": {
"solution": ["log-levels.R"],
"test": ["test_log-levels.R"],
"exemplar": [".meta/exemplar.R"]
},
"forked_from": ["fsharp/log-levels"],
"blurb": "Learn basic string manipulation in R by processing a log file."
}
31 changes: 31 additions & 0 deletions exercises/concept/log-levels/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Design

## Goal

The current goal of this exercise is to introduce the student to string-handling finctions in core R.

In an ideal world it would cover `stringr` as the preferred alternative, but Docker seems to be blocking that package in the test runner for now.

## Learning objectives

- Understand `nchar()` versus `length()`
- Understand `paste()` and `sprintf()` to construct strings.
- Understand `substr()` and `strsplit()` to get parts of a string.
- Understand `trimws()` to remove whitespace.

## Out of scope

- `grep()`, `regexexpr()` and `gregexpr()` are postponed to the `regular-expressions` concept.
- Discussion of `lists` as return values, beyond the necessary minimum.

## Concepts

The concepts this exercise unlocks are:

- `nothingness`
- `randomness`
- `regular-expressions`

## Prerequisites

- `vectors`
22 changes: 22 additions & 0 deletions exercises/concept/log-levels/.meta/exemplar.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Aiming to solve this early concept exercise without regex knowledge
# or access to stringr
#
# Makes me understand why stringr is so popular!

split_msg <- function(msg) {
strsplit(msg, ": ")[[1]]
}

message <- function(msg) {
trimws(split_msg(msg)[2])
}

log_level <- function(msg) {
raw <- tolower(split_msg(msg)[1])
len <- nchar(raw)
substr(raw, 2, len - 1)
}

reformat <- function(msg) {
paste(message(msg), " (", log_level(msg), ")", sep = "")
}
8 changes: 8 additions & 0 deletions exercises/concept/log-levels/log-levels.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
message <- function(msg) {
}

log_level <- function(msg) {
}

reformat <- function(msg) {
}
58 changes: 58 additions & 0 deletions exercises/concept/log-levels/test_log-levels.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
source("./log-levels.R")
library(testthat)

# message

test_that("Error message", {
msg <- "[ERROR]: Stack overflow"
expect_equal(message(msg), "Stack overflow")
})

test_that("Warning message", {
msg <- "[WARNING]: Disk almost full"
expect_equal(message(msg), "Disk almost full")
})

test_that("Info message", {
msg <- "[INFO]: File moved"
expect_equal(message(msg), "File moved")
})

test_that("Message with leading and trailing white space", {
msg <- "[WARNING]: \tTimezone not set \r\n"
expect_equal(message(msg), "Timezone not set")
})

# log_level

test_that("Error log level", {
msg <- "[ERROR]: Disk full"
expect_equal(log_level(msg), "error")
})

test_that("Warning log level", {
msg <- "[WARNING]: Unsafe password"
expect_equal(log_level(msg), "warning")
})

test_that("Info log level", {
msg <- "[INFO]: Timezone changed"
expect_equal(log_level(msg), "info")
})

# reformat

test_that("Warning reformat", {
msg <- "[WARNING]: Decreased performance"
expect_equal(reformat(msg), "Decreased performance (warning)")
})

test_that("Info reformat", {
msg <- "[INFO]: Disk defragmented"
expect_equal(reformat(msg), "Disk defragmented (info)")
})

test_that("Reformat with leading and trailing white space", {
msg <- "[ERROR]: \t Corrupt disk\t \t \r\n"
expect_equal(reformat(msg), "Corrupt disk (error)")
})