Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Extracting ui (views and components) * Extracting core functions * redesigning dependencies (especially gi)
- Loading branch information
StephaneMangin
committed
Oct 6, 2020
1 parent
278ca58
commit dc279aa
Showing
34 changed files
with
2,441 additions
and
2,383 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
#!/usr/bin/python3 | ||
#!/usr/bin/env python | ||
""" | ||
Script to run GTimeLog from the source checkout without installing | ||
""" | ||
import os | ||
import sys | ||
|
||
from gtimelog.main import main | ||
|
||
basedir = os.path.dirname(os.path.realpath(__file__)) | ||
pkgdir = os.path.join(basedir, 'src') | ||
sys.path.insert(0, pkgdir) | ||
|
||
from gtimelog.main import main # noqa: E402 | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,8 @@ | ||
# The gtimelog package. | ||
import logging | ||
import sys | ||
|
||
__version__ = '0.12.0.dev0' | ||
DEBUG = '--debug' in sys.argv | ||
root_logger = logging.getLogger() | ||
root_logger.addHandler(logging.StreamHandler()) | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
class EmailError(Exception): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
import datetime | ||
from email.header import Header | ||
from email.mime.text import MIMEText | ||
from email.utils import parseaddr, formataddr | ||
import os | ||
import re | ||
import time | ||
from gettext import gettext as _ | ||
|
||
from gtimelog import __version__, DEBUG | ||
|
||
|
||
if DEBUG: | ||
def mark_time(what=None, _prev=[0, 0]): | ||
t = time.time() | ||
if what: | ||
print("{:.3f} ({:+.3f}) {}".format(t - _prev[1], t - _prev[0], what)) | ||
else: | ||
print() | ||
_prev[1] = t | ||
_prev[0] = t | ||
else: | ||
def mark_time(what=None): | ||
pass | ||
|
||
|
||
def as_minutes(duration): | ||
"""Convert a datetime.timedelta to an integer number of minutes.""" | ||
return duration.days * 24 * 60 + duration.seconds // 60 | ||
|
||
|
||
def as_hours(duration): | ||
"""Convert a datetime.timedelta to a float number of hours.""" | ||
return duration.days * 24.0 + duration.seconds / (60.0 * 60.0) | ||
|
||
|
||
def format_duration(duration): | ||
"""Format a datetime.timedelta with minute precision.""" | ||
h, m = divmod(as_minutes(duration), 60) | ||
return '%d h %d min' % (h, m) | ||
|
||
|
||
def format_duration_short(duration): | ||
"""Format a datetime.timedelta with minute precision.""" | ||
h, m = divmod((duration.days * 24 * 60 + duration.seconds // 60), 60) | ||
return '%d:%02d' % (h, m) | ||
|
||
|
||
def format_duration_long(duration): | ||
"""Format a datetime.timedelta with minute precision, long format.""" | ||
h, m = divmod((duration.days * 24 * 60 + duration.seconds // 60), 60) | ||
if h and m: | ||
return '%d hour%s %d min' % (h, h != 1 and "s" or "", m) | ||
elif h: | ||
return '%d hour%s' % (h, h != 1 and "s" or "") | ||
else: | ||
return '%d min' % m | ||
|
||
|
||
def parse_datetime(dt): | ||
"""Parse a datetime instance from 'YYYY-MM-DD HH:MM' formatted string.""" | ||
if len(dt) != 16 or dt[4] != '-' or dt[7] != '-' or dt[10] != ' ' or dt[13] != ':': | ||
raise ValueError('bad date time: %r' % dt) | ||
try: | ||
year = int(dt[:4]) | ||
month = int(dt[5:7]) | ||
day = int(dt[8:10]) | ||
hour = int(dt[11:13]) | ||
min = int(dt[14:]) | ||
except ValueError: | ||
raise ValueError('bad date time: %r' % dt) | ||
return datetime.datetime(year, month, day, hour, min) | ||
|
||
|
||
def parse_time(t): | ||
"""Parse a time instance from 'HH:MM' formatted string.""" | ||
m = re.match(r'^(\d+):(\d+)$', t) | ||
if not m: | ||
raise ValueError('bad time: %r' % t) | ||
hour, min = map(int, m.groups()) | ||
return datetime.time(hour, min) | ||
|
||
|
||
def virtual_day(dt, virtual_midnight): | ||
"""Return the "virtual day" of a timestamp. | ||
Timestamps between midnight and "virtual midnight" (e.g. 2 am) are | ||
assigned to the previous "virtual day". | ||
""" | ||
if dt.time() < virtual_midnight: # assign to previous day | ||
return dt.date() - datetime.timedelta(1) | ||
return dt.date() | ||
|
||
|
||
def different_days(dt1, dt2, virtual_midnight): | ||
"""Check whether dt1 and dt2 are on different "virtual days". | ||
See virtual_day(). | ||
""" | ||
return virtual_day(dt1, virtual_midnight) != virtual_day(dt2, | ||
virtual_midnight) | ||
|
||
|
||
def first_of_month(date): | ||
"""Return the first day of the month for a given date.""" | ||
return date.replace(day=1) | ||
|
||
|
||
def prev_month(date): | ||
"""Return the first day of the previous month.""" | ||
if date.month == 1: | ||
return datetime.date(date.year - 1, 12, 1) | ||
else: | ||
return datetime.date(date.year, date.month - 1, 1) | ||
|
||
|
||
def next_month(date): | ||
"""Return the first day of the next month.""" | ||
if date.month == 12: | ||
return datetime.date(date.year + 1, 1, 1) | ||
else: | ||
return datetime.date(date.year, date.month + 1, 1) | ||
|
||
|
||
def uniq(items): | ||
"""Return list with consecutive duplicates removed.""" | ||
result = items[:1] | ||
for item in items[1:]: | ||
if item != result[-1]: | ||
result.append(item) | ||
return result | ||
|
||
|
||
def get_mtime(filename): | ||
"""Return the modification time of a file, if it exists. | ||
Returns None if the file doesn't exist. | ||
""" | ||
# Accept any file-like object instead of a filename (for the benefit of | ||
# unit tests). | ||
if hasattr(filename, 'read'): | ||
return None | ||
try: | ||
return os.stat(filename).st_mtime | ||
except OSError: | ||
return None | ||
|
||
|
||
def format_duration(duration): | ||
"""Format a datetime.timedelta with minute precision. | ||
The difference from gtimelog.timelog.format_duration() is that this | ||
one is internationalized. | ||
""" | ||
h, m = divmod(as_minutes(duration), 60) | ||
return _('{0} h {1} min').format(h, m) | ||
|
||
|
||
def isascii(s): | ||
return all(0 <= ord(c) <= 127 for c in s) | ||
|
||
|
||
def address_header(name_and_address): | ||
if isascii(name_and_address): | ||
return name_and_address | ||
name, addr = parseaddr(name_and_address) | ||
name = str(Header(name, 'UTF-8')) | ||
return formataddr((name, addr)) | ||
|
||
|
||
def subject_header(header): | ||
if isascii(header): | ||
return header | ||
return Header(header, 'UTF-8') | ||
|
||
|
||
def prepare_message(sender, recipient, subject, body): | ||
if isascii(body): | ||
msg = MIMEText(body) | ||
else: | ||
msg = MIMEText(body, _charset="UTF-8") | ||
if sender: | ||
msg["From"] = address_header(sender) | ||
msg["To"] = address_header(recipient) | ||
msg["Subject"] = subject_header(subject) | ||
msg["User-Agent"] = "gtimelog/{}".format(__version__) | ||
return msg |
Oops, something went wrong.