diff --git a/src/tasker/actions/__init__.py b/src/tasker/actions/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/tasker/profiles/__init__.py b/src/tasker/profiles/__init__.py new file mode 100644 index 0000000..f2ef86a --- /dev/null +++ b/src/tasker/profiles/__init__.py @@ -0,0 +1 @@ +from .time import Time diff --git a/src/tasker/profiles/time.py b/src/tasker/profiles/time.py new file mode 100644 index 0000000..0f97b5c --- /dev/null +++ b/src/tasker/profiles/time.py @@ -0,0 +1,27 @@ +from dataclasses import dataclass + +from tasker.py import Event + + +@dataclass +class Time(Event): + start_hour: int + start_minute: int + + end_hour: int + end_minute: int + + _every_type = None + _every_value = None + + def every_hour(self, value: int): + self._every_type = 1 + self._every_value = value + + return self + + def every_minute(self, value: int): + self._every_type = 2 + self._every_value = value + + return self diff --git a/src/tasker/py/__init__.py b/src/tasker/py/__init__.py index f99b239..56cec77 100644 --- a/src/tasker/py/__init__.py +++ b/src/tasker/py/__init__.py @@ -1,4 +1,5 @@ from .action import Action +from .event import Event from .main import TaskerPy from .profile import Profile from .profile_variable import ProfileVariable diff --git a/src/tasker/py/event.py b/src/tasker/py/event.py new file mode 100644 index 0000000..046c171 --- /dev/null +++ b/src/tasker/py/event.py @@ -0,0 +1,7 @@ +from dataclasses import dataclass + +@dataclass +class Event: + """Basic building block for new profiles""" + + _code_ = 0 diff --git a/src/tasker/py/main.py b/src/tasker/py/main.py index 4e759b9..f9fbe02 100644 --- a/src/tasker/py/main.py +++ b/src/tasker/py/main.py @@ -10,6 +10,8 @@ from .scene import Scene from .task import Task +from .event import Event + @dataclass class TaskerPy: @@ -61,11 +63,36 @@ def _generate_id(self, name: str) -> int: hash_md5 = md5(name.encode()).hexdigest() return int(hash_md5[:7], 16) + def add_profile( + self, + name: str, + event: Event, + profile_id: int = None + ): + def decorator(task: Task): + profile = Profile( + profile_id, + task.id, + name, + event + ) + + if profile.id is None: + profile.id = self._generate_id(name) + + if not (0 < profile.id < 1_000_000_000): + raise ValueError('Profile id invalid min: 0 max: 999_999_999') + + self._profiles.append(profile) + return profile + + return decorator + def add_task( self, - name: str | None = None, - task_id: int | None = None, - output_variables: dict[str, str] | None = None, + name: str = None, + task_id: int = None, + output_variables: dict[str, str] = None, ): """ Decorator to add a task to the TaskerPy application. diff --git a/src/tasker/py/profile.py b/src/tasker/py/profile.py index 45639de..21536a2 100644 --- a/src/tasker/py/profile.py +++ b/src/tasker/py/profile.py @@ -1,7 +1,9 @@ from dataclasses import dataclass - +from .event import Event @dataclass class Profile: id: int task_id: int + name: str + event: Event diff --git a/src/tasker/xml/profile_xml.py b/src/tasker/xml/profile_xml.py index 19b3133..50a17dc 100644 --- a/src/tasker/xml/profile_xml.py +++ b/src/tasker/xml/profile_xml.py @@ -1,6 +1,64 @@ from .xml_builder import XmlBuilder +from tasker.py.profile import Profile +from tasker.profiles import Time + class ProfileXml(XmlBuilder): - def __init__(self, profile): + def __init__(self, profile: Profile): self.profile = profile + + def _create_event(self): + E = self.E + + profile_event = self.profile.event + + match profile_event: + case Time(): + start_hour = str(profile_event.start_hour) + start_minute = str(profile_event.start_minute) + + end_hour = str(profile_event.end_hour) + end_minute = str(profile_event.end_minute) + + every_type = profile_event._every_type + + every_list = [] + + if (every_type is not None): + every_type = str(every_type) + every_value = str( + profile_event._every_value + ) + + every_list = [ + E.rep(every_type), + E.repval(every_value) + ] + + yield E.Time( + E.fh(start_hour), + E.fm(start_minute), + *every_list, + E.th(end_hour), + E.tm(end_minute), + sr='con0' + ) + + def to_xml(self): + E = self.E + + profile_id = str(self.profile.id) + task_id = str(self.profile.task_id) + profile_name = self.profile.name + + return E.Profile( + E.cdate('0'), + E.edate('1'), + E.flags('8'), + E.id(profile_id), + E.mid0(task_id), + E.nme(profile_name), + *self._create_event(), + sr='prof' + ) diff --git a/src/tasker/xml/task_xml.py b/src/tasker/xml/task_xml.py index ceea85e..6b4c213 100644 --- a/src/tasker/xml/task_xml.py +++ b/src/tasker/xml/task_xml.py @@ -85,7 +85,11 @@ def _profile_variables_to_xml(self, *variables: ProfileVariable): def to_xml(self): E = self.E - task_collision = int(self.task.collision) + task_id = str(self.task.id) + task_name = self.task.name + task_priority = str(self.task.priority) + task_collision = str(self.task.collision) + notify = str(self.task.notify).lower() awake = str(self.task.awake).lower() profile_variables = self.task.profile_variables @@ -98,10 +102,10 @@ def to_xml(self): return E.Task( E.cdate('0'), E.edate('1'), - E.id(f'{self.task.id}'), - E.nme(self.task.name), - E.pri(f'{self.task.priority}'), - E.rty(f'{task_collision}'), + E.id(task_id), + E.nme(task_name), + E.pri(task_priority), + E.rty(task_collision), E.showinnot(notify), E.stayawake(awake), *self._actions_to_xml(*self.task()), diff --git a/test/__mocks__/profiles/time.xml b/test/__mocks__/profiles/time.xml new file mode 100644 index 0000000..26396f3 --- /dev/null +++ b/test/__mocks__/profiles/time.xml @@ -0,0 +1,36 @@ + + + 0 + 1 + 8 + 1 + 172601898 + Profile Time + + + + 0 + 1 + 172601898 + task_beep + 100 + 0 + false + false + + 171 + + + + + + + + diff --git a/test/__mocks__/beep.xml b/test/__mocks__/tasks/beep.xml similarity index 93% rename from test/__mocks__/beep.xml rename to test/__mocks__/tasks/beep.xml index b40f7f4..4a0532d 100644 --- a/test/__mocks__/beep.xml +++ b/test/__mocks__/tasks/beep.xml @@ -14,7 +14,7 @@ - + diff --git a/test/__mocks__/flash.xml b/test/__mocks__/tasks/flash.xml similarity index 67% rename from test/__mocks__/flash.xml rename to test/__mocks__/tasks/flash.xml index 56a80c5..1b4dc20 100644 --- a/test/__mocks__/flash.xml +++ b/test/__mocks__/tasks/flash.xml @@ -13,19 +13,19 @@ - - - - - - + + + + + + - + - + - + diff --git a/test/__mocks__/flash_profile_variables.xml b/test/__mocks__/tasks/flash_profile_variables.xml similarity index 100% rename from test/__mocks__/flash_profile_variables.xml rename to test/__mocks__/tasks/flash_profile_variables.xml diff --git a/test/conftest.py b/test/conftest.py index be5977c..05ae3bb 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -2,8 +2,9 @@ from pytest import Function, fixture, mark -from tasker.actions.alert import Beep, Flash -from tasker.py import TaskerPy +from tasker.actions.alert import Beep, Flash, Notify +from tasker.profiles.time import Time +from tasker.py import TaskerPy, Profile @fixture @@ -40,3 +41,15 @@ def task_flash(): yield Flash('Hello, World') return task_flash + +@fixture +def time_profile(beep_task): + app = TaskerPy() + + profile_creator = app.add_profile( + 'Profile Time', + Time(12, 0, 20, 0).every_hour(2), + profile_id=1, + ) + + return profile_creator(beep_task) diff --git a/test/helpers/str_exporter.py b/test/helpers/str_exporter.py new file mode 100644 index 0000000..a9d4831 --- /dev/null +++ b/test/helpers/str_exporter.py @@ -0,0 +1,11 @@ +from pathlib import Path + + +PYCACHE_DIR = 'test/__pycache__/' + +path_default = Path(PYCACHE_DIR) + + +def tmp_export_str(filename: str, data: str): + path = path_default / filename + path.write_text(data, encoding='utf8') diff --git a/test/helpers/xml_reader.py b/test/helpers/xml_reader.py new file mode 100644 index 0000000..8003af5 --- /dev/null +++ b/test/helpers/xml_reader.py @@ -0,0 +1,36 @@ +from lxml import etree +from pathlib import Path + + +MOCK_DIR = 'test/__mocks__' + +path_default = Path(MOCK_DIR) + + +def xml_to_string(element_xml): + return etree.tostring(element_xml, pretty_print=True, encoding='utf-8').decode() + + +def read_file(path: Path): + return path.read_text(encoding='utf8') + + +def read_xml_task(filename): + path = path_default / 'tasks' + path /= filename + + return read_file(path) + + +def read_xml_profile(filename): + path = path_default / 'profiles' + path /= filename + + return read_file(path) + + +def read_xml_scene(filename): + path = path_default / 'scenes' + path /= filename + + return read_file(path) diff --git a/test/test_profile_xml.py b/test/test_profile_xml.py new file mode 100644 index 0000000..65f7b8e --- /dev/null +++ b/test/test_profile_xml.py @@ -0,0 +1,23 @@ +from tasker.xml import TaskerXml + +from test.helpers.xml_reader import ( + read_xml_profile, + xml_to_string +) + +from test.helpers.str_exporter import tmp_export_str + + +def test_time_xml(time_profile, beep_task): + expected_xml = read_xml_profile('time.xml') + + xml = TaskerXml( + profiles=[time_profile], + tasks=[beep_task], + ).to_xml() + + xml_string = xml_to_string(xml) + + tmp_export_str('time_exported.xml', xml_string) + + assert xml_string == expected_xml diff --git a/test/test_task_xml.py b/test/test_task_xml.py index 74ff755..f50d329 100644 --- a/test/test_task_xml.py +++ b/test/test_task_xml.py @@ -1,23 +1,18 @@ -from lxml import etree from pytest import fixture from tasker.py.profile_variable import ProfileVariable from tasker.xml import TaskerXml +from test.helpers.xml_reader import ( + read_xml_task, + xml_to_string +) -def xml_to_string(element_xml): - etree.tostring(element_xml, pretty_print=True, encoding='utf-8').decode() - - -def read_xml(path): - parser = etree.XMLParser(remove_blank_text=True) - mock_tree = etree.parse(path, parser) - - return xml_to_string(mock_tree) +from test.helpers.str_exporter import tmp_export_str def test_beep_xml(beep_task): - expected_xml = read_xml('test/__mocks__/beep.xml') + expected_xml = read_xml_task('beep.xml') task_xml = TaskerXml(tasks=[beep_task]).to_xml() xml_string = xml_to_string(task_xml) @@ -25,7 +20,7 @@ def test_beep_xml(beep_task): def test_flash_xml(flash_task): - expected_xml = read_xml('test/__mocks__/flash.xml') + expected_xml = read_xml_task('flash.xml') task_xml = TaskerXml(tasks=[flash_task]).to_xml() xml_string = xml_to_string(task_xml) @@ -33,7 +28,7 @@ def test_flash_xml(flash_task): def test_task_profile_variable(flash_task): - expected_xml = read_xml('test/__mocks__/flash_profile_variables.xml') + expected_xml = read_xml_task('flash_profile_variables.xml') flash_task.profile_variables = [ ProfileVariable(variable_name='%test_variable', value='TEST_VALUE'), @@ -53,4 +48,7 @@ def test_task_profile_variable(flash_task): task_xml = TaskerXml(tasks=[flash_task]).to_xml() xml_string = xml_to_string(task_xml) + + tmp_export_str('task_profile_exported.xml', xml_string) + assert xml_string == expected_xml