Skip to content

Commit

Permalink
Merge pull request #95 from mmngreco/master
Browse files Browse the repository at this point in the history
Refactor for Python Standardization and CLI Isolation
  • Loading branch information
matclab committed Nov 5, 2023
2 parents d83a46f + 1043fab commit 65fa0e8
Show file tree
Hide file tree
Showing 21 changed files with 245 additions and 144 deletions.
15 changes: 6 additions & 9 deletions .github/workflows/github-action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,18 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest freezegun pytest-cov
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
pip install -r requirements-dev.txt
pip install . -r requirements.txt
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
flake8 src --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
flake8 src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
working-directory: ./i3_agenda
run: |
pytest --junitxml=../junit/test-results.xml
pytest --doctest-modules --cov=. --cov-report=xml:../coverage/cov.xml --cov-report=html:../coverage/
pytest tests --junitxml=junit/test-results.xml
pytest tests --doctest-modules --cov=. --cov-report=xml:coverage/cov.xml --cov-report=html:coverage/
- name: Code Coverage Report
uses: irongut/CodeCoverageSummary@51cc3a756ddcd398d447c044c02cb6aa83fdae95
with:
Expand All @@ -59,4 +57,3 @@ jobs:
with:
name: coverage
path: pr/

2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ i3_agenda/__pycache__
.mypy_cache/
__pycache__

.autoenv
.coverage
32 changes: 32 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
help:
@cat ./Makefile | grep -E '^[a-zA-Z_-]+:.*?## .*$$' | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'

install: ## Install dependencies
pip install . -r requirements.txt

dev: ## Install dependencies for development
pip install -e .
pip install -r requirements-dev.txt

test: ## Run tests
pytest tests --doctest-modules --cov=src

lint: ## Check code style
flake8 src --count --select=E9,F63,F7,F82 --show-source --statistics
flake8 src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics

fix: ## Format code
black -l79 src

release: ## Create a release: make release v=0.1.0
@if [ -z "$(v)" ]; then echo "Missing version number:\nUse: make release v=0.1"; exit 1; fi
@sed -i -e "s/version = \".*\"/version = \"$(v)\"/" pyproject.toml
@sed -i -e "s/__version__ = \".*\"/__version__ = \"$(v)\"/" src/i3_agenda/__init__.py
@sed -i -e "s/archive\/[0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?\.tar\.gz/archive\/$(v).tar.gz/" pyproject.toml
@git diff
@# Ideally, we would use the following
@# git add pyproject.toml src/i3_agenda/__init__.py
@# git commit -m "Release v$(v)"
@# git tag -a v$(v) -m "Release v$(v)"
@# git push
@# git push --tags
44 changes: 34 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[![AUR version](https://img.shields.io/aur/version/i3-agenda?style=flat-square&logo=arch-linux)](https://aur.archlinux.org/packages/i3-agenda/)
[![PyPI](https://img.shields.io/pypi/v/i3-agenda?style=flat-square&logo=python)](https://pypi.org/project/i3-agenda/) <a href="https://github.com/rosenpin/i3-agenda/releases"><img src="https://img.shields.io/github/commits-since/rosenpin/i3-agenda/latest.svg?color=green" alt="Version Badge"></a>
[![PyPI](https://img.shields.io/pypi/v/i3-agenda?style=flat-square&logo=python)](https://pypi.org/project/i3-agenda/) <a href="https://github.com/rosenpin/i3-agenda/releases"><img src="https://img.shields.io/github/commits-since/rosenpin/i3-agenda/latest.svg?color=green" alt="Version Badge"></a>
<a target="_blank" href="https://www.paypal.com/donate/?hosted_button_id=9M8LL9GJXELBN" title="Donate using PayPal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" /></a>

# What is this?
Expand All @@ -16,21 +16,38 @@ It will print the time and title of the closest event.
# Setup

## Google API

https://developers.google.com/calendar/quickstart/python

1. You need to create a Google API project and download your OAuth 2.0 credentials json file.\
You first need to create a project [here](https://console.developers.google.com/apis/credentials), then add Google Calendar support, then download the credentials.json file.\
**Alternatively, you can just use [this link](https://developers.google.com/calendar/quickstart/python) and click "Enable the Google Calendar API". This will create a project, add Google Calendar support, and let you download the file in 1 click**.\
If you're having trouble, you can use this tutorial for more information [https://developers.google.com/calendar/auth](https://developers.google.com/calendar/auth).\
Another great guide can be found here if you're still having trouble: [https://github.com/jay0lee/GAM/wiki/CreatingClientSecretsFile](https://github.com/jay0lee/GAM/wiki/CreatingClientSecretsFile).
1. You need to create a Google API project and download your OAuth 2.0
credentials json file.You first need to create a project [here][cred], then
add Google Calendar support, then download the credentials.json file.
**Alternatively, you can just use [this link][python] and click "Enable the
Google Calendar API". This will create a project, add Google Calendar
support, and let you download the file in 1 click**. If you're having
trouble, you can use this tutorial for more information
[https://developers.google.com/calendar/auth][auth]. Another great guide can
be found here if you're still having trouble:
[https://github.com/jay0lee/GAM/wiki/CreatingClientSecretsFile][secret].
2. Download the credentials file to somewhere on your computer.
3. Proceed to installation phase.

## Installation
After downloading the credentials file, install the package.

### Pipx

Using [`pipx`][pipx] will save you time and it's cross-platform. It is a
package manager for Python that allows you to easily install and run Python
packages in isolated environments.

```bash
pipx install https://github.com/rosenpin/i3-agenda
```

### Pip
1. `sudo pip install i3-agenda`

1. `sudo pip install .`
2. Try running `i3-agenda -c $CREDENTIALS_FILE_PATH` with "$CREDENTIALS_FILE_PATH" replaced with the path to the credentials.json file you downloaded in the previous step.
3. Add configuration to your bar (examples in the Examples section below).

Expand Down Expand Up @@ -90,7 +107,7 @@ Leaving the list empty will fetch all calendars (default behavior).
It might not work properly if you have more than 10 all day events, this can be fixed by increasing the maxResults variable.

### RTL support
If you use RTL or some of your events contain RTL languages, you will need to pipe [pybidi](https://pypi.org/project/python-bidi/) with the script. Example:
If you use RTL or some of your events contain RTL languages, you will need to pipe [pybidi](https://pypi.org/project/python-bidi/) with the script. Example:
`i3-agenda -c ~/.google_credentials.json -ttl 60 | pybidi`

### Caching
Expand Down Expand Up @@ -120,7 +137,7 @@ interval = 60

### Example [SwiftBar](https://github.com/swiftbar/SwiftBar) configuration
![example](https://raw.githubusercontent.com/rosenpin/i3-agenda/master/art/mac_screenshot.png)
This will show your next event as the menu bar title, when you press it you will see a dropdown with all your today events
This will show your next event as the menu bar title, when you press it you will see a dropdown with all your today events
You can call the file `agenda.2m.sh` to make it refresh every 2 minutes
``` bash
#!/bin/bash
Expand All @@ -132,7 +149,7 @@ echo "---"
href="href='https://calendar.google.com/calendar/u/0/r/'"

i=1
while :; do
while :; do
event=$(i3-agenda -c ~/.google_credentials.json -ttl 60 --limchar 30 --skip $i --today)
((i++))
if [[ "$event" == "No events" ]];then
Expand Down Expand Up @@ -205,3 +222,10 @@ if [ -n "${1}" ]; then
echo $skip > $file
fi
```


[pipx]: https://pypa.github.io/pipx/installation/
[cred]: https://console.developers.google.com/apis/credentials
[python]: https://developers.google.com/calendar/quickstart/python
[auth]: https://developers.google.com/calendar/auth
[secret]: https://github.com/jay0lee/GAM/wiki/CreatingClientSecretsFile
17 changes: 0 additions & 17 deletions i3_agenda/const.py

This file was deleted.

33 changes: 33 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[build-system]
requires = ["setuptools", "wheel", "build"]
build-backend = "setuptools.build_meta"

[project]
name = "i3-agenda"
version = "1.8"
description = "Show your next google calendar event in polybar or i3-bar"
readme = "README.md"
authors = [{ name = "Tomer Rosenfeld", email = "[email protected]" }]
license = {text = "Unlicense"}
classifiers = ["Programming Language :: Python :: 3"]
requires-python = ">=3.3"
dependencies = [
"aiohttp",
"google-api-python-client",
"google-auth-httplib2",
"google-auth-oauthlib",
"python-bidi",
"typing_extensions"
]

[project.urls]
Download = "https://github.com/rosenpin/i3-agenda/archive/1.8.tar.gz"

[project.scripts]
i3-agenda = "i3_agenda.main:main"

[metadata]
url = "https://github.com/rosenpin/i3-agenda"
author = "Tomer Rosenfeld"
author_email = "[email protected]"

6 changes: 6 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
flake8
pytest
freezegun
pytest-cov
black
isort
3 changes: 1 addition & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ google-api-python-client>=2.66
google-auth-httplib2>=0.1.
google-auth-oauthlib>=0.7
aiohttp>=3.8
typing_extensions

typing_extensions>=4.7.1
2 changes: 0 additions & 2 deletions setup.cfg

This file was deleted.

41 changes: 0 additions & 41 deletions setup.py

This file was deleted.

4 changes: 4 additions & 0 deletions src/i3_agenda/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
__version__ = "1.8"
__all__ = [
"__version__",
]
20 changes: 15 additions & 5 deletions i3_agenda/API.py → src/i3_agenda/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build, Resource

from event import Event, from_json
from config import CONF_DIR
from textwrap import dedent
from i3_agenda.event import Event, from_json
from i3_agenda.config import CONF_DIR

SCOPES = ["https://www.googleapis.com/auth/calendar.readonly"]
TMP_TOKEN = f"{CONF_DIR}/i3agenda_google_token.pickle"
Expand All @@ -36,7 +37,12 @@ def get_credentials(credspath):
if not creds or not creds.valid:
if not Path(credspath).is_file():
print(
"""You need to download your credentials json file from the Google API Console and pass its path to this script"""
dedent(
"""
You need to download your credentials json file from the Google
API Console and pass its path to this script
"""
).replace("\n", " ")
)
exit(1)
if creds and creds.expired and creds.refresh_token:
Expand All @@ -50,7 +56,9 @@ def get_credentials(credspath):
return creds


def get_callendar_ids(allowed_calendars_ids: List[str], service: Resource) -> List:
def get_callendar_ids(
allowed_calendars_ids: List[str], service: Resource
) -> List:
calendar_ids = []
while True:
calendar_list = service.calendarList().list().execute()
Expand Down Expand Up @@ -85,7 +93,9 @@ def get_result(service, calendar_id, max_results, time_max_rfc3339=None):

def get_today_events(service, calendar_id, max_results):
now = datetime.datetime.utcnow()
midnight_rfc3339 = now.replace(hour=23, minute=59, second=59).isoformat() + "Z"
midnight_rfc3339 = (
now.replace(hour=23, minute=59, second=59).isoformat() + "Z"
)
return get_result(service, calendar_id, max_results, midnight_rfc3339).get(
"items", []
)
Expand Down
11 changes: 7 additions & 4 deletions i3_agenda/cache_utils.py → src/i3_agenda/cache_utils.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from config import CONF_DIR
from i3_agenda.config import CONF_DIR
from typing import Optional, List, TextIO

import os.path
import time
import json

from event import Event, EventEncoder
from const import SECONDS_PER_MINUTE
from i3_agenda.event import Event, EventEncoder
from i3_agenda.const import SECONDS_PER_MINUTE

CACHE_PATH = f"{CONF_DIR}/i3agenda_cache.txt"

Expand All @@ -15,7 +15,10 @@ def load_cache(cachettl: int) -> Optional[List[Event]]:
if not os.path.exists(CACHE_PATH):
return None

if time.time() - os.path.getmtime(CACHE_PATH) > cachettl * SECONDS_PER_MINUTE:
if (
time.time() - os.path.getmtime(CACHE_PATH)
> cachettl * SECONDS_PER_MINUTE
):
return None

try:
Expand Down
7 changes: 3 additions & 4 deletions i3_agenda/config.py → src/i3_agenda/config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import os
from os.path import expanduser
import argparse
from const import *

from i3_agenda.const import MIN_DELAY, MIN_CHARS


CONF_DIR = expanduser("~") + os.path.sep + ".i3agenda"
Expand Down Expand Up @@ -40,7 +39,7 @@
"-u",
action="store_true",
default=False,
help="""when using this flag it will not load previous results from cache, it will however save
help="""when using this flag it will not load previous results from cache, it will however save
new results to cache. You can use this flag to refresh all the cache forcefully""",
)
parser.add_argument(
Expand All @@ -56,7 +55,7 @@
"-r",
type=int,
default=10,
help="""max number of events to query Google's API for each of your calendars. Increase this number if you
help="""max number of events to query Google's API for each of your calendars. Increase this number if you
have lot of events in your google calendar""",
)
parser.add_argument(
Expand Down
File renamed without changes.
15 changes: 15 additions & 0 deletions src/i3_agenda/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from typing_extensions import Final

DAYS_PER_WEEK: Final = 7
HOURS_PER_DAY: Final = 24
SECONDS_PER_DAY: Final = 86400
SECONDS_PER_HOUR: Final = 3600
SECONDS_PER_MINUTE: Final = 60

URGENT_DELAY_MN: Final = 5

MIN_CHARS: Final = -1
MIN_DELAY: Final = -1

LEFT_MOUSE_BUTTON: Final = "1"
RIGHT_MOUSE_BUTTON: Final = "3"
Loading

0 comments on commit 65fa0e8

Please sign in to comment.