<repo_name>vinicelms/word_counter<file_sep>/requirements.txt
Flask==0.12
Flask-RESTful==0.3.5
lxml==3.7.2
requests==2.12.4
beautifulsoup4==4.5.3<file_sep>/core/scraping.py
from urllib import request, parse
from lxml import html
from bs4 import BeautifulSoup

class Scraping():

    def count_word(url, selected_word):
        p = parse.urlparse(url)
        if not p.scheme or not p.netloc:
            raise ValueError("URL is not valid!")

        if not selected_word:
            raise ValueError("Word cannot be blank!")

        html = request.urlopen(str(url)).read().decode("utf-8")

        soup = BeautifulSoup(html, "html.parser")
        for script in soup(["script", "style"]):
            script.extract()

        content = soup.get_text()

        words = content.split()

        word_quantity = 0

        for word in words:
            if word.lower() == selected_word.lower():
                word_quantity += 1

        return word_quantity<file_sep>/README.md
# Word Counter

API for the purpose of performing a data scraping on a page and returning a number of times that appears on the page.

The URL and a word are pre-informed through parameters entered in the API URL structure.

## Python version used: `3.6.0`

## Project requirements:
- Flask
- Flask-RESTful
- lxml
- requests
- beautifulsoup4

## How to use (install):
1 - Clone the project:
```sh
git clone https://github.com/vinicelms/word_counter.git
```
2 - Enter the project directory:
```sh
cd word_counter
```
3 - Create your Virtualenv `(optional, but recommended)`:
```sh
python -m venv .venv
```
4 - `(only if step 3 is used)` Activate your Virtualenv (Linux and Windows):
- Linux
```sh
source .venv/bin/activate
```
- Windows
```sh
.venv\Scripts\activate.bat
```
5 - Update Pip:
```sh
python -m pip install --upgrade pip
```
6 - Install the project requirements:
```sh
pip install -r requirements.txt
```

## How to use (API):
- Browser `(any operating system)`
```sh
127.0.0.1:5000/word_counter?url=https://docs.python.org/3/&word=python
```
- Linux (Terminal):
```sh
curl -X GET "127.0.0.1:5000/word_counter?url=https://docs.python.org/3/&word=python"
```
- Windows (Powershell):
```sh
Invoke-RestMethod -Uri "127.0.0.1:5000/word_counter?url=https://docs.python.org/3/&word=python"
```
> Powershell returns objects as PSObject, returning a key value pair with common tool formatting.

```sh
Powershell output formatting:
python
------
  18
```

> For the Powershell output to be formatted in JSON:
```sh
Invoke-RestMethod -Uri "127.0.0.1:5000/word_counter?url=https://docs.python.org/3/&word=python" | ConvertTo-Json
```

## Important:
##### `The values entered can be changed if the page is changed or if you use another URL or word.`

## Error Handling:
- Calling the endpoint without parameters or without URL
```sh
{"error": "URL cannot be blank!"}
```
- Calling the endpoint without the word parameter
```sh
{"error": "Word cannot be blank!"}
```
- Calling the endpoint with some incorrect URL information
```sh
{"error": "URL is not valid!"}
```

## How to run tests (Unit Tests):
```sh
python -m unittest discover tests
```
- Expected return on tests:
```sh
Ran 9 tests in 0.724s
OK
```
> The runtime of the test may vary depending on factors of operating system, hardware features or miscellaneous settings<file_sep>/core/app.py
from flask import Flask, jsonify
from flask_restful import Resource, Api, reqparse
from scraping import Scraping
import json

app = Flask(__name__)
api = Api(app)

class Word_Counter(Resource):
    def get(self):
        parser = reqparse.RequestParser()
        parser.add_argument('url', type=str)
        parser.add_argument('word', type=str)
        args = parser.parse_args()

        url = args.get('url')
        word = args.get('word')

        word_counted = {}

        if not url:
            return jsonify({"error": "URL cannot be blank!"})
        elif not word:
            return jsonify({"error": "Word cannot be blank!"})
        else:
            try:
                quantity = Scraping.count_word(url, word)
                word_counted[str(word)] = quantity
            except ValueError as url_error:
                return jsonify({"error": str(url_error)})
        
        return jsonify(word_counted)

api.add_resource(Word_Counter, '/word_counter')

if __name__ == "__main__":
    app.run(debug=True)<file_sep>/tests/tests.py
import unittest
from core.scraping import Scraping
from core.app import *
import json

class WordCounterTestCase(unittest.TestCase):

    def setUp(self):
        self.tester = app.test_client(self)

    def tearDown(self):
        pass

    def test_empty_parameters(self):
        response = self.tester.get("/word_counter", content_type="html/text")
        json_data = json.loads(response.get_data().decode("utf-8"))
        self.assertEqual(json_data, {"error": "URL cannot be blank!"})
        self.assertEqual(response.status_code, 200)

    def test_url_parameter_empty(self):
        response = self.tester.get("/word_counter?url=&word=python", content_type="html/text")
        json_data = json.loads(response.get_data().decode("utf-8"))
        self.assertEqual(json_data, {"error": "URL cannot be blank!"})
        self.assertEqual(response.status_code, 200)

    def test_word_parameter_empty(self):
        response = self.tester.get("/word_counter?url=https://docs.python.org/3&word=", content_type="html/text")
        json_data = json.loads(response.get_data().decode("utf-8"))
        self.assertEqual(json_data, {"error": "Word cannot be blank!"})
        self.assertEqual(response.status_code, 200)

    def test_url_without_protocol(self):
        response = self.tester.get("/word_counter?url=docs.python.org/3&word=python", content_type="html/text")
        json_data = json.loads(response.get_data().decode("utf-8"))
        self.assertEqual(json_data, {"error": "URL is not valid!"})
        self.assertEqual(response.status_code, 200)

    def test_correct_call(self):
        response = self.tester.get("/word_counter?url=https://docs.python.org/3&word=python", content_type="html/text")
        json_data = json.loads(response.get_data().decode("utf-8"))
        self.assertEqual(json_data, {"python": 18})


class ScrapingTestCase(unittest.TestCase):

    def setUp(self):
        pass

    def tearDown(self):
        pass

    def test_url_without_protocol(self):
        with self.assertRaises(ValueError) as e:
            Scraping.count_word("docs.python.org/3", "python")

        excpetion_message = str(e.exception)
        self.assertEqual(excpetion_message, "URL is not valid!")

    def test_url_empty(self):
        with self.assertRaises(ValueError) as e:
            Scraping.count_word("", "python")

        excpetion_message = str(e.exception)
        self.assertEqual(excpetion_message, "URL is not valid!")

    def test_word_empy(self):
        with self.assertRaises(ValueError) as e:
            Scraping.count_word("https://docs.python.org/3", "")

        excpetion_message = str(e.exception)
        self.assertEqual(excpetion_message, "Word cannot be blank!")

    def test_correct_call(self):
        response = Scraping.count_word("https://docs.python.org/3", "python")
        self.assertEqual(response, int(18))