Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: code quality and update to v1.0.0 #20

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: lint
on:
push:
branches:
- main
pull_request:

jobs:
lint:
uses: lnbits/lnbits/.github/workflows/lint.yml@dev
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
12 changes: 12 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"semi": false,
"arrowParens": "avoid",
"insertPragma": false,
"printWidth": 80,
"proseWrap": "preserve",
"singleQuote": true,
"trailingComma": "none",
"useTabs": false,
"bracketSameLine": false,
"bracketSpacing": false
}
47 changes: 47 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
all: format check

format: prettier black ruff

check: mypy pyright checkblack checkruff checkprettier

prettier:
poetry run ./node_modules/.bin/prettier --write .
pyright:
poetry run ./node_modules/.bin/pyright

mypy:
poetry run mypy .

black:
poetry run black .

ruff:
poetry run ruff check . --fix

checkruff:
poetry run ruff check .

checkprettier:
poetry run ./node_modules/.bin/prettier --check .

checkblack:
poetry run black --check .

checkeditorconfig:
editorconfig-checker

test:
PYTHONUNBUFFERED=1 \
DEBUG=true \
poetry run pytest
install-pre-commit-hook:
@echo "Installing pre-commit hook to git"
@echo "Uninstall the hook with poetry run pre-commit uninstall"
poetry run pre-commit install

pre-commit:
poetry run pre-commit run --all-files


checkbundle:
@echo "skipping checkbundle"
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

lnbits scheduler extension [previously: crontabs]

IMPORTANT:
IMPORTANT:

- **The user that runs LNBits server MUST have crontab -e permissions in order to read/write to crontab file.**
- This extension should be limited to admin account(s).
- min version 0.12.10
Expand All @@ -11,23 +12,24 @@ IMPORTANT:

https://github.com/bitkarrot/scheduler/assets/73979971/888d9e77-4edf-4573-85ee-8fc43c710120

### Overview:
Add, Edit, Delete and Monitor your scheduled Jobs from the Main Panel.
### Overview:

Add, Edit, Delete and Monitor your scheduled Jobs from the Main Panel.

<img width="992" alt="Screenshot 2024-01-19 at 2 39 23 PM" src="https://github.com/bitkarrot/scheduler/assets/73979971/01656f95-bdde-4015-99c5-415ce9483ddb">

### Create a job Dialog Box.
### Create a job Dialog Box.

Schedule a specific http call with a specific timed interval.

1. Create a new job by clicking "New Scheduled Job"
2. Fill the options for your new SCHEDULED JOB
- Enter a Name for your Job
- Select an action (GET/PUT/POST/DEL)
- Enter the URL
- Add any headers if required
- Add body data if required, leave blank if there is no body (e.g. for DELETE)
- enter the scheduled time/day you want to run your job. You can use [crontab.guru](https://crontab.guru) to help validate your cron schedules.
- Enter a Name for your Job
- Select an action (GET/PUT/POST/DEL)
- Enter the URL
- Add any headers if required
- Add body data if required, leave blank if there is no body (e.g. for DELETE)
- enter the scheduled time/day you want to run your job. You can use [crontab.guru](https://crontab.guru) to help validate your cron schedules.
3. Save your scheduled job and return to the main page to test the job (Orange) or start the job (Green arrow)
4. All methods for controlling your job are on the main panel [Start/Stop, Edit, Test, View Logs and Delete]

Expand All @@ -36,6 +38,7 @@ NOTE: Jobs may not run automatically on creation depending on the release versio
<img width="605" alt="imgtwo" src="https://github.com/bitkarrot/scheduler/assets/73979971/77f55660-52b6-459c-9ce2-d81e6fa7d1b5">

### There are three sets of logs:

- Individual Job Logs - these are viewable by clicking on the blue info icons on the main panel.
- Test job Logs - this is for testing the job and the result is not recorded to the database only to the test log file.
- Complete Extension Logs - This will show all errors and all events, these are viewable by clicking on the "View all logs" button, located the top of the main panel.
- Test job Logs - this is for testing the job and the result is not recorded to the database only to the test log file.
- Complete Extension Logs - This will show all errors and all events, these are viewable by clicking on the "View all logs" button, located the top of the main panel.
18 changes: 6 additions & 12 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from fastapi import APIRouter

from lnbits.db import Database
from lnbits.helpers import template_renderer

db = Database("ext_scheduler")
from .crud import db
from .views import scheduler_generic_router
from .views_api import scheduler_api_router

scheduler_ext: APIRouter = APIRouter(prefix="/scheduler", tags=["scheduler"])
scheduler_ext.include_router(scheduler_generic_router)
scheduler_ext.include_router(scheduler_api_router)

scheduler_static_files = [
{
Expand All @@ -14,11 +15,4 @@
}
]


def scheduler_renderer():
return template_renderer(["scheduler/templates"])


from .views import * # noqa
from .views_api import * # noqa
from .cron_handler import * # noqa
__all__ = ["db", "scheduler_ext", "scheduler_static_files"]
10 changes: 5 additions & 5 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "Scheduler",
"short_description": "Generate Scheduled tasks via crontabs for lnbits",
"tile": "/scheduler/static/image/scheduler.png",
"contributors": ["bitkarrot"],
"min_lnbits_version": "0.12.10"
"name": "Scheduler",
"short_description": "Generate Scheduled tasks via crontabs for lnbits",
"tile": "/scheduler/static/image/scheduler.png",
"contributors": ["bitkarrot"],
"min_lnbits_version": "1.0.0"
}
74 changes: 39 additions & 35 deletions cron_handler.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,60 @@
from crontab import CronTab, CronSlices
import json
from typing import Union

'''
CronHanlder class contains methods for handling cron jobs, create, edit, delete
from crontab import CronSlices, CronTab

"""
CronHandler class contains methods for handling cron jobs, create, edit, delete

see originaldocs for python-crontab package
https://pypi.org/project/python-crontab/
'''
"""


class CronHandler():
def __init__(self, user:str):
class CronHandler:
def __init__(self, user: Union[str, bool] = True):
self._user = user
self._cron = CronTab(user=self._user)

def get_cron(self):
return self._cron

def set_cron(self, cron):
self._cron = cron

def get_user(self):
return self._user

def set_user(self, user):
self._user = user
self._user = user

async def list_jobs(self) -> str:
async def list_jobs(self) -> list:
jobs = []
for job in self._cron:
jobs.append(str(job))
return jobs

async def pretty_print_jobs(self) -> None:
print(f'==== Cron Jobs List: ====')
print("==== Cron Jobs List: ====")
jobs = await self.list_jobs()
for i in jobs:
print(i)

async def new_job(self, command:str, frequency:str, comment:str, env:json):
async def new_job(self, command: str, frequency: str, comment: str, env: dict):
job = self._cron.new(command=command, comment=comment)
if len(env) > 0:
for key in env:
job.env[key] = env[key]
job.setall(frequency)
if job.is_valid():
if job.is_valid():
self._cron.write_to_user(user=self._user)
return f"job created: {command}, {self._user}, {frequency}"
else:
else:
return f"Error creating job: {command}, {self._user}, {frequency}"

# TODO: add means to edit env variables in the job
async def edit_job(self, command:str, frequency:str, comment:str):
iter = self._cron.find_comment(comment)
for job in iter:
async def edit_job(self, command: str, frequency: str, comment: str):
jobs = self._cron.find_comment(comment)
for job in jobs:
## assume job comment ID is unique
job.command = command
job.setall(frequency)
Expand All @@ -62,21 +64,21 @@ async def edit_job(self, command:str, frequency:str, comment:str):
else:
return f"Error editing job: {command}, {self._user}, {frequency}"

async def enable_job_by_comment(self, comment:str, bool:bool):
# print(f'enable_job_by_comment: {comment}, bool: {bool}')
iter = self._cron.find_comment(comment)
async def enable_job_by_comment(self, comment: str, active: bool):
jobs = self._cron.find_comment(comment)
## assume iter is only 1 long as using unique comment ID
for job in iter:
job.enable(bool)
for job in jobs:
job.enable(active)
self._cron.write_to_user(user=self._user)
return job.is_enabled()

async def get_job_status(self, job_id: str) -> bool:
iter = self._cron.find_comment(job_id)
for job in iter:
jobs = self._cron.find_comment(job_id)
for job in jobs:
return job.is_enabled()
return False

async def remove_job(self, command=str):
async def remove_job(self, command: str):
self._cron.remove_all(command=command)
self._cron.write_to_user(user=self._user)

Expand All @@ -92,22 +94,24 @@ async def remove_by_time(self, time):
self._cron.remove_all(time=time)
self._cron.write_to_user(user=self._user)


async def set_global_env_vars(self, env:dict):
for (name, value) in env.items():
self._cron.env[name] = value
async def set_global_env_vars(self, env: dict):
if self._cron.env:
for name, value in env.items():
self._cron.env[name] = value
self._cron.write_to_user(user=self._user)

async def get_global_env_vars(self):
output = ''
for (name, value) in self._cron.env.items():
output += f'name: {name}, value: {value}\n'
output = ""
if self._cron.env:
for name, value in self._cron.env.items():
output += f"name: {name}, value: {value}\n"
return output

async def clear_global_env_vars(self):
self._cron.env.clear()
if self._cron.env:
self._cron.env.clear()
self._cron.write_to_user(user=self._user)

async def validate_cron_string(self, timestring: str):
is_valid = CronSlices.is_valid(timestring)
return is_valid
return is_valid
Loading
Loading