Skip to content

Commit

Permalink
release 1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
jim.molecule committed Mar 21, 2020

Verified

This commit was signed with the committer’s verified signature.
mbeacom Mark Beacom
1 parent a7b4e2a commit 5b48ef9
Showing 25 changed files with 821 additions and 44 deletions.
38 changes: 38 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]

**Additional context**
Add any other context about the problem here.
22 changes: 22 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex.
I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions
or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
23 changes: 17 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -14,8 +14,6 @@ dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
@@ -85,10 +83,12 @@ ipython_config.py
.python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
# According to pypa/pipenv#598, it is recommended to include
# Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific
# dependencies or dependencies having no cross-platform support,
# pipenv may install dependencies that don't work, or not install
# all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
@@ -127,3 +127,14 @@ dmypy.json

# Pyre type checker
.pyre/

# Exclude IntelliJ files, they will be recreated from the build files
.idea/*
# But keep dictionaries to have less false positives in spellcheck inspection
!.idea/dictionaries

# PyCharm files
.DS_Store

# Application log files
histogramer/.logs/
14 changes: 14 additions & 0 deletions .idea/dictionaries/jim_molecule.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

80 changes: 43 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,43 @@
## Welcome to GitHub Pages

You can use the [editor on GitHub](https://github.com/jim-molecule/histogramer/edit/master/README.md) to maintain and preview the content for your website in Markdown files.

Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files.

### Markdown

Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for

```markdown
Syntax highlighted code block

# Header 1
## Header 2
### Header 3

- Bulleted
- List

1. Numbered
2. List

**Bold** and _Italic_ and `Code` text

[Link](url) and ![Image](src)
```

For more details see [GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/).

### Jekyll Themes

Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/jim-molecule/histogramer/settings). The name of this theme is saved in the Jekyll `_config.yml` configuration file.

### Support or Contact

Having trouble with Pages? Check out our [documentation](https://help.github.com/categories/github-pages-basics/) or [contact support](https://github.com/contact) and we’ll help you sort it out.
# Compatibility #
Histogramer has tested using `python 3.6.8` virtual environment in:
* Windows 10 OS
* macOS Catalina

# Description #
This tool analyze text files in a directory (which was specified by user)
and it's sub folders. Statistics by words count is gathering
for each text file was found. Then a histogram will be building
by this statistics.

# Example #
![](examples/histogram.png)

# Installation #
* #### Using a `*.whl` dist: ####
* Download the latest `*.whl` version from a
[releases page](https://github.com/jim-molecule/histogramer/releases)
* Install histogramer: `pip3 install --upgrade path_to_wheel.whl`

* #### Using sources: ####
* Remove dist files from project root:
* Windows: `RMDIR /Q/S build dist histogramer.egg-info`
* Mac: `rm -r build dist histogramer.egg-info`
* Install wheel: `pip3 install wheel`
* Build dist: `python setup.py bdist_wheel`
* Install histogramer: `pip3 install --upgrade path_to_wheel.whl`

# Issues #
Please, report about any issues to an
[issues page](https://github.com/jim-molecule/histogramer/issues/new/choose)
with `~/.logs` folder's files attached.

# Testing #
For run all tests, please, use `pytest ./histogramer/tests` from project root.
Pytest options are placed in `~/histogramer/tests/pytest.ini` file.

# Usage #
Run a `python -m histogramer --help` script.

# Virtual environment #
For main usage `~/requirements_main.txt` should be installed.
For testing: `~/requirements_tests.txt`.
1 change: 0 additions & 1 deletion _config.yml

This file was deleted.

Binary file added examples/histogram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions histogramer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Init for 'histogramer' python package.
"""
22 changes: 22 additions & 0 deletions histogramer/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""
Histogramer main module.
"""
import asyncio

from histogramer.src.helpers.args_helper import parse_arguments
from histogramer.src.helpers.log_helper import init_logger
from histogramer.src.histogram import process_text_files, show_histogram


async def main():
"""
Run Histogrammer.
:return: None.
"""
arguments = await parse_arguments()
logger = await init_logger(folder_name=".logs", root_path=arguments.log)
words_count = await process_text_files("*.txt", logger, arguments.path)
await show_histogram(logger, words_count)


{"__main__": lambda: asyncio.run(main())}.get(__name__, lambda: None)()
3 changes: 3 additions & 0 deletions histogramer/src/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Init for 'histogramer' python package.
"""
3 changes: 3 additions & 0 deletions histogramer/src/helpers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Init for 'helpers' python package.
"""
56 changes: 56 additions & 0 deletions histogramer/src/helpers/args_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""
Helps to work with argument parser.
"""
import argparse
import os


def __raise_error(path):
"""
Raise NotADirectoryError.
:param path: Directory which should exists.
:return: None.
"""
raise NotADirectoryError(f"directory '{path}' not exists")


def get_dir_type(path):
"""
Validate that directory exists.
:param path: Directory which should exists.
:return: Path or NotADirectoryError if directory not exists.
"""
{False: lambda: __raise_error(path)}.get(
os.path.isdir(path) or path == "0", lambda: None)()
return path


async def parse_arguments(raw_args=None):
"""
Parse arguments.
:param raw_args: Arguments for arg parser.
:return: Parsed arguments.
"""
parser = argparse.ArgumentParser(description="please, provide root path"
" in which (and it's sub "
"folders) text files "
"will be processed for "
"histogram building")
parser.add_argument("-p",
action="store",
default="",
dest="path",
help="root path in which (and it's sub "
"folders) text files will be processed",
required=True,
type=get_dir_type)
parser.add_argument("-l",
action="store",
default=os.getcwd(),
dest="log",
help="path to store logs. Use '0' "
"if you don't want to store them. "
"Default value: ~/.logs/",
required=False,
type=get_dir_type)
return parser.parse_args(raw_args)
23 changes: 23 additions & 0 deletions histogramer/src/helpers/datetime_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
Helps to work with datetime objects.
"""
from datetime import timedelta


async def datetime_to_str(datetime_obj):
"""
Convert datetime object to formatted string.
:param datetime_obj: Datetime object.
:return: Formatted string.
"""
return datetime_obj.isoformat(sep=" ", timespec="milliseconds")


async def get_duration(start, end):
"""
Get event duration.
:param start: Datetime when an event started.
:param end: Datetime when an event finished.
:return: Time period as formatted string.
"""
return round(number=timedelta.total_seconds(end - start), ndigits=3)
60 changes: 60 additions & 0 deletions histogramer/src/helpers/log_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""
Helps to work with console & file logger.
"""
import logging
import os
import shutil
from logging.handlers import RotatingFileHandler
from pathlib import Path


def _add_rotating_file_handler(folder_name, logger, log_formatter, root_path):
"""
Add rotating file handler to the logger instance for logging to a file.
:param folder_name: Name of the folder where logs will be stored.
:param logger: Instance of logger.
:param log_formatter: Format of log messages.
:param root_path: Path to the log folder.
:return: None.
"""
path = os.path.join(root_path, folder_name)
file_name = os.path.join(path, ".histogramer")
{True: lambda: shutil.rmtree(path, ignore_errors=True)}.get(
os.path.isdir(file_name)
and Path(file_name).stat().st_size >= 5 * (1024 ** 2),
lambda: None)()
# create folder for file logs if not exists
Path(path).mkdir(parents=True, exist_ok=True)

rotating_file_handler = RotatingFileHandler(filename=file_name)
rotating_file_handler.setFormatter(fmt=log_formatter)
rotating_file_handler.setLevel(level=logging.INFO)
logger.addHandler(hdlr=rotating_file_handler)


async def init_logger(folder_name, root_path):
"""
Configure logger for logging events in console (and in a file, optional).
:param folder_name: Name of the folder where logs will be stored.
:param root_path: Path to the log folder.
:return: Instance of logger.
"""
log_formatter = logging.Formatter("[%(asctime)s] "
"[%(threadName)s] "
"[%(levelname)s] "
"%(message)s")
logger = logging.getLogger()
logger.setLevel(level=logging.INFO)

console_handler = logging.StreamHandler()
console_handler.setFormatter(fmt=log_formatter)
console_handler.setLevel(level=logging.ERROR)
logger.addHandler(hdlr=console_handler)

{"0": lambda: None}.get(
root_path,
lambda: _add_rotating_file_handler(folder_name,
logger,
log_formatter,
root_path))()
return logger
16 changes: 16 additions & 0 deletions histogramer/src/helpers/random_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
Helps to generate random objects.
"""
import secrets
import string


async def get_random_string(string_length=10):
"""
Generate a random string of fixed length.
:param string_length: Length of string.
:return: Random string of fixed length.
"""

return "".join(secrets.choice(string.ascii_lowercase)
for _ in range(string_length))
Loading

0 comments on commit 5b48ef9

Please sign in to comment.