Add some private projects

This commit is contained in:
bt3gl 2022-03-23 18:25:34 +04:00
parent c8cd5cdbc4
commit 07aa882d51
80 changed files with 5216 additions and 41 deletions

0
art_and_logic/.gitkeep Normal file
View file

70
art_and_logic/1-efun/.gitignore vendored Normal file
View file

@ -0,0 +1,70 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# pyenv,
.python-version
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# mypy
.mypy_cache/
# Others
.DS_Store

Binary file not shown.

View file

@ -0,0 +1,28 @@
.PHONY: setup install clean test lint
default: test
setup:
pip install -r requirements.txt
install:
python setup.py install
clean:
@find . -type f -name '*.pyc' -delete
@find . -type d -name '__pycache__' | xargs rm -rf
@find . -type d -name '*.ropeproject' | xargs rm -rf
@rm -rf build/
@rm -rf dist/
@rm -rf venv/
@rm -f src/*.egg*
@rm -f MANIFEST
@rm -rf docs/build/
@rm -f .coverage.*
test:
@tox -- -s
lint:
@tox -e lint

View file

@ -0,0 +1,80 @@
# Efun: Art + Logic Enconding + Decoding
This program i) converts and encodes a 14-bit decimal input value to a 2-byte hexadecimal, ii) decodes hexadecimal representations to 14-bit decimal (as described in [this doc](https://github.com/bt3gl/PRIV-Interview_take_home_projects/blob/master/art_and_logic/1-efun/AlpcPart1-190801.pdf).
## Installing
Create and source virtual enviroment. You can use [virtualenv](https://virtualenv.pypa.io/en/latest/) or [conda](https://docs.conda.io/en/latest/):
```bash
virtualenv venv
source venv/bin/activate
```
Install dependencies:
```bash
make setup
```
Install Efun:
```bash
make install
```
## Usage
### Encoding
To enconde a integer, run:
```bash
efun -e <integer>
```
Note that the value must be in the range `[-8192, 8191]`.
#### Decoding
To decode an integer, run:
```bash
efun -d <integer>
```
Note that the value must be in the range `[0x0000, 0x7F7F]`.
## Developer
### Running tests
You can run tests with:
```bash
make test
```
### Linting
You can lint your code with:
```bash
make lint
```
### Cleaning up
Clean residual compilation and installation files with:
```bash
make clean
```
----
Thank you for reading my code!

View file

@ -0,0 +1,13 @@
# Encoding function
6111 -> 6f5f
340 -> 4254
-2628 -> 2b3c
-255 -> 3e01
7550 -> 7a7e
# Decoding function
0A0A -> -6902
0029 -> -8151
3F0F -> -113
4400 -> 512
5E7F -> 3967

View file

@ -0,0 +1,41 @@
#!/usr/bin/env python
import unittest
import src.main as m
class EfunTest(unittest.TestCase):
def setUp(self):
self.e = m.Efun()
self.test_data = [
(-4096, 0x2000, (0x20, 0x00)),
(-8192, 0x0000, (0x00, 0x00)),
(0, 0x4000, (0x40, 0x00)),
(2048, 0x5000, (0x50, 0x00)),
(8191, 0x7F7F, (0x7F, 0x7F)),
(6111, 0x6F5F, (0x6F, 0x5F)),
(340, 0x4254, (0x42, 0x54)),
(-2628, 0x2B3C, (0x2B, 0x3C)),
(-255, 0x3E01, (0x3E, 0x01)),
(-6902, 0x0A0A, (0x0A, 0x0A)),
(-8151, 0x0029, (0x00, 0x29)),
(-113, 0x3F0F, (0x3F, 0x0F)),
(512, 0x4400, (0x44, 0x00)),
(3967, 0x5E7F, (0x5E, 0x7F)),
]
def test_extract(self):
for item in self.test_data:
self.assertEqual(self.e._extract_two_bytes(item[1]), item[2])
def test_decode(self):
for item in self.test_data:
by1, by2 = self.e._extract_two_bytes(item[1])
self.e._decode(by1, by2)
self.assertEqual(self.e.dec_byte, item[0])
def test_encode(self):
for item in self.test_data:
self.e._encode(item[0])
self.assertEqual(self.e.enc_byte, hex(item[1]))

View file

@ -0,0 +1,2 @@
tox==3.14.3
virtualenv==20.0.1

View file

@ -0,0 +1,15 @@
from setuptools import setup, find_packages
setup(
name='efun',
version='0.0.1',
packages=find_packages(),
include_package_data=True,
author='Mia von Steinkirch',
install_requires=[
],
entry_points='''
[console_scripts]
efun=src.main:main
''',
)

View file

@ -0,0 +1 @@
# -*- coding: utf8 -*-

View file

@ -0,0 +1,154 @@
#!/usr/bin/env python
import argparse
class Efun(object):
def __init__(self):
self.enc_byte = None
self.dec_byte = None
self.enc_range = [-8192, 8191]
self.dec_range = [0x0000, 0x7F7F]
def _decode(self, higher_byte, lower_byte):
"""
Converts encoded hex value back into the original signed integer.
Argument:
higher_byte {string} -- higher byter hex input to be decoded.
lower_byte {string} -- lower byter hex input to be decoded.
Returns:
Decoded value string in decimal base.
"""
# Left shift 7 to get higher byte.
higher_byte <<= 7
# 'OR' byte withing the decimal lower limit.
self.dec_byte = (lower_byte | higher_byte) + self.enc_range[0]
def _encode(self, value):
"""
Gets a signed integer and return a 4 character string,
such that after the encoding is complete, the most
significant bit of each byte has been cleared.
Argument:
value {string} -- integer input value to be encoded.
Returns:
Encoded 2-byte string.
"""
# Add 8192 for unsigned
sign_int = int(value)
unsign_int = sign_int + 8192
# 'AND' with 0b0000000001111111 for lower byte and
# 'AND' with 0b0011111110000000 for higher byter
lower_byte = unsign_int & 0x007F
higher_byte = ((unsign_int & 0x3F80) << 1)
# Return hex enconding of sum of the two bytes
self.enc_byte = hex(lower_byte + higher_byte)
def _extract_two_bytes(self, value):
"""
Extract two bytes from a hexadecimal string.
Arguments:
value {hex} -- hexadecimal between 0x0000 and 0x7F7F.
Returns:
higher_byte, lower_byte -- byte strings.
"""
# 'And' with 0b0000000001111111 for lower byte
lower_byte = value & 0x00FF
# Left shift 8 positions for higher byte
higher_byte = value >> 8
return higher_byte, lower_byte
def run_decode(self, value):
"""
Verifies if an input is valid 2-byte hexadecimal,
returning its decoded value.
Arguments:
value {string} -- hexadecimal input value.
Returns:
None.
"""
# Convert to hexadecimal.
try:
value = int(value, 16)
except ValueError as e:
print('Could not convert value to hexadecimal: {}.'.fomart(e))
return -1
# Verify whether the value is within the range,
# and then decode it.
if self.dec_range[0] <= value <= self.dec_range[1]:
self._decode(*self._extract_two_bytes(value))
print('{0} decodes as {1}'.format(hex(value), self.dec_byte))
else:
print('Value {0} is out of range of [{1}, {2}]'.format(hex(value),
hex(self.dec_range[0], hex(self.dec_range[1]))))
def run_encode(self, value):
"""
Verifies whether an input is valid 14-bit integer,
returning its encoded value.
Arguments:
value {byte} -- 14-bit signed integer.
Returns:
None.
"""
int_value = int.from_bytes(value, byteorder='big', signed=True)
if self.enc_range[0] <= int_value <= self.enc_range[1]:
self._encode(int_value)
print('Value {0} encodes as {1}'.format(int_value, self.enc_byte))
else:
print('Value {0} out of range: {1}'.format(int_value, self.enc_byte))
def main():
# Creater an instance of Efun().
e = Efun()
# Set strings for Argparse.
description = 'Efun is an Art + Logic Enconding + Decoding application.'
encode_help = 'Enter an INT value in the range {}.'.format(e.enc_range)
decode_help = 'Enter an HEX value in the range {}.'.format(e.dec_range)
# Run CLI menu.
parser = argparse.ArgumentParser(description=description)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-e', '--encode', type=int, help=encode_help)
group.add_argument('-d', '--decode', help=decode_help)
args = parser.parse_args()
# Get the 14-byte input, enconde/decode it, and print results.
bit_range = 14
if args.encode:
sig_bytes = args.encode.to_bytes(bit_range, byteorder='big', signed=True)
e.run_encode(sig_bytes)
else:
e.run_decode(args.decode)
if __name__ == "__main__":
main()

View file

@ -0,0 +1 @@
# -*- coding: utf8 -*-

View file

@ -0,0 +1,12 @@
[tox]
envlist = py27
[testenv]
deps = pytest
commands =
pytest
[testenv:lint]
skip_install = true
deps = flake8
commands = flake8 src/

View file

@ -0,0 +1,5 @@
LOG_LEVEL = INFO
LOG_FORMAT = %(levelname)s: %(message)s
BOUNDARY_LIMIT_ENC = -8192, 8191
BOUNDARY_LIMIT_DEC = 0x0000, 0x7F7F
INPUT_STREAM_FILE = './data/InputStream.txt'

71
art_and_logic/2-epen/.gitignore vendored Normal file
View file

@ -0,0 +1,71 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# pyenv,
.python-version
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# mypy
.mypy_cache/
# Others
.DS_Store
.env

Binary file not shown.

View file

@ -0,0 +1,29 @@
.PHONY: setup install clean test lint run
default: test
setup:
pip install -r requirements.txt
install:
python setup.py install
run:
epen
clean:
@find . -type f -name '*.pyc' -delete
@find . -type d -name '__pycache__' | xargs rm -rf
@find . -type d -name '*.ropeproject' | xargs rm -rf
@rm -rf build/
@rm -rf dist/
@rm -rf *.egg*
@rm -f MANIFEST
@rm -rf docs/build/
@rm -f .coverage.*
test:
@tox -- -s
lint:
@tox -e lint

View file

@ -0,0 +1,143 @@
# EPen: Art + Logic Pen Language
This program creates an interface for a digital pen for drawing, as described on [this document](https://github.com/bt3gl/PRIV-Interview_take_home_projects/blob/master/art_and_logic/2-epen/AlpcPart2-190801.pdf).
----
## Installing
Create and source virtual enviroment. You can use [virtualenv](https://virtualenv.pypa.io/en/latest/) or [conda](https://docs.conda.io/en/latest/):
```bash
virtualenv venv
source venv/bin/activate
```
Create and customize an enviroment file:
```bash
cp .env_example .env
vim .env
```
Install dependencies:
```bash
make setup
```
Install EPen:
```bash
make install
```
-----
## Usage
Edit the stream data to be read by the program:
```
vim data/InputStream.txt
```
Run this program with:
```bash
make run
```
or simply
```bash
epen
```
These commands run `./src/main.py`, which calls a class called `Epen()`.
This is the main class that defines all the functionalities of this application.
-----
## Examples
The stream:
```bash
F0A04000417F4000417FC040004000804001C05F205F20804000
```
prints
```bash
CLR;
CO 0 255 0 255;
MV (0, 0);
PEN DOWN;
MV (4000, 4000);
PEN UP;
```
The stream:
```bash
F0A0417F40004000417FC067086708804001C0670840004000187818784000804000
```
prints
```bash
CLR;
CO 255 0 0 255;
MV (5000, 5000);
PEN DOWN;
MV (8191, 5000);
PEN UP;
MV (8191, 0);
PEN DOWN;
MV (5000, 0);
PEN UP;
```
And the stream:
```bash
F0A040004000417F417FC04000400080400047684F5057384000804001C05F204000400001400140400040007E405B2C4000804000
```
prints
```bash
CLR;
CO 0 0 255 255;
MV (0, 0);
PEN DOWN;
MV (4000, 0) (4000, -8000) (-4000, -8000) (-4000, 0) (-500, 0);
PEN UP;
(base)
```
## Developer
### Linting
You can lint the code with:
```bash
make lint
```
### Cleaning up
Clean residual compilation and installation files with:
```bash
make clean
```
----
Thank you for reading my code!

View file

@ -0,0 +1,13 @@
# Encoding function
6111 -> 6f5f
340 -> 4254
-2628 -> 2b3c
-255 -> 3e01
7550 -> 7a7e
# Decoding function
0A0A -> -6902
0029 -> -8151
3F0F -> -113
4400 -> 512
5E7F -> 3967

View file

@ -0,0 +1,3 @@
#F0A04000417F4000417FC040004000804001C05F205F20804000
#F0A0417F40004000417FC067086708804001C0670840004000187818784000804000
F0A040004000417F417FC04000400080400047684F5057384000804001C05F204000400001400140400040007E405B2C4000804000

View file

@ -0,0 +1,4 @@
tox==3.14.3
virtualenv==20.0.1
python-dotenv==0.11.0
numpy==1.18.1

View file

@ -0,0 +1,13 @@
from setuptools import setup, find_packages
setup(
name='epen',
version='0.0.1',
packages=find_packages(),
include_package_data=True,
author='Mia von Steinkirch',
entry_points='''
[console_scripts]
epen=src.main:main
''',
)

View file

@ -0,0 +1,304 @@
# -*- coding: utf8 -*-
import sys
import logging as l
import src.encoder as enc
from src.utils import get_boundary_limits
class EPen(object):
def __init__(self):
self.boundary = enc.get_boundary_limits('enc')
self.color = None
self.pen_location = None
self.is_pen_down = None
def _parse_clear(self):
"""
Runs clear operations and prints CLEAR STDOUT.
"""
self.pen_location = [0, 0]
self.color = [0, 0, 0, 255]
self.is_pen_down = False
print('CLR;')
def _parse_pen_state(self, substr):
"""
Sets pen to be UP or DOWN and prints PEN STATE STDOUT.
Arguments:
substr {str} -- parameters for this opcode, which is
either 0 (for pen up) or any other (for pen down)
"""
if substr:
pen_state = enc.run_decoder(substr)
else:
l.error('Could not parse pen state string {}'.format(substr))
if pen_state == 0:
if self.is_pen_down:
self.is_pen_down = False
print('PEN UP;')
else:
if not self.is_pen_down:
self.is_pen_down = True
print('PEN DOWN;')
def _parse_color(self, substr):
"""
Sets pen color and prints COLOR SET STDOUT.
Arguments:
substr {str} -- RGBA parameters for this opcode,
which is represented by four integers in the range
[0, 255] defining colors (red, green, blue) and alpha.
"""
red = enc.run_decoder(substr[:4])
green = enc.run_decoder(substr[4:8])
blue = enc.run_decoder(substr[8:12])
alpha = enc.run_decoder(substr[12:16])
self.color = [red, green, blue, alpha]
print('CO {0} {1} {2} {3};'.format(*self.color))
def _parse_pen_move(self, substr):
"""
Parses a substring of parameters and usex this data
as pair of coordinates to move the pen inside the
square boundary limit.
Arguments:
substr {str} -- (x,y) pairs with directions to where
the pen should move to (at least one pair).
Returns:
i - an integer indicating how much of the input
substring was used in this opcode.
"""
i = 0
move_list = []
while i < len(substr):
try:
# Ends if find another opcode.
if substr[i:i+2] in set(['F0', '80', 'A0']):
break
if len(substr[i:]) > 8:
x_coords = enc.run_decoder(substr[i:i+4])
y_coords = enc.run_decoder(substr[i+4:i+8])
move_list.append([x_coords, y_coords])
i += 8
else:
break
except (KeyError, ValueError) as e:
l.error('Ill-formated MV data: {}'.format(e))
self._move_pen(move_list)
return i
def _print_mv(self, cmd_list=None):
"""
Prints MV PEN STDOUT. If a string with a list of commands
is given, print that instead of pen_location.
Arguments:
cmd_list {list (optional)} -- a list with pairs of
(x, y) coordinates.
"""
if cmd_list:
cmd_str = ' '.join(cmd_list)
print('MV {};'.format(cmd_str))
else:
print('MV ({0}, {1});'.format(self.pen_location[0], self.pen_location[1]))
def _fix_boundary(self, coord):
"""
Check if the coordinate exceeds
the boundary limit, returning this
value in that case.
Arguments:
coord {int} -- x or y coordinate.
Returns:
An integer representing the fixed coordinate.
"""
if coord < self.boundary[0]:
return self.boundary[0]
elif coord > self.boundary[1]:
return self.boundary[1]
else:
return coord
def _fix_boundaries(self, x, y):
"""
Checks if a set of (x, y) coordinates
exceeds the boundary limits, returning
a fixed (x, y) pair.
Arguments:
coord {list} -- (x, y) coordinates.
Returns:
A (x, y) pair representing the fixed coordinates.
"""
return self._fix_boundary(x), self._fix_boundary(y)
def _is_inside_boundary(self, xymove=None):
"""
Checks whether a set of coordinate is inside
of the boundary, returning True if the case,
or False otherwise.
Arguments:
xymove {tuple} - (x, y) tuple.
Returns:
True if the coordinates were fixed, False
otherwise.
"""
if xymove is None:
xymove = self.pen_location
x_fix = self._fix_boundary(xymove[0])
y_fix = self._fix_boundary(xymove[1])
return (xymove[0] == x_fix and xymove[1] == y_fix)
def _is_on_the_boundary(self, xymove=None):
"""
Checks whether a coordinate is on the boundary,
returning True if the case, or False otherwise.
Arguments:
xymove {tuple} - (x, y) tuple.
Returns:
True if any of the (x, y) coordinates are
on the boundary, False otherwise.
"""
if xymove is None:
x, y = self.pen_location
return x == self.boundary[0] or x == self.boundary[1] or \
y == self.boundary[0] or y == self.boundary[1]
def _get_next_position(self, xymove):
"""
Givens a move coordinates (x, y), returns the next
destination coordinate for the pen.
Arguments:
xymove {tuple} - (x, y) tuple.
Returns:
The next (x, y) coordinates to move the pen to.
"""
return self.pen_location[0] + xymove[0], \
self.pen_location[1] + xymove[1]
def _move_pen(self, move_list):
"""
Changes the location of the pen relative to its current location.
Arguments:
move_list {list} -- list of coordinates to move the pen.
"""
# Loop over the list of (x ,y) moves, adding them to
# a dictionary, together with a boolean False if x or y
# is outside the limits.
tablet_dict = {}
for n, xymove in enumerate(move_list, 1):
self.pen_location = self._get_next_position(xymove)
if self._is_inside_boundary():
tablet_dict[n] = [self.pen_location, True]
else:
tablet_dict[n] = [self.pen_location, False]
# Find the positions and print it as pen down.
if self.is_pen_down:
cmd_list = []
for i in sorted(tablet_dict):
x = tablet_dict[i][0][0]
y = tablet_dict[i][0][1]
is_inside = tablet_dict[i][1]
if is_inside:
cmd_list.append('({0}, {1})'.format(x, y))
else:
break
if cmd_list:
self._print_mv(cmd_list)
# It broke the boundary at some point.
if i < len(tablet_dict):
for item in range(i, len(tablet_dict)+1):
coords, is_inside = tablet_dict[item]
self.pen_location = self._fix_boundaries(coords[0], coords[1])
self._print_mv()
if item != len(tablet_dict):
if not is_inside:
if self.is_pen_down:
self._parse_pen_state('4000')
else:
self._parse_pen_state('4001')
else:
coords, is_inside = tablet_dict[i]
self.pen_location = self._fix_boundaries(coords[0], coords[1])
if not is_inside:
self._parse_pen_state('4000')
# Pen is up.
else:
last_position = tablet_dict[sorted(tablet_dict)[-1]][0]
self.pen_location = self._fix_boundaries(*last_position)
self._print_mv()
def parse_stream(self, stream_str):
"""
Parses a stream string and run its commands and their
adjacent parameters.
Arguments:
stream_str {str} - data stream of actions be taken.
"""
i = 0
while i < len(stream_str):
# Opcode is CLEAR.
if stream_str[i:i+2] == 'F0':
l.debug('Parsing clear opcode...')
self._parse_clear()
i += 2
# Opcode is SET PEN STATE.
elif stream_str[i:i+2] == '80':
l.debug('Parsing pen state opcode...')
self._parse_pen_state(stream_str[i+2:i+6])
i += 6
# Opcode is SET COLOR.
elif stream_str[i:i+2] == 'A0':
l.debug('Parsing color opcode...')
self._parse_color(stream_str[i+2:i+18])
i += 18
# Opcode is MOVE PEN.
elif stream_str[i:i+2] == 'C0':
l.debug('Parsing pen move opcode...')
i += 2 + self._parse_pen_move(stream_str[i+2:])
# Opcode is not identified.
else:
l.debug('Non-identificated char: {}'.format(stream_str[i]))
i += 1

View file

@ -0,0 +1,2 @@
# -*- coding: utf8 -*-

View file

@ -0,0 +1,130 @@
# -*- coding: utf8 -*-
import logging as l
from src.utils import get_boundary_limits
def decoder(higher_byte, lower_byte):
"""
Converts encoded hex value back into the original signed integer.
Argument:
higher_byte {string} -- higher byter hex input to be decoded.
lower_byte {string} -- lower byter hex input to be decoded.
Returns:
Decoded value string in decimal base.
"""
min_lim, _ = get_boundary_limits('enc')
# Left shift 7 to get higher byte.
higher_byte <<= 7
# 'OR' byte withing the decimal lower limit.
encoding_range = get_boundary_limits
return (lower_byte | higher_byte) + min_lim
def encoder(value):
"""
Gets a signed integer and return a 4 character string,
such that after the encoding is complete, the most
significant bit of each byte has been cleared.
Argument:
value {string} -- integer input value to be encoded.
Returns:
Encoded 2-byte string.
"""
# Add 8192 for unsigned
_, max_lim = get_boundary_limits('enc')
sign_int = int(value)
unsign_int = sign_int + max_lim + 1
# 'AND' with 0b0000000001111111 for lower byte and
# 'AND' with 0b0011111110000000 for higher byter
lower_byte = unsign_int & 0x007F
higher_byte = ((unsign_int & 0x3F80) << 1)
# Return hex encoding of the sum of the two bytes
return hex(lower_byte + higher_byte)
def extract_two_bytes(value):
"""
Extract two bytes from a hexadecimal string.
Arguments:
value {hex} -- hexadecimal between 0x0000 and 0x7F7F.
Returns:
higher_byte, lower_byte -- byte strings.
"""
# 'And' with 0b0000000001111111 for lower byte
lower_byte = value & 0x00FF
# Left shift 8 positions for higher byte
higher_byte = value >> 8
return higher_byte, lower_byte
def run_decoder(input_hex):
"""
Verifies if an input is valid 2-byte hexadecimal,
returning its decoded value.
Arguments:
value {string} -- hexadecimal input value.
Returns:
Decoded byte or -1 (if error).
"""
min_lim, max_lim = get_boundary_limits('dec')
# Convert to hexadecimal.
try:
l.debug('Converting {} to decimal...'.format(input_hex))
input_dec = int(input_hex, 16)
except ValueError as e:
l.error('Could not convert {0} decimal: {1}'.format(input_hex, e))
return -1
# Verify whether the input is within the range,
# and then decode it.
if min_lim <= input_dec <= max_lim:
dec_byte = decoder(*extract_two_bytes(input_dec))
l.debug('{0} decodes as {1}'.format(input_dec, dec_byte))
return dec_byte
else:
l.debug('{0} is out of range of [{1}, {2}].'.format(input_dec,
hex(min_lim), hex(max_lim)))
return -1
def run_encoder(value):
"""
Verifies whether a byte input is a valid 14-bit integer,
returning its encoded value.
Arguments:
value {byte} -- 14-bit signed integer.
Returns:
Encoded byte or -1 (if error).
"""
int_value = int.from_bytes(value, byteorder='big', signed=True)
min_lim, max_lim = get_boundary_limits('enc')
if min_lim <= int_value <= max_lim:
enc_byte = encoder(int_value)
l.debug('Value {0} encodes as {1}'.format(value, enc_byte))
return enc_byte
else:
l.error(int('Value {0} out of range: {1}'.format(value, enc_byte)))
return -1

View file

@ -0,0 +1,28 @@
#!/usr/bin/env python
import logging as l
import src.Epen as Epen
import src.utils as utils
from src.settings import LOG_LEVEL, LOG_FORMAT, INPUT_STREAM_FILE
# Define log level: choose between DEBUG or INFO.
l.basicConfig(level=LOG_LEVEL, format=LOG_FORMAT)
def main():
p = Epen.EPen()
# Get input stream from file.
input_stream = utils.parse_input_stream(INPUT_STREAM_FILE)
# Extract and run list of commands from stream input.
if input_stream:
p.parse_stream(input_stream)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,16 @@
# Load all env variables from .env file
import os
from dotenv import load_dotenv
from pathlib import Path
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)
# General constants
LOG_LEVEL = os.getenv('LOG_LEVEL')
LOG_FORMAT = os.getenv('LOG_FORMAT')
BOUNDARY_LIMIT_ENC = os.getenv('BOUNDARY_LIMIT_ENC')
BOUNDARY_LIMIT_DEC = os.getenv('BOUNDARY_LIMIT_DEC')
INPUT_STREAM_FILE = os.getenv('INPUT_STREAM_FILE')

View file

@ -0,0 +1,54 @@
# -*- coding: utf8 -*-
import os
import sys
import logging as l
from src.settings import BOUNDARY_LIMIT_ENC, BOUNDARY_LIMIT_DEC
def parse_input_stream(filename):
"""
Loads a file with the input stream with
encoded commands.
Returns:
filename -- location of the file in disk.
"""
try:
with open(filename, 'r') as f:
lines = f.readlines()
for input_stream in lines:
if input_stream[0] != '#':
l.debug('Input stream:\n{}'.format(input_stream))
return input_stream.strip()
except (KeyError, OSError, TypeError) as e:
l.error('Could not open input stream file {0}: {1}.'.format(filename, e))
sys.exit(0)
def get_boundary_limits(lim_base):
"""
Extract boundary limits from env file, returning two integers
representing these limits, either in the encoded or
the decode base.
Arguments:
lim_base {string}: 'enc' for the encoded limit
representation or 'dec' for the encoded limit
representation.
"""
try:
if lim_base == 'enc':
enc_range = tuple(BOUNDARY_LIMIT_ENC.split(', '))
return int(enc_range[0]), int(enc_range[1])
elif lim_base == 'dec':
dec_range = tuple(BOUNDARY_LIMIT_DEC.split(', '))
return int(dec_range[0], 16), int(dec_range[1], 16)
except (KeyError, ValueError, AttributeError) as e:
l.error('Could not extract boundary limits from .env file: {}'.format(e))
return -1

View file

@ -0,0 +1,12 @@
[tox]
envlist = py27
[testenv]
deps = pytest
commands =
pytest
[testenv:lint]
skip_install = true
deps = flake8
commands = flake8 src/