Skip to content

Commit

Permalink
Merge pull request #28 from ShaneSutro/issues/formatter-warnings
Browse files Browse the repository at this point in the history
Add error when instantiating `Board()` with a string
  • Loading branch information
ShaneSutro authored Jun 18, 2022
2 parents 5f6f328 + 04bdf83 commit 370fef8
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 75 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
language: python
python:
- '3.6'
- '3.7'
- '3.8'
- '3.9'

install:
- pip install setuptools==60.8.2
- pip install -r requirements.txt
- pip install .

script: pytest
script: pytest
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [v1.0.1](https://github.com/SonicRift/Vestaboard/releases/tag/v1.0.1) - 2022-06-18

<small>[Compare with v1.0.0](https://github.com/SonicRift/Vestaboard/compare/v1.0.0...v1.0.1)</small>


## [v1.0.0](https://github.com/SonicRift/Vestaboard/releases/tag/v1.0.0) - 2022-01-07

<small>[Compare with v0.5.0](https://github.com/SonicRift/Vestaboard/compare/v0.5.0...v1.0.0)</small>


## [v0.5.0](https://github.com/SonicRift/Vestaboard/releases/tag/v0.5.0) - 2021-02-22

<small>[Compare with v0.4.0](https://github.com/SonicRift/Vestaboard/compare/v0.4.0...v0.5.0)</small>


## [v0.4.0](https://github.com/SonicRift/Vestaboard/releases/tag/v0.4.0) - 2021-02-12

<small>[Compare with v0.3.1](https://github.com/SonicRift/Vestaboard/compare/v0.3.1...v0.4.0)</small>


## [v0.3.1](https://github.com/SonicRift/Vestaboard/releases/tag/v0.3.1) - 2021-02-10

<small>[Compare with v0.2.0-beta](https://github.com/SonicRift/Vestaboard/compare/v0.2.0-beta...v0.3.1)</small>


## [v0.2.0-beta](https://github.com/SonicRift/Vestaboard/releases/tag/v0.2.0-beta) - 2021-02-09

<small>[Compare with first commit](https://github.com/SonicRift/Vestaboard/compare/049afbf9bbb45f5e6b5b797023729471ab6e653f...v0.2.0-beta)</small>


23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
![GitHub commit activity](https://img.shields.io/github/commit-activity/m/SonicRift/Vestaboard)
![GitHub contributors](https://img.shields.io/github/contributors/SonicRift/Vestaboard)

This is a lightweight and unassuming wrapper for the Vestaboard API. This project is open source and no payment is necessary to use - project donations are always appreciated to help fund this effort. If interested, you can [ view the donation page here.](https://shanesutro.com/donate)
This is a lightweight and unassuming wrapper for the Vestaboard API. This project is open source and no payment is necessary to use - project donations are always appreciated to help fund this effort. If interested, you can [view the donation page here.](https://shanesutro.com/donate)

By [Shane Sutro][] and [contributors](https://github.com/SonicRift/Vestaboard/graphs/contributors)

Expand All @@ -37,15 +37,15 @@ Once created, you will need to store your API Key and API Secret - you'll need t
- Via `pip`:

```pip3 install vestaboard```
_Note: if using a virtual environment, use `pip` instead of `pip3`_
*Note: if using a virtual environment, use `pip` instead of `pip3`*

#### Usage

This package will simplify the process of connecting your code to Vestaboard's API.
By default, the module will store your API Key, API Secret, and Subscriber ID in a .txt file in the root folder of the project.
If you do _not_ want to store this, pass `saveCredentials=False` into the creation of an `Installable`. Alternatively, you may skip creating an `Installable` alltogether if you already know your Subscription ID (which you can get from Vestaboards official portal if you'd like to skip this step).
If you do *not* want to store this, pass `saveCredentials=False` into the creation of an `Installable`. Alternatively, you may skip creating an `Installable` alltogether if you already know your Subscription ID (which you can get from Vestaboards official portal if you'd like to skip this step).

If you do **_not_** know your Subscription ID call `Installable()` with your API Key and API Secret to find and store it:
If you do *not* know your Subscription ID call `Installable()` with your API Key and API Secret to find and store it:
```python
import vestaboard
#This will print your subscription ID, and store all keys in 'credentials.txt'
Expand Down Expand Up @@ -87,7 +87,7 @@ Currently this module supports the following:

- Creating an instance of Board by passing in one of the following:
- An Installable, instantiated with API Key and API Secret
- By passing in an API Key, API Secret _and_ Subscription ID directly to `Board()`
- By passing in an API Key, API Secret *and* Subscription ID directly to `Board()`
- By passing in an Installable where `getSubscription=False` and manually providing the Subscription ID to `Board`.

The board currently has 2 methods available, the `.post()` method, which takes in a string and sends it to the board, and the `.raw()` method, which allows you to place characters precisely where you'd like them.
Expand All @@ -105,7 +105,7 @@ vboard.post('Everything you can imagine is real.')

The `.post()` method supports all letters and symbols that Vestaboard supports, including all letters, numbers, and symbols.
In addition, you may pass in a character code in curly brackets to represent a single character or a color tile. You can view a reference of character and color codes on [Vestaboard's official website by clicking here.](https://docs.vestaboard.com/characters)
Vestaboard's API currently strips leading and trailing spaces from lines - _this includes the `{0}` character (the black tile)_. To precisely place characters, use the `.raw()` method (see below).
Vestaboard's API currently strips leading and trailing spaces from lines - *this includes the `{0}` character (the black tile)*. To precisely place characters, use the `.raw()` method (see below).

```python
import vestaboard
Expand Down Expand Up @@ -141,11 +141,11 @@ vboard.raw(characters)
![Board with raw input example](../media/rawexample.png?raw=true)

### New in Version 1.0.0
The `.raw()` method now supports padding and truncating if more or fewer than 6 lines are provided! By default, your text will be centered vertically on the board, but will generate a warning (if an odd number of lines are provided, the additional line will be at the bottom). Supress this warning by passing in `pad='center'`. When passing in greater than 6 lines, the board will only display the first 6 lines.
The `.raw()` method now supports padding and truncating if more or fewer than 6 lines are provided! By default, your text will be centered vertically on the board, but will generate a warning (if an odd number of lines are provided, the additional line will be at the bottom). Supress this warning by passing in `pad='center'`. When passing in greater than 6 lines, the board will only display the first 6 lines, removing lines from the bottom.

You can also specify whether you'd like the padding to be added above or below your text by passing in `pad='top'` or `pad='bottom'` (only available when passing in < 6 lines). `pad='top'` will add padding above your text (your text will be at the bottom of the board), and `pad='bottom'` will add padding below your text (your text will be at the top of the board).

---
***

To assist with character conversion, use the `Formatter` class.
The `Formatter` has two public helper options:
Expand All @@ -162,6 +162,7 @@ from vestaboard.formatter import Formatter
Formatter().convert('Oh hi!')
# Returns [15, 8, 0, 8, 9, 37]
```
There is no limit to the number of letters you can convert. If you need to create a row with exactly 22 characters, you can use the `.convertLine()` method instead.

To split by word, pass in the argument `byWord=True` along with your input string:

Expand Down Expand Up @@ -192,16 +193,16 @@ Formatter().convertLine('Happy Birthday!')
***
## Repository Info and Disclaimers
### Needs
- Conversion from string to list of lists for `.raw()` method
- Unit and other tests inside the `/test` folder
- ~~Conversion from string to list of lists for `.raw()` method~~ ✅ Complete!
- ~~Unit and other tests inside the `/test` folder~~ ✅ Complete!
- Suggestions or ideas for improvement are always welcome!

Interested in contributing to this project? Send a PR with changes and I'd be happy to review! If you're having trouble with this library, be sure to [open an issue][] so that I can look into the problem. Any details that can be provided alongside the problem would be greatly appreciated.
Thanks!

#### [Shane Sutro][]

#### You belong here :heart:
You belong here ♥️

*Note: this project is maintaned by independent developers and is not sponsored by nor affiliated with Vestaboard, Inc. I am unable to make changes to their API or answer questions about the company, upcoming API support, or future-state plans. For questions regarding Vestaboard's API, privacy policies, or to request assistance with your board, [please get in touch with them here.](https://www.vestaboard.com/contact)*

Expand Down
15 changes: 2 additions & 13 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,2 @@
attrs==20.3.0
certifi==2020.12.5
chardet==4.0.0
idna==2.10
iniconfig==1.1.1
packaging==20.9
pluggy==0.13.1
py==1.10.0
pyparsing==2.4.7
pytest==6.2.2
requests==2.25.1
toml==0.10.2
urllib3==1.26.5
pytest==7.0.0
requests==2.27.0
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="Vestaboard",
version="0.5.0",
version="1.1.0",
author="Shane Sutro",
author_email="[email protected]",
description="A Vestaboard Wrapper",
Expand All @@ -18,9 +18,11 @@
'requests'
],
classifiers=[
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
],
python_requires='>=3.6',
)
58 changes: 25 additions & 33 deletions tests/test_init.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import vestaboard
import pytest
import os
import warnings

validRawChar = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
Expand Down Expand Up @@ -137,42 +136,35 @@ def test_below_pad():
def test_no_pad():
small_board = return_valid_too_small_board()
vb = create_fake_vestaboard()
vb.raw(small_board)
# warnings.warn doesn't work with f strings
warning_message = 'you provided a list with length 6, which has been centered on the board by default. Either provide a list with length 6, or set the "pad" option to suppress this warning.'
# tests if specific warning type and message is raised
with pytest.warns(UserWarning, match=warning_message):
warnings.warn(warning_message, UserWarning)
expected = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 5, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0],
[1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
assert small_board == expected
expected_warning = 'you provided a list with length 3, which has been centered vertically on the board by default. Either provide a list with length 6, or set the "pad" option to suppress this warning.'
with pytest.warns(UserWarning, match=expected_warning):
vb.raw(small_board)
expected = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 5, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0],
[1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
assert small_board == expected

def test_large_board():
large_board = return_valid_too_large_board()
vb = create_fake_vestaboard()
vb.raw(large_board)
# warnings.warn doesn't work with f strings
warning_message = f'The Vestaboard API accepts only 6 lines of characters; you\'ve passed in {len(large_board)}. Only the first 6 will be shown.'
# tests if specific warning type and message is raised
with pytest.warns(UserWarning, match=warning_message):
warnings.warn(warning_message, UserWarning)
expected = [
[0, 0, 0, 0, 0, 0, 5, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0],
[1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 5, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0],
[1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 0, 0]
]
for i in large_board:
print(i)
assert large_board == expected
expected_warning = f'The Vestaboard API accepts only 6 lines of characters; you\'ve passed in {len(large_board)}. Only the first 6 will be shown.'

with pytest.warns(UserWarning, match=expected_warning):
vb.raw(large_board)
expected = [
[0, 0, 0, 0, 0, 0, 5, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0],
[1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 5, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0],
[1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 0, 0]
]
assert large_board == expected

def create_fake_cred_file():
with open(os.path.dirname(os.path.dirname(__file__)) + '/credentials.txt', 'w') as f:
Expand Down
38 changes: 24 additions & 14 deletions vestaboard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@
import warnings

class Board:
def __init__(self, Installable=False, apiKey=False, apiSecret=False, subscriptionId=False):
def __init__(self, installable=False, apiKey=False, apiSecret=False, subscriptionId=False):
"""
Returns an instance of Board().
Keyword arguments:
Installable - an instance of Installable()
installable - an instance of installable()
apiKey - your Vestaboard API Key
apiSecret - your Vestaboard API Secret
subscriptionId - your Subscription ID (this can be obtained for you by creating a new Installable() instance)
subscriptionId - your Subscription ID (this can be obtained for you by creating a new installable() instance)
"""
if not Installable: #check for cred file
if installable and not isinstance(installable, Installable):
raise ValueError(f'Expected the first argument passed to be an instance of Installable, but instead got {type(installable)}. If you are not trying to pass an Installable, specify the name of the arguments, or pass "None" as the first argument.')

if not installable: #check for cred file
if (not apiKey or not apiSecret or not subscriptionId):
try:
creds = get_creds()
Expand All @@ -31,17 +34,17 @@ def __init__(self, Installable=False, apiKey=False, apiSecret=False, subscriptio
self.apiSecret = creds[1]
self.subscriptionId = creds[2]
elif (len(creds) < 3 or len(creds) > 3):
raise ValueError('Credentials have been saved, but one or more are missing. Create a new Installable and pass in saveCredentials=True, or pass in all three parameters when initializing a new Board.')
raise ValueError('Credentials have been saved, but one or more are missing. Create a new installable and pass in saveCredentials=True, or pass in all three parameters when initializing a new Board.')
except FileNotFoundError:
raise ValueError('You must create an installable first or save credentials by passing saveCredentials=True into Installable().')
raise ValueError('You must create an installable first or save credentials by passing saveCredentials=True into installable().')
else:
self.apiKey = apiKey
self.apiSecret = apiSecret
self.subscriptionId = subscriptionId
else:
self.apiKey = Installable.apiKey
self.apiSecret = Installable.apiSecret
self.subscriptionId = Installable.subscriptionId or subscriptionId
self.apiKey = installable.apiKey
self.apiSecret = installable.apiSecret
self.subscriptionId = installable.subscriptionId or subscriptionId

def post(self, text):
headers = {
Expand All @@ -52,19 +55,26 @@ def post(self, text):
requests.post(vbUrls.post.format(self.subscriptionId), headers=headers, json=finalText)

def raw(self, charList: list, pad=None):
"""
Posts already-formatted characters to the board.
Keyword arguments:
`charList` - a list of character lists
`pad` - adds padding when list or character lists is less than 6. Valid options are `top`, `bottom`, and `center`.
"""
base_filler = [0] * 22
filler_needed = 6 - len(charList)
for i, row in enumerate(charList):
if not isinstance(row, list):
raise ValueError(f'Nested items must be lists, not {type(row)}.')
if len(row) != 22:
raise ValueError(f'Nested lists must be exactly 22 characters long. Element at {i} is {len(row)} characters long.')
raise ValueError(f'Nested lists must be exactly 22 characters long. Element at {i} is {len(row)} characters long. Use the Formatter().convertLine() function if you need to add padding to your row.')
for j, char in enumerate(row):
if not isinstance(char, int):
raise ValueError(f'Nested lists must contain numbers only - check row {i} char {j} (0 indexed)')
if len(charList) == 6:
pass
elif len(charList) > 6:
if len(charList) > 6:
# warnings.warn doesn't work with f strings
warning_message = f'The Vestaboard API accepts only 6 lines of characters; you\'ve passed in {len(charList)}. Only the first 6 will be shown.'
warnings.warn(warning_message)
Expand All @@ -79,7 +89,7 @@ def raw(self, charList: list, pad=None):
else:
if pad == None:
# warnings.warn doesn't work with f strings
warning_message = f'you provided a list with length {len(charList)}, which has been centered on the board by default. Either provide a list with length 6, or set the "pad" option to suppress this warning.'
warning_message = f'you provided a list with length {len(charList)}, which has been centered vertically on the board by default. Either provide a list with length 6, or set the "pad" option to suppress this warning.'
warnings.warn(warning_message)
while len(charList) < 6:
charList.append(base_filler)
Expand Down

0 comments on commit 370fef8

Please sign in to comment.