Skip to content

Implement exercise markdown #797

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 7 commits into from
Oct 14, 2017
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
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,14 @@
"conditionals"
]
},
{
"uuid": "1818f134-0ed9-e680-9b29-45ffd2b3344b0f841c7",
"slug": "markdown",
"difficulty": 3,
"topics": [
"refactoring"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure that this topic is appropriate, could you please explain your reasoning?

You can use this list of general topics common among all tracks - https://github.com/exercism/problem-specifications/blob/master/TOPICS.txt

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regular_expressions, pattern_matching are good candidates for example

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look at this file:
"The markdown exercise is a refactoring exercise. There is code that parses a given string with Markdown syntax and returns the associated HTML for that string. Even though this code is confusingly written and hard to follow, somehow it works and all the tests are passing! Your challenge is to re-write this code to make it easier to read and maintain while still making sure that all the tests keep passing."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m-a-ge, this exercise is intended to be a refactoring exercise rather than an implementation one (see the README). This also explains the presence of a working solution (does TravisCI currently test this solution? I feel like it needs to) in markdown.py.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paulglass sorry, I've missed it due to amount of opened PRs 😕
@N-Parsons thanks!

No changes needed then 😄

]
},
{
"uuid": "e348a307-078c-5280-65af-a159283d4e79438b755",
"slug": "forth",
Expand Down
30 changes: 30 additions & 0 deletions exercises/markdown/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Markdown

The markdown exercise is a refactoring exercise. There is code that parses a
given string with [Markdown
syntax](https://guides.github.com/features/mastering-markdown/) and returns the
associated HTML for that string. Even though this code is confusingly written
and hard to follow, somehow it works and all the tests are passing! Your
challenge is to re-write this code to make it easier to read and maintain
while still making sure that all the tests keep passing.

It would be helpful if you made notes of what you did in your refactoring in
comments so reviewers can see that, but it isn't strictly necessary. The most
important thing is to make the code better!

### Submitting Exercises

Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory.

For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.


For more detailed information about running tests, code style and linting,
please see the [help page](http://exercism.io/languages/python).

## Source

Syntax [https://guides.github.com/features/mastering-markdown/](https://guides.github.com/features/mastering-markdown/)

## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
80 changes: 80 additions & 0 deletions exercises/markdown/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import re


def parse_markdown(markdown):
lines = markdown.split('\n')
html = ''
in_list = False
for line in lines:
res = parse_line(line, in_list)
html += res['line']
in_list = res['in_list']
if in_list:
html += '</ul>'
return html


def wrap(line, tag):
return '<{tag}>{line}</{tag}>'.format(line=line, tag=tag)


def check_headers(line):
pattern = '# (.*)'
for i in range(6):
if re.match(pattern, line):
return wrap(line[(i + 2):], 'h' + str(i + 1))
pattern = '#' + pattern
return line


def check_bold(line):
bold_pattern = '(.*)__(.*)__(.*)'
bold_match = re.match(bold_pattern, line)
if bold_match:
return bold_match.group(1) + wrap(bold_match.group(2), 'strong')\
+ bold_match.group(3)
else:
return None


def check_italic(line):
italic_pattern = '(.*)_(.*)_(.*)'
italic_match = re.match(italic_pattern, line)
if italic_match:
return italic_match.group(1) + wrap(italic_match.group(2), 'em')\
+ italic_match.group(3)
else:
return None


def parse_line(line, in_list):
res = check_headers(line)

list_match = re.match(r'\* (.*)', res)

if (list_match):
if not in_list:
res = '<ul>' + wrap(list_match.group(1), 'li')
in_list = True
else:
res = wrap(list_match.group(1), 'li')
else:
if in_list:
res += '</ul>'
in_list = False

if not re.match('<h|<ul|<li', res):
res = wrap(res, 'p')

if not re.match('(<ul>)?<li>_(.*)', res):
res = re.sub('(.*)(<li>)(.*)(</li>)(.*)', r'\1\2<p>\3</p>\4\5', res)

while check_bold(res):
res = check_bold(res)
while check_italic(res):
res = check_italic(res)

return {
'line': res,
'in_list': in_list
}
71 changes: 71 additions & 0 deletions exercises/markdown/markdown.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import re


def parse_markdown(markdown):
lines = markdown.split('\n')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you forgot to replace an actual solution with an exercise placeholder for the learner.
Could you please fix this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Learner should refactor this code as I mentioned before.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paulglass yeah, I see it now, thanks

res = ''
in_list = False
for i in lines:
if re.match('###### (.*)', i) is not None:
i = '<h6>' + i[7:] + '</h6>'
elif re.match('## (.*)', i) is not None:
i = '<h2>' + i[3:] + '</h2>'
elif re.match('# (.*)', i) is not None:
i = '<h1>' + i[2:] + '</h1>'
m = re.match(r'\* (.*)', i)
if m:
if not in_list:
in_list = True
is_bold = False
is_italic = False
curr = m.group(1)
m1 = re.match('(.*)__(.*)__(.*)', curr)
if m1:
curr = m1.group(1) + '<strong>' + \
m1.group(2) + '</strong>' + m1.group(3)
is_bold = True
m1 = re.match('(.*)_(.*)_(.*)', curr)
if m1:
curr = m1.group(1) + '<em>' + m1.group(2) + \
'</em>' + m1.group(3)
is_italic = True
if is_italic or is_bold:
i = '<ul><li>' + curr + '</li>'
else:
i = '<ul><li><p>' + curr + '</p></li>'
else:
is_bold = False
is_italic = False
curr = m.group(1)
m1 = re.match('(.*)__(.*)__(.*)', curr)
if m1:
curr = m1.group(1) + '<strong>' + \
m1.group(2) + '</strong>' + m1.group(3)
is_bold = True
m1 = re.match('(.*)_(.*)_(.*)', curr)
if m1:
curr = m1.group(1) + '<em>' + m1.group(2) + \
'</em>' + m1.group(3)
is_italic = True
if is_italic or is_bold:
i = '<li>' + curr + '</li>'
else:
i = '<li><p>' + curr + '</p></li>'
else:
if in_list:
i = '</ul>+i'
in_list = False

m = re.match('<h|<ul|<p|<li', i)
if not m:
i = '<p>' + i + '</p>'
m = re.match('(.*)__(.*)__(.*)', i)
if m:
i = m.group(1) + '<strong>' + m.group(2) + '</strong>' + m.group(3)
m = re.match('(.*)_(.*)_(.*)', i)
if m:
i = m.group(1) + '<em>' + m.group(2) + '</em>' + m.group(3)
res += i
if in_list:
res += '</ul>'
return res
51 changes: 51 additions & 0 deletions exercises/markdown/markdown_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import unittest
from markdown import parse_markdown


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please also leave a comment stating what version of canonical-data.json the tests were adopted as discussed in #784?

Copy link
Contributor

@N-Parsons N-Parsons Oct 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paulglass, the new format is:
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0


class TestMarkdown(unittest.TestCase):

def test_paragraph(self):
self.assertEqual(parse_markdown('This will be a paragraph'),
'<p>This will be a paragraph</p>')

def test_italics(self):
self.assertEqual(parse_markdown('_This will be italic_'),
'<p><em>This will be italic</em></p>')

def test_bold(self):
self.assertEqual(parse_markdown('__This will be bold__'),
'<p><strong>This will be bold</strong></p>')

def test_mixed(self):
self.assertEqual(parse_markdown('This will _be_ __mixed__'),
'<p>This will <em>be</em> <strong>mixed</strong></p>')

def test_h1(self):
self.assertEqual(parse_markdown('# This will be an h1'),
'<h1>This will be an h1</h1>')

def test_h2(self):
self.assertEqual(parse_markdown('## This will be an h2'),
'<h2>This will be an h2</h2>')

def test_h6(self):
self.assertEqual(parse_markdown(
'###### This will be an h6'), '<h6>This will be an h6</h6>')

def test_unordered_lists(self):
self.assertEqual(parse_markdown('* Item 1\n* Item 2'),
'<ul><li><p>Item 1</p></li>'
'<li><p>Item 2</p></li></ul>')

def test_little_bit_of_everything(self):
self.assertEqual(parse_markdown(
'# Header!\n* __Bold Item__\n* _Italic Item_'),
'<h1>Header!</h1><ul><li><strong>Bold Item</strong></li>'
'<li><em>Italic Item</em></li></ul>')


if __name__ == '__main__':
unittest.main()