Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -277,60 +277,88 @@ def internet_available():
|
||||
return False
|
||||
|
||||
|
||||
def draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1, 0.1)):
|
||||
"""Draws a border at given coordinates.
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
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:
|
||||
- image: The image on which the border should be drawn (usually im_black or
|
||||
im_colour.
|
||||
|
||||
- xy: Tuple representing the top-left corner of the border e.g. (32, 100)
|
||||
where 32 is the x co-ordinate and 100 is the y-coordinate.
|
||||
|
||||
- size: Size of the border as a tuple -> (width, height).
|
||||
|
||||
- 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%
|
||||
- image: Image on which the border should be drawn.
|
||||
- xy: Tuple for the top-left corner of the border.
|
||||
- size: Size of the border as a tuple (width, height).
|
||||
- radius: Radius of the corners.
|
||||
- thickness: Thickness of the border in pixels.
|
||||
- shrinkage: Tuple for width and height shrinkage percentages.
|
||||
- style: Style of the border ('solid', 'dotted', 'dashed').
|
||||
"""
|
||||
|
||||
colour = 'black'
|
||||
|
||||
# size from function paramter
|
||||
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)
|
||||
|
||||
x, y, diameter = xy[0] + offset_x, xy[1] + offset_y, radius * 2
|
||||
# lenght of rectangle size
|
||||
a, b = (width - diameter), (height - diameter)
|
||||
|
||||
# Set coordinates for staright lines
|
||||
p1, p2 = (x + radius, y), (x + radius + a, y)
|
||||
p3, p4 = (x + width, y + radius), (x + width, y + radius + b)
|
||||
p5, p6 = (p2[0], y + height), (p1[0], y + height)
|
||||
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:
|
||||
# Set coordinates for arcs
|
||||
c1, c2 = (x, y), (x + diameter, y + diameter)
|
||||
c3, c4 = ((x + width) - diameter, y), (x + width, y + diameter)
|
||||
c5, c6 = ((x + width) - diameter, (y + height) - diameter), (x + width, 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((c3, c4), 270, 360, fill=colour, width=thickness)
|
||||
draw.arc((c5, c6), 0, 90, fill=colour, width=thickness)
|
||||
|
@@ -296,14 +296,27 @@ class Calendar(inkycal_module):
|
||||
month_events = parser.get_events(month_start, month_end, self.timezone)
|
||||
parser.sort()
|
||||
self.month_events = month_events
|
||||
|
||||
# Initialize days_with_events as an empty list
|
||||
days_with_events = []
|
||||
|
||||
# find out on which days of this month events are taking place
|
||||
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)
|
||||
list(set(days_with_events)).sort()
|
||||
days_with_events = sorted(set(days_with_events))
|
||||
self._days_with_events = days_with_events
|
||||
|
||||
# Draw a border with specified parameters around days with events
|
||||
@@ -355,7 +368,13 @@ class Calendar(inkycal_module):
|
||||
cursor = 0
|
||||
for event in upcoming_events:
|
||||
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_time = event['begin'].format(self.time_format, locale=lang)
|
||||
# logger.debug(f"name:{the_name} date:{the_date} time:{the_time}")
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!python3
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Stocks Module for Inkycal Project
|
||||
|
||||
@@ -10,26 +11,17 @@ Version 0.1: Migration to Inkycal 2.0.0b
|
||||
|
||||
by https://github.com/worstface
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
|
||||
from inkycal.modules.template import inkycal_module
|
||||
from inkycal.custom import write, internet_available
|
||||
import os
|
||||
|
||||
from PIL import Image
|
||||
|
||||
try:
|
||||
import yfinance as yf
|
||||
except ImportError:
|
||||
print('yfinance is not installed! Please install with:')
|
||||
print('pip3 install yfinance')
|
||||
from inkycal.custom import write, internet_available
|
||||
from inkycal.modules.template import inkycal_module
|
||||
|
||||
try:
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.image as mpimg
|
||||
except ImportError:
|
||||
print('matplotlib is not installed! Please install with:')
|
||||
print('pip3 install matplotlib')
|
||||
import yfinance as yf
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.image as mpimg
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -82,11 +74,11 @@ class Stocks(inkycal_module):
|
||||
tmpPath = '/tmp/inkycal_stocks/'
|
||||
|
||||
try:
|
||||
if not os.path.exists(tmpPath):
|
||||
os.mkdir(tmpPath)
|
||||
print(f"Successfully created tmp directory {tmpPath} ")
|
||||
os.mkdir(tmpPath)
|
||||
except OSError:
|
||||
print(f"Creation of tmp directory {tmpPath} failed")
|
||||
else:
|
||||
print(f"Successfully created tmp directory {tmpPath} ")
|
||||
|
||||
# Check if internet is available
|
||||
if internet_available() == True:
|
||||
@@ -96,10 +88,10 @@ class Stocks(inkycal_module):
|
||||
|
||||
# Set some parameters for formatting feeds
|
||||
line_spacing = 1
|
||||
text_bbox_height = self.font.getbbox("hg")
|
||||
line_height = text_bbox_height[3] - text_bbox_height[1] + line_spacing
|
||||
text_bbox = self.font.getbbox("hg")
|
||||
line_height = text_bbox[3] - text_bbox[1] + line_spacing
|
||||
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}")
|
||||
|
||||
|
@@ -1,56 +1,75 @@
|
||||
# #!python3
|
||||
# """
|
||||
# inkycal_stocks unittest
|
||||
# """
|
||||
# import logging
|
||||
# import sys
|
||||
# import unittest
|
||||
# from inkycal.modules import Stocks as Module
|
||||
#
|
||||
# from inkycal.modules.inky_image import Inkyimage
|
||||
# from inkycal.tests import Config
|
||||
# preview = Inkyimage.preview
|
||||
# merge = Inkyimage.merge
|
||||
#
|
||||
# tests = [
|
||||
# {
|
||||
# "name": "Stocks",
|
||||
# "config": {
|
||||
# "size": [528, 30],
|
||||
# "tickers": ['TSLA', 'AMD', 'NVDA', '^DJI', 'BTC-USD', 'EURUSD=X'],
|
||||
# "padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
||||
# }
|
||||
# },
|
||||
# {
|
||||
# "name": "Stocks",
|
||||
# "config": {
|
||||
# "size": [528, 50],
|
||||
# "tickers": [],
|
||||
# "padding_x": 10, "padding_y": 10, "fontsize": 12, "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)
|
||||
# im_black, im_colour = module.generate_image()
|
||||
# print('OK')
|
||||
# if Config.USE_PREVIEW:
|
||||
# preview(merge(im_black, im_colour))
|
||||
#
|
||||
#
|
||||
# if __name__ == '__main__':
|
||||
# logger = logging.getLogger()
|
||||
# logger.level = logging.DEBUG
|
||||
# logger.addHandler(logging.StreamHandler(sys.stdout))
|
||||
#
|
||||
# unittest.main()
|
||||
import unittest
|
||||
from inkycal.modules import Stocks as Module
|
||||
|
||||
tests = [
|
||||
{
|
||||
"position": 1,
|
||||
"name": "Stocks",
|
||||
"config": {
|
||||
"size": [528, 20],
|
||||
"tickers": ['TSLA', 'AMD', 'NVDA', '^DJI', 'BTC-USD', 'EURUSD=X'],
|
||||
"padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
||||
}
|
||||
},
|
||||
{
|
||||
"position": 1,
|
||||
"name": "Stocks",
|
||||
"config": {
|
||||
"size": [528, 20],
|
||||
"tickers": [],
|
||||
"padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
||||
}
|
||||
},
|
||||
{
|
||||
"position": 1,
|
||||
"name": "Stocks",
|
||||
"config": {
|
||||
"size": [528, 200],
|
||||
"tickers": ['TSLA', 'AMD', 'NVDA', '^DJI', 'BTC-USD', 'EURUSD=X'],
|
||||
"padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
||||
}
|
||||
},
|
||||
{
|
||||
"position": 1,
|
||||
"name": "Stocks",
|
||||
"config": {
|
||||
"size": [528, 800],
|
||||
"tickers": ['TSLA', 'AMD', 'NVDA', '^DJI', 'BTC-USD', 'EURUSD=X'],
|
||||
"padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
||||
}
|
||||
},
|
||||
{
|
||||
"position": 1,
|
||||
"name": "Stocks",
|
||||
"config": {
|
||||
"size": [528, 100],
|
||||
"tickers": "TSLA,AMD,NVDA,^DJI,BTC-USD,EURUSD=X",
|
||||
"padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
||||
}
|
||||
},
|
||||
{
|
||||
"position": 1,
|
||||
"name": "Stocks",
|
||||
"config": {
|
||||
"size": [528, 400],
|
||||
"tickers": ['TSLA', 'AMD', 'NVDA', '^DJI', 'BTC-USD', 'EURUSD=X'],
|
||||
"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()
|
||||
|
Reference in New Issue
Block a user