Merge remote-tracking branch 'origin/main'

This commit is contained in:
Ace 2023-11-21 13:29:08 +01:00
commit 96a972e31f
8 changed files with 261 additions and 156 deletions

View File

@ -1,26 +1,26 @@
# Contributor Covenant Code of Conduct # Code of Conduct
## Our Pledge ## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards ## Our Standards
Examples of behavior that contributes to creating a positive environment include: Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language - Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences - Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism - Gracefully accepting constructive criticism
* Focusing on what is best for the community - Focusing on what is best for the community
* Showing empathy towards other community members - Showing empathy towards other community members
Examples of unacceptable behavior by participants include: Examples of unacceptable behavior include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances - The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks - Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment - Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission - Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting - Other conduct that could reasonably be considered inappropriate in a professional setting
## Our Responsibilities ## Our Responsibilities
@ -30,17 +30,16 @@ Project maintainers have the right and responsibility to remove, edit, or reject
## Scope ## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
## Enforcement ## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at aceisace63@yahoo.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [this email](inkycal@aceinnolab.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution ## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
[homepage]: http://contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq.
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -1,31 +1,49 @@
# Inkycal Contribution Policy # Contributing to Inkycal
Thanks for willing to contribute to Inkycal Welcome to Inkycal! We are excited that you are considering contributing to our project. Before you get started, please take a moment to read through our contribution guidelines.
We welcome all sort of contributions, for example:
* giving support via the Discord server
* submitting hotfixes for existing bugs
* giving ideas for new features
* financial contributions (while Inkycal is still dependent on them. These go towards new hardware, displays and a bit of coffee)
# Third party modules ## Code of Conduct
So you had a great idea for an inkycal-module? Awesome! In fact, there is already a repo sepcfifically created for that purpose: [inkycal-modules-template](https://github.com/aceisace/inkycal-modules-template). Just fork that repo, add your module and give me a shout via Discord, Github or Email. If it is really unique and convincing, chances are, if you agree, that it will be available as default module in a future release. Please do not attempt to have it merged straight into main. We try not to touch main except for new releases to keep things consistent, stable and easy-to-maintain.
# Code contributions (PRs, hotfixes, Critical improvements) This project and everyone participating in it are governed by our [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report any unacceptable behavior.
So you found a bug in Inkycal and tested out a bugfix? Kudos! Please fork the Inkycal repo, add your changes in there and create a PR targeting main. For all other PRs, please target a different branch.
Don't forget to add your name in the file `CONTRIBUTORS.md` of the corresponding branch. Thank You! ## How Can I Contribute?
# Submitting Issues ### Reporting Bugs
Please only submit reproducible issues with clear instructions on how to reproduce them. Before submitting a bug report, check if the issue is already reported in the [Issues](https://github.com/aceinnolab/Inkycal/issues) section. If not, please open a new issue with a detailed description of the problem, including steps to reproduce it.
When you are submitting a new issue, please supply the following information: ### Suggesting Enhancements
### Release version We welcome suggestions for new features or enhancements. Use the [Issues](https://github.com/aceinnolab/Inkycal/issues) section to submit your ideas, and provide as much detail as possible.
* are you using main or a different branch. In most cases, this is main
### Expected behavior and actual behavior ### Third party modules
* what were you expecting to happen and what did really happen? So you had a great idea for an inkycal-module? Awesome! In fact, there is already a repo sepcfifically created for that purpose: [inkycal-modules-template](https://github.com/aceisace/inkycal-modules-template). Just fork that repo, add your module and give me a shout via Discord, Github or Email.
### Pull Requests
1. Fork the repository and create a new branch for your feature or bug fix.
2. Make your changes and test thoroughly.
3. Ensure your code follows our coding standards.
4. Update the documentation if necessary.
5. Add your name in the file `CONTRIBUTORS.md`.
6. Open a pull request, referencing any related issues.
## Code Standards
Follow our coding standards to maintain consistency across the project. Check the existing codebase to understand the style and conventions.
## Testing
Ensure that your changes are thoroughly tested. If applicable, provide test cases to cover your code.
## License
By contributing, you agree that your contributions will be licensed under the [LICENSE](https://github.com/aceinnolab/Inkycal/blob/main/LICENSE) file of this project.
## Thank You
Thank you for considering contributing to Inkycal! Your help is invaluable, and we appreciate your time and effort.
Happy coding!
### Steps to reproduce the behavior
* How can the devs re-create the same problem you were having?

View File

@ -0,0 +1,29 @@
# Pull Request
## Description
_Briefly describe the purpose of this pull request_
## Changes Made
_Describe the changes you made in this PR_
## Related Issues
_Reference any related issues here. Use the format "Fixes #<issue_number>" if this PR fixes an issue._
## How to Test
_Provide step-by-step instructions or commands on how to test your changes_
## Screenshots (if applicable)
_Include screenshots or GIFs that demonstrate the changes (if applicable)_
## Checklist
_Place an 'x' in the checkboxes that apply.
If you're unsure about any of them, don't hesitate to ask._
- [ ] I have read the [contribution guidelines](https://github.com/aceinnolab/Inkycal/blob/main/.github/CONTRIBUTING.md)
- [ ] My code follows the project's coding standards
- [ ] I have tested my changes
- [ ] I have updated the documentation
- [ ] My changes do not introduce new warnings or errors
## Additional Notes
_Any additional information or context you want to provide_

View File

@ -277,60 +277,88 @@ def internet_available():
return False return False
def draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1, 0.1)): from PIL import Image, ImageDraw
"""Draws a border at given coordinates.
def draw_dotted_line(draw, start, end, colour, thickness):
"""Draws a dotted line between start and end points using dots."""
delta_x = end[0] - start[0]
delta_y = end[1] - start[1]
distance = ((delta_x ** 2 + delta_y ** 2) ** 0.5)
dot_spacing = 6 # Distance between dots
for i in range(0, int(distance / dot_spacing), 1):
dot_position = (start[0] + (i * dot_spacing * delta_x / distance),
start[1] + (i * dot_spacing * delta_y / distance))
# Drawing a circle at each dot position to create a dotted effect
draw.ellipse([(dot_position[0] - thickness, dot_position[1] - thickness),
(dot_position[0] + thickness, dot_position[1] + thickness)],
fill=colour)
def draw_dashed_line(draw, start, end, colour, thickness):
"""Draws a dashed line between start and end points."""
delta_x = end[0] - start[0]
delta_y = end[1] - start[1]
distance = ((delta_x ** 2 + delta_y ** 2) ** 0.5)
step_size = 10
gap_size = 5
for i in range(0, int(distance / (step_size + gap_size)), 1):
segment_start = (start[0] + (i * (step_size + gap_size) * delta_x / distance),
start[1] + (i * (step_size + gap_size) * delta_y / distance))
segment_end = (segment_start[0] + (step_size * delta_x / distance),
segment_start[1] + (step_size * delta_y / distance))
draw.line((segment_start, segment_end), fill=colour, width=thickness)
def draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1, 0.1), style='solid'):
"""
Draws a border at given coordinates with specified styles (solid, dotted, dashed).
Args: Args:
- image: The image on which the border should be drawn (usually im_black or - image: Image on which the border should be drawn.
im_colour. - xy: Tuple for the top-left corner of the border.
- size: Size of the border as a tuple (width, height).
- xy: Tuple representing the top-left corner of the border e.g. (32, 100) - radius: Radius of the corners.
where 32 is the x co-ordinate and 100 is the y-coordinate. - thickness: Thickness of the border in pixels.
- shrinkage: Tuple for width and height shrinkage percentages.
- size: Size of the border as a tuple -> (width, height). - style: Style of the border ('solid', 'dotted', 'dashed').
- radius: Radius of the corners, where 0 = plain rectangle, 5 = round corners.
- thickness: Thickness of the border in pixels.
- shrinkage: A tuple containing decimals presenting a percentage of shrinking
-> (width_shrink_percentage, height_shrink_percentage).
e.g. (0.1, 0.2) ~ shrinks the width of border by 10%, shrinks height of
border by 20%
""" """
colour = 'black' colour = 'black'
# size from function paramter
width, height = int(size[0] * (1 - shrinkage[0])), int(size[1] * (1 - shrinkage[1])) width, height = int(size[0] * (1 - shrinkage[0])), int(size[1] * (1 - shrinkage[1]))
# shift cursor to move rectangle to center
offset_x, offset_y = int((size[0] - width) / 2), int((size[1] - height) / 2) offset_x, offset_y = int((size[0] - width) / 2), int((size[1] - height) / 2)
x, y, diameter = xy[0] + offset_x, xy[1] + offset_y, radius * 2 x, y, diameter = xy[0] + offset_x, xy[1] + offset_y, radius * 2
# lenght of rectangle size
a, b = (width - diameter), (height - diameter) a, b = (width - diameter), (height - diameter)
# Set coordinates for staright lines
p1, p2 = (x + radius, y), (x + radius + a, y) p1, p2 = (x + radius, y), (x + radius + a, y)
p3, p4 = (x + width, y + radius), (x + width, y + radius + b) p3, p4 = (x + width, y + radius), (x + width, y + radius + b)
p5, p6 = (p2[0], y + height), (p1[0], y + height) p5, p6 = (p2[0], y + height), (p1[0], y + height)
p7, p8 = (x, p4[1]), (x, p3[1]) p7, p8 = (x, p4[1]), (x, p3[1])
draw = ImageDraw.Draw(image)
# Choose the appropriate line drawing function based on style
if style == 'solid':
line_drawer = draw.line
elif style == 'dotted':
line_drawer = lambda coords, fill, width: draw_dotted_line(draw, coords[0], coords[1], fill, width)
elif style == 'dashed':
line_drawer = lambda coords, fill, width: draw_dashed_line(draw, coords[0], coords[1], fill, width)
else:
raise ValueError(f"Unknown style: {style}")
# Draw lines according to the chosen style
line_drawer((p1, p2), fill=colour, width=thickness)
line_drawer((p3, p4), fill=colour, width=thickness)
line_drawer((p5, p6), fill=colour, width=thickness)
line_drawer((p7, p8), fill=colour, width=thickness)
if radius != 0: if radius != 0:
# Set coordinates for arcs
c1, c2 = (x, y), (x + diameter, y + diameter) c1, c2 = (x, y), (x + diameter, y + diameter)
c3, c4 = ((x + width) - diameter, y), (x + width, y + diameter) c3, c4 = ((x + width) - diameter, y), (x + width, y + diameter)
c5, c6 = ((x + width) - diameter, (y + height) - diameter), (x + width, y + height) c5, c6 = ((x + width) - diameter, (y + height) - diameter), (x + width, y + height)
c7, c8 = (x, (y + height) - diameter), (x + diameter, y + height) c7, c8 = (x, (y + height) - diameter), (x + diameter, y + height)
# Draw lines and arcs, creating a square with round corners
draw = ImageDraw.Draw(image)
draw.line((p1, p2), fill=colour, width=thickness)
draw.line((p3, p4), fill=colour, width=thickness)
draw.line((p5, p6), fill=colour, width=thickness)
draw.line((p7, p8), fill=colour, width=thickness)
if radius != 0:
draw.arc((c1, c2), 180, 270, fill=colour, width=thickness) draw.arc((c1, c2), 180, 270, fill=colour, width=thickness)
draw.arc((c3, c4), 270, 360, fill=colour, width=thickness) draw.arc((c3, c4), 270, 360, fill=colour, width=thickness)
draw.arc((c5, c6), 0, 90, fill=colour, width=thickness) draw.arc((c5, c6), 0, 90, fill=colour, width=thickness)

View File

@ -297,13 +297,26 @@ class Calendar(inkycal_module):
parser.sort() parser.sort()
self.month_events = month_events self.month_events = month_events
# find out on which days of this month events are taking place # Initialize days_with_events as an empty list
days_with_events = [ days_with_events = []
int(events['begin'].format('D')) for events in month_events
] # Handle multi-day events by adding all days between start and end
for event in month_events:
start_date = event['begin'].date()
end_date = event['end'].date()
# Convert start and end dates to arrow objects with timezone
start = arrow.get(event['begin'].date(), tzinfo=self.timezone)
end = arrow.get(event['end'].date(), tzinfo=self.timezone)
# Use arrow's range function for generating dates
for day in arrow.Arrow.range('day', start, end):
day_num = int(day.format('D')) # get day number using arrow's format method
if day_num not in days_with_events:
days_with_events.append(day_num)
# remove duplicates (more than one event in a single day) # remove duplicates (more than one event in a single day)
list(set(days_with_events)).sort() days_with_events = sorted(set(days_with_events))
self._days_with_events = days_with_events self._days_with_events = days_with_events
# Draw a border with specified parameters around days with events # Draw a border with specified parameters around days with events
@ -355,7 +368,13 @@ class Calendar(inkycal_module):
cursor = 0 cursor = 0
for event in upcoming_events: for event in upcoming_events:
if cursor < len(event_lines): if cursor < len(event_lines):
the_name = event['title'] event_duration = (event['end'] - event['begin']).days
if event_duration > 1:
# Format the duration using Arrow's localization
days_translation = arrow.get().shift(days=event_duration).humanize(only_distance=True, locale=lang)
the_name = f"{event['title']} ({days_translation})"
else:
the_name = event['title']
the_date = event['begin'].format(self.date_format, locale=lang) the_date = event['begin'].format(self.date_format, locale=lang)
the_time = event['begin'].format(self.time_format, locale=lang) the_time = event['begin'].format(self.time_format, locale=lang)
# logger.debug(f"name:{the_name} date:{the_date} time:{the_time}") # logger.debug(f"name:{the_name} date:{the_date} time:{the_time}")

View File

@ -1,4 +1,5 @@
#!python3 #!/usr/bin/python3
# -*- coding: utf-8 -*-
""" """
Stocks Module for Inkycal Project Stocks Module for Inkycal Project
@ -10,26 +11,17 @@ Version 0.1: Migration to Inkycal 2.0.0b
by https://github.com/worstface by https://github.com/worstface
""" """
import os
import logging import logging
import os
from inkycal.modules.template import inkycal_module
from inkycal.custom import write, internet_available
from PIL import Image from PIL import Image
try: from inkycal.custom import write, internet_available
import yfinance as yf from inkycal.modules.template import inkycal_module
except ImportError:
print('yfinance is not installed! Please install with:')
print('pip3 install yfinance')
try: import yfinance as yf
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import matplotlib.image as mpimg import matplotlib.image as mpimg
except ImportError:
print('matplotlib is not installed! Please install with:')
print('pip3 install matplotlib')
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -82,11 +74,11 @@ class Stocks(inkycal_module):
tmpPath = '/tmp/inkycal_stocks/' tmpPath = '/tmp/inkycal_stocks/'
try: try:
if not os.path.exists(tmpPath): os.mkdir(tmpPath)
os.mkdir(tmpPath)
print(f"Successfully created tmp directory {tmpPath} ")
except OSError: except OSError:
print(f"Creation of tmp directory {tmpPath} failed") print(f"Creation of tmp directory {tmpPath} failed")
else:
print(f"Successfully created tmp directory {tmpPath} ")
# Check if internet is available # Check if internet is available
if internet_available() == True: if internet_available() == True:
@ -96,10 +88,10 @@ class Stocks(inkycal_module):
# Set some parameters for formatting feeds # Set some parameters for formatting feeds
line_spacing = 1 line_spacing = 1
text_bbox_height = self.font.getbbox("hg") text_bbox = self.font.getbbox("hg")
line_height = text_bbox_height[3] - text_bbox_height[1] + line_spacing line_height = text_bbox[3] - text_bbox[1] + line_spacing
line_width = im_width line_width = im_width
max_lines = (im_height // line_height) max_lines = (im_height // (line_height + line_spacing))
logger.debug(f"max_lines: {max_lines}") logger.debug(f"max_lines: {max_lines}")

View File

@ -1,56 +1,75 @@
# #!python3 import unittest
# """ from inkycal.modules import Stocks as Module
# inkycal_stocks unittest
# """ tests = [
# import logging {
# import sys "position": 1,
# import unittest "name": "Stocks",
# from inkycal.modules import Stocks as Module "config": {
# "size": [528, 20],
# from inkycal.modules.inky_image import Inkyimage "tickers": ['TSLA', 'AMD', 'NVDA', '^DJI', 'BTC-USD', 'EURUSD=X'],
# from inkycal.tests import Config "padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
# preview = Inkyimage.preview }
# merge = Inkyimage.merge },
# {
# tests = [ "position": 1,
# { "name": "Stocks",
# "name": "Stocks", "config": {
# "config": { "size": [528, 20],
# "size": [528, 30], "tickers": [],
# "tickers": ['TSLA', 'AMD', 'NVDA', '^DJI', 'BTC-USD', 'EURUSD=X'], "padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
# "padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en" }
# } },
# }, {
# { "position": 1,
# "name": "Stocks", "name": "Stocks",
# "config": { "config": {
# "size": [528, 50], "size": [528, 200],
# "tickers": [], "tickers": ['TSLA', 'AMD', 'NVDA', '^DJI', 'BTC-USD', 'EURUSD=X'],
# "padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en" "padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
# } }
# } },
# ] {
# "position": 1,
# "name": "Stocks",
# class module_test(unittest.TestCase): "config": {
# def test_get_config(self): "size": [528, 800],
# print('getting data for web-ui...', end="") "tickers": ['TSLA', 'AMD', 'NVDA', '^DJI', 'BTC-USD', 'EURUSD=X'],
# Module.get_config() "padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
# print('OK') }
# },
# def test_generate_image(self): {
# for test in tests: "position": 1,
# print(f'test {tests.index(test) + 1} generating image..') "name": "Stocks",
# module = Module(test) "config": {
# im_black, im_colour = module.generate_image() "size": [528, 100],
# print('OK') "tickers": "TSLA,AMD,NVDA,^DJI,BTC-USD,EURUSD=X",
# if Config.USE_PREVIEW: "padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
# preview(merge(im_black, im_colour)) }
# },
# {
# if __name__ == '__main__': "position": 1,
# logger = logging.getLogger() "name": "Stocks",
# logger.level = logging.DEBUG "config": {
# logger.addHandler(logging.StreamHandler(sys.stdout)) "size": [528, 400],
# "tickers": ['TSLA', 'AMD', 'NVDA', '^DJI', 'BTC-USD', 'EURUSD=X'],
# unittest.main() "padding_x": 10, "padding_y": 10, "fontsize": 14, "language": "en"
}
},
]
class module_test(unittest.TestCase):
def test_get_config(self):
print('getting data for web-ui...', end = "")
Module.get_config()
print('OK')
def test_generate_image(self):
for test in tests:
print(f'test {tests.index(test)+1} generating image..')
module = Module(test)
module.generate_image()
print('OK')
if __name__ == '__main__':
unittest.main()

View File

@ -24,3 +24,4 @@ urllib3==2.0.7
python-dotenv==1.0.0 python-dotenv==1.0.0
setuptools==68.2.2 setuptools==68.2.2
html2text==2020.1.16 html2text==2020.1.16
yfinance==0.2.32