mirror of
https://github.com/Xevion/bulk-reminders.git
synced 2025-12-09 10:06:42 -06:00
Add logging to project
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
import traceback
|
|
||||||
from typing import Any, Iterator, List, Optional, Tuple, Union
|
from typing import Any, Iterator, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from PyQt5 import QtGui
|
from PyQt5 import QtGui
|
||||||
@@ -23,49 +23,62 @@ TIME_REGEX = re.compile(r'\d{2}:\d{2}(?:AM|PM)')
|
|||||||
DATE_FORMAT = '%Y-%m-%d'
|
DATE_FORMAT = '%Y-%m-%d'
|
||||||
DATETIME_FORMAT = DATE_FORMAT + ' %H:%M%p'
|
DATETIME_FORMAT = DATE_FORMAT + ' %H:%M%p'
|
||||||
|
|
||||||
|
logger = logging.getLogger(__file__)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
class Calendar(object):
|
class Calendar(object):
|
||||||
|
TOKEN_FILE = 'token.json'
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.credentials: Optional[Credentials] = None
|
self.credentials: Optional[Credentials] = None
|
||||||
self.service: Optional[Resource] = None
|
self.service: Optional[Resource] = None
|
||||||
|
|
||||||
def save_token(self) -> None:
|
def save_token(self) -> None:
|
||||||
"""Store the credentials for later use."""
|
"""Store the credentials for later use."""
|
||||||
with open('token.json', 'w') as token:
|
logger.debug('Saving token to token.json')
|
||||||
|
with open(Calendar.TOKEN_FILE, 'w') as token:
|
||||||
token.write(self.credentials.to_json())
|
token.write(self.credentials.to_json())
|
||||||
|
|
||||||
def authenticate_via_token(self) -> bool:
|
def authenticate_via_token(self) -> bool:
|
||||||
"""Attempt to login using the tokens stored in token.json"""
|
"""Attempt to login using the tokens stored in token.json"""
|
||||||
if os.path.exists('token.json'):
|
logger.info('Attempting to authenticate via token')
|
||||||
|
if os.path.exists(Calendar.TOKEN_FILE):
|
||||||
self.credentials = Credentials.from_authorized_user_file('token.json', SCOPES)
|
self.credentials = Credentials.from_authorized_user_file('token.json', SCOPES)
|
||||||
if self.credentials and self.credentials.expired and self.credentials.refresh_token:
|
if self.credentials and self.credentials.expired and self.credentials.refresh_token:
|
||||||
try:
|
try:
|
||||||
|
logger.info('Refreshing token')
|
||||||
self.credentials.refresh(Request())
|
self.credentials.refresh(Request())
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
traceback.print_exc()
|
logger.error('Failed to refresh token', exc_info=e)
|
||||||
return False
|
return False
|
||||||
self.save_token()
|
else:
|
||||||
|
logger.info('Successfully authenticated via token')
|
||||||
|
self.save_token()
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def authenticate_via_oauth(self) -> bool:
|
def authenticate_via_oauth(self) -> bool:
|
||||||
"""Attempt to acquire credentials"""
|
"""Attempt to acquire credentials"""
|
||||||
try:
|
try:
|
||||||
flow = InstalledAppFlow.from_client_secrets_file(
|
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
|
||||||
'credentials.json', SCOPES)
|
|
||||||
self.credentials = flow.run_local_server(port=0)
|
self.credentials = flow.run_local_server(port=0)
|
||||||
self.save_token()
|
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
traceback.print_exc()
|
logger.error('Failed to authenticate via OAuth 2.0 flow', exc_info=e)
|
||||||
return False
|
return False
|
||||||
|
else:
|
||||||
|
self.save_token()
|
||||||
|
return True
|
||||||
|
|
||||||
def setupService(self) -> None:
|
def setupService(self) -> None:
|
||||||
"""Setup the Google App Engine API Service for the Calendar API"""
|
"""Setup the Google App Engine API Service for the Calendar API"""
|
||||||
|
logger.debug('Initializing Calendar API Service')
|
||||||
self.service = build('calendar', 'v3', credentials=self.credentials)
|
self.service = build('calendar', 'v3', credentials=self.credentials)
|
||||||
|
|
||||||
def getCalendars(self) -> Iterator[Any]:
|
def getCalendars(self) -> Iterator[Any]:
|
||||||
"""Retrieve all calendar data"""
|
"""Retrieve all calendar data"""
|
||||||
page_token = None
|
logger.debug('Retrieving all calendar data')
|
||||||
|
page, page_token = 1, None
|
||||||
while True:
|
while True:
|
||||||
calendar_list = self.service.calendarList().list(pageToken=page_token, minAccessRole='writer').execute()
|
calendar_list = self.service.calendarList().list(pageToken=page_token, minAccessRole='writer').execute()
|
||||||
for entry in calendar_list['items']:
|
for entry in calendar_list['items']:
|
||||||
@@ -76,12 +89,16 @@ class Calendar(object):
|
|||||||
yield entry
|
yield entry
|
||||||
|
|
||||||
# Continue loading more calendars
|
# Continue loading more calendars
|
||||||
|
page += 1
|
||||||
page_token = calendar_list.get('nextPageToken')
|
page_token = calendar_list.get('nextPageToken')
|
||||||
if page_token is None:
|
if page_token is None:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
logger.debug(f'Retrieving page {page} of Calendars')
|
||||||
|
|
||||||
def getEvents(self, calendarID: str) -> List[Any]:
|
def getEvents(self, calendarID: str) -> List[Any]:
|
||||||
"""Retrieves up to 2500 events for a given calendar ordered by occurrence that happen in the future."""
|
"""Retrieves up to 2500 events for a given calendar ordered by occurrence that happen in the future."""
|
||||||
|
logger.debug(f'Retrieving all events from Calendar {calendarID}')
|
||||||
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
|
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
|
||||||
events = self.service.events().list(calendarId=calendarID, timeMin=now,
|
events = self.service.events().list(calendarId=calendarID, timeMin=now,
|
||||||
maxResults=2500, singleEvents=True,
|
maxResults=2500, singleEvents=True,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
from typing import Iterator, List
|
from typing import Iterator, List
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
@@ -10,12 +11,16 @@ from bulk_reminders.load import LoadDialog
|
|||||||
from bulk_reminders.oauth import OAuthDialog
|
from bulk_reminders.oauth import OAuthDialog
|
||||||
from bulk_reminders.undo import IDPair, Stage
|
from bulk_reminders.undo import IDPair, Stage
|
||||||
|
|
||||||
|
logging.basicConfig(format='[%(asctime)s] [%(levelname)s] [%(threadName)s] %(message)s')
|
||||||
|
logger = logging.getLogger(__file__)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
class MainWindow(QMainWindow, Ui_MainWindow):
|
class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# Initial UI setup
|
# Initial UI setup
|
||||||
super(MainWindow, self).__init__(*args, **kwargs)
|
super(MainWindow, self).__init__(*args, **kwargs)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
logger.debug('UI Initialized.')
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
self.calendar = api.Calendar()
|
self.calendar = api.Calendar()
|
||||||
@@ -63,6 +68,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.loadEventsButton.clicked.connect(self.load_events)
|
self.loadEventsButton.clicked.connect(self.load_events)
|
||||||
self.cachedLoadText = ''
|
self.cachedLoadText = ''
|
||||||
self.readyEvents: List[Event] = []
|
self.readyEvents: List[Event] = []
|
||||||
|
self.apiEvents: List[dict] = []
|
||||||
|
|
||||||
self.populate()
|
self.populate()
|
||||||
|
|
||||||
@@ -119,6 +125,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.eventCountLabel.setText(f'{len(self.readyEvents)} ready, {undoable} undoable in {stage} stages, {foreign} foreign ({total})')
|
self.eventCountLabel.setText(f'{len(self.readyEvents)} ready, {undoable} undoable in {stage} stages, {foreign} foreign ({total})')
|
||||||
|
|
||||||
self.eventsView.setRowCount(len(events))
|
self.eventsView.setRowCount(len(events))
|
||||||
|
logger.debug(f'Populating table with {self.eventsView.rowCount()} events.')
|
||||||
for row, event in enumerate(events):
|
for row, event in enumerate(events):
|
||||||
event.fill_row(row, self.eventsView)
|
event.fill_row(row, self.eventsView)
|
||||||
|
|
||||||
@@ -126,4 +133,5 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
def comboBoxChanged(self, row) -> None:
|
def comboBoxChanged(self, row) -> None:
|
||||||
"""When the Calendar Selection combobox"""
|
"""When the Calendar Selection combobox"""
|
||||||
self.currentCalendarID = self.comboModel.item(row).data()
|
self.currentCalendarID = self.comboModel.item(row).data()
|
||||||
|
logger.info(f'Switching to Calendar "{self.comboModel.item(row).text()} ({self.currentCalendarID})"')
|
||||||
self.populate()
|
self.populate()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
@@ -9,6 +10,9 @@ from PyQt5.QtWidgets import QApplication, QDialog, QLabel
|
|||||||
from bulk_reminders.api import Event
|
from bulk_reminders.api import Event
|
||||||
from bulk_reminders.load_base import Ui_Dialog
|
from bulk_reminders.load_base import Ui_Dialog
|
||||||
|
|
||||||
|
logger = logging.getLogger(__file__)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
REGEX = re.compile(
|
REGEX = re.compile(
|
||||||
r'\s*([\w\d\s,.;\'!\[\]()]{1,})\s+\|\s+(\d{4}-\d{2}-\d{2})\s+(\d{1,2}:\d{2}(?:AM|PM))?\s*(\d{4}-\d{2}-\d{2})(\d{1,2}:\d{2}(?:AM|PM))?')
|
r'\s*([\w\d\s,.;\'!\[\]()]{1,})\s+\|\s+(\d{4}-\d{2}-\d{2})\s+(\d{1,2}:\d{2}(?:AM|PM))?\s*(\d{4}-\d{2}-\d{2})(\d{1,2}:\d{2}(?:AM|PM))?')
|
||||||
|
|
||||||
@@ -46,6 +50,7 @@ class LoadDialog(QDialog, Ui_Dialog):
|
|||||||
try:
|
try:
|
||||||
self.parsed = list(map(Event.parse_raw, results))
|
self.parsed = list(map(Event.parse_raw, results))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
logger.debug('Dialog input has data errors (invalid dates etc.)')
|
||||||
resultsText += ' Data error.'
|
resultsText += ' Data error.'
|
||||||
self.eventCountLabel.setText(resultsText)
|
self.eventCountLabel.setText(resultsText)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
from typing import Callable, Optional
|
from typing import Callable, Optional
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
@@ -5,6 +6,10 @@ from PyQt5.QtWidgets import QDialog
|
|||||||
|
|
||||||
from bulk_reminders.oauth_base import Ui_Dialog
|
from bulk_reminders.oauth_base import Ui_Dialog
|
||||||
|
|
||||||
|
logger = logging.getLogger(__file__)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
class OAuthDialog(QDialog, Ui_Dialog):
|
class OAuthDialog(QDialog, Ui_Dialog):
|
||||||
def __init__(self, *args, callback: Optional[Callable] = None, **kwargs):
|
def __init__(self, *args, callback: Optional[Callable] = None, **kwargs):
|
||||||
super(QDialog, self).__init__(*args, **kwargs)
|
super(QDialog, self).__init__(*args, **kwargs)
|
||||||
@@ -16,6 +21,7 @@ class OAuthDialog(QDialog, Ui_Dialog):
|
|||||||
callback()
|
callback()
|
||||||
self.accept()
|
self.accept()
|
||||||
else:
|
else:
|
||||||
|
logger.debug('No callback given for OAuth Dialog; closing immediately.')
|
||||||
self.reject()
|
self.reject()
|
||||||
self._closable = True
|
self._closable = True
|
||||||
|
|
||||||
@@ -23,6 +29,6 @@ class OAuthDialog(QDialog, Ui_Dialog):
|
|||||||
if self.closable:
|
if self.closable:
|
||||||
super(QDialog, self).closeEvent(evnt)
|
super(QDialog, self).closeEvent(evnt)
|
||||||
else:
|
else:
|
||||||
|
logger.debug('Ignoring close event.')
|
||||||
evnt.ignore()
|
evnt.ignore()
|
||||||
self.setWindowState(Qt.WindowMinimized)
|
self.setWindowState(Qt.WindowActive)
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import json
|
import logging
|
||||||
import os
|
import os
|
||||||
from typing import Iterator, List
|
from typing import Iterator, List
|
||||||
|
|
||||||
import jsonpickle
|
import jsonpickle
|
||||||
|
|
||||||
|
logger = logging.getLogger(__file__)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
class HistoryManager(object):
|
class HistoryManager(object):
|
||||||
def __init__(self, file: str) -> None:
|
def __init__(self, file: str) -> None:
|
||||||
@@ -20,11 +23,13 @@ class HistoryManager(object):
|
|||||||
|
|
||||||
def load(self) -> None:
|
def load(self) -> None:
|
||||||
"""Load data from the undo history file"""
|
"""Load data from the undo history file"""
|
||||||
|
logger.info('Loading from undo history file.')
|
||||||
with open(self.file, 'r') as file:
|
with open(self.file, 'r') as file:
|
||||||
self.stages = jsonpickle.decode(file.read())
|
self.stages = jsonpickle.decode(file.read())
|
||||||
|
|
||||||
def save(self) -> None:
|
def save(self) -> None:
|
||||||
"""Save data to the undo history file."""
|
"""Save data to the undo history file."""
|
||||||
|
logger.info('Saving to undo history file.')
|
||||||
with open(self.file, 'w') as file:
|
with open(self.file, 'w') as file:
|
||||||
file.write(jsonpickle.encode(self.stages))
|
file.write(jsonpickle.encode(self.stages))
|
||||||
|
|
||||||
@@ -34,10 +39,10 @@ class HistoryManager(object):
|
|||||||
|
|
||||||
def exists(self, eventID: 'IDPair') -> int:
|
def exists(self, eventID: 'IDPair') -> int:
|
||||||
"""Check if a given Event ID exists anywhere in the undo history data. Returns the stage index or -1 if it wasn't found."""
|
"""Check if a given Event ID exists anywhere in the undo history data. Returns the stage index or -1 if it wasn't found."""
|
||||||
print(f'Checking for {eventID} in undo history')
|
|
||||||
for stage in self.stages:
|
for stage in self.stages:
|
||||||
for undoable in stage.events:
|
for undoable in stage.events:
|
||||||
if eventID == undoable.eventID:
|
if eventID == undoable.eventID:
|
||||||
|
logger.debug(f'Found Event {eventID} in Stage {stage.index}')
|
||||||
return stage.index
|
return stage.index
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
@@ -59,6 +64,7 @@ class HistoryManager(object):
|
|||||||
|
|
||||||
def addStage(self, newStage: 'Stage'):
|
def addStage(self, newStage: 'Stage'):
|
||||||
"""Adds and inserts a new Stage at the start of the history."""
|
"""Adds and inserts a new Stage at the start of the history."""
|
||||||
|
logger.debug(f'Adding new stage with {len(newStage)} events.')
|
||||||
self.stages.insert(0, newStage)
|
self.stages.insert(0, newStage)
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
@@ -94,4 +100,3 @@ class IDPair(object):
|
|||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
"""Returns a hash value for the IDPair"""
|
"""Returns a hash value for the IDPair"""
|
||||||
return hash((self.calendarID, self.eventID))
|
return hash((self.calendarID, self.eventID))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user