Adds integration with Google Calendar: includes API configuration, event management nodes, and project structure in the README
This commit is contained in:
parent
8efcfa3fd2
commit
6826b5570c
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Google Calendar API Configuration
|
||||||
|
GOOGLE_CALENDAR_ID=your_calendar_id@group.calendar.google.com
|
||||||
|
GOOGLE_APPLICATION_CREDENTIALS=credentials.json
|
||||||
|
|
||||||
|
# Application Configuration
|
||||||
|
TIMEZONE=America/Sao_Paulo # or your preferred timezone
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
.env
|
||||||
|
Pipfile.lock
|
||||||
|
credentials.json
|
||||||
|
token.pickle
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
python-dotenv = ">=0.19.0"
|
||||||
|
pocketflow = ">=0.0.2"
|
||||||
|
google-auth-oauthlib = ">=1.0.0"
|
||||||
|
google-auth-httplib2 = ">=0.1.0"
|
||||||
|
google-api-python-client = ">=2.0.0"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.13"
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
# Pocket Google Calendar
|
||||||
|
|
||||||
|
An application based on the Pocket Flow framework for Google Calendar integration.
|
||||||
|
|
||||||
|
## 📋 Description
|
||||||
|
|
||||||
|
This project implements a Google Calendar integration using the Pocket Flow framework, allowing efficient management of events and appointments through a simple and intuitive interface.
|
||||||
|
|
||||||
|
## 🚀 Features
|
||||||
|
|
||||||
|
- Google Calendar API Integration
|
||||||
|
- Event Management
|
||||||
|
- Appointment Viewing
|
||||||
|
- Flow-based Interface using Pocket Flow
|
||||||
|
|
||||||
|
## 🛠️ Technologies Used
|
||||||
|
|
||||||
|
- Python
|
||||||
|
- Pocket Flow Framework
|
||||||
|
- Google Calendar API
|
||||||
|
- Pipenv for dependency management
|
||||||
|
|
||||||
|
## 📦 Installation
|
||||||
|
|
||||||
|
1. Clone the repository:
|
||||||
|
```bash
|
||||||
|
git clone [REPOSITORY_URL]
|
||||||
|
cd pocket-google-calendar
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install dependencies using Pipenv:
|
||||||
|
```bash
|
||||||
|
pipenv install
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔑 Credentials Setup
|
||||||
|
|
||||||
|
1. Go to the [Google Cloud Console](https://console.cloud.google.com/)
|
||||||
|
2. Create a new project or select an existing one
|
||||||
|
3. Enable the Google Calendar API for your project
|
||||||
|
4. Create credentials:
|
||||||
|
- Go to "APIs & Services" > "Credentials"
|
||||||
|
- Click "Create Credentials" > "OAuth client ID"
|
||||||
|
- Choose "Desktop application" as the application type
|
||||||
|
- Download the credentials file
|
||||||
|
- Rename it to `credentials.json`
|
||||||
|
- Place it in the root directory of the project
|
||||||
|
|
||||||
|
## 🌍 Environment Variables
|
||||||
|
|
||||||
|
Create a `.env` file in the root directory with the following variables:
|
||||||
|
|
||||||
|
```env
|
||||||
|
# Google Calendar API Configuration
|
||||||
|
GOOGLE_CALENDAR_ID=your_calendar_id@group.calendar.google.com
|
||||||
|
GOOGLE_APPLICATION_CREDENTIALS=credentials.json
|
||||||
|
|
||||||
|
# Application Configuration
|
||||||
|
TIMEZONE=America/Sao_Paulo # or your preferred timezone
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Configuration
|
||||||
|
|
||||||
|
1. Activate the virtual environment:
|
||||||
|
```bash
|
||||||
|
pipenv shell
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Run the application:
|
||||||
|
```bash
|
||||||
|
python main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Expected Output
|
||||||
|
|
||||||
|
When running the example, you'll see an output similar to this:
|
||||||
|
|
||||||
|
```
|
||||||
|
=== Listing your calendars ===
|
||||||
|
- Primary Calendar
|
||||||
|
- Work
|
||||||
|
- Personal
|
||||||
|
|
||||||
|
=== Creating an example event ===
|
||||||
|
Event created successfully!
|
||||||
|
Event ID: abc123xyz
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 📁 Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
pocket-google-calendar/
|
||||||
|
├── main.py # Application entry point
|
||||||
|
├── nodes.py # Pocket Flow node definitions
|
||||||
|
├── utils/ # Utilities and helper functions
|
||||||
|
├── Pipfile # Pipenv configuration
|
||||||
|
├── credentials.json # Google Calendar API credentials
|
||||||
|
├── .env # Environment variables
|
||||||
|
└── token.pickle # Google Calendar authentication token
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
1. Fork the project
|
||||||
|
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
||||||
|
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
||||||
|
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
||||||
|
5. Open a Pull Request
|
||||||
|
|
||||||
|
## 📝 License
|
||||||
|
|
||||||
|
This project is under the MIT License. See the [LICENSE](LICENSE) file for more details.
|
||||||
|
|
||||||
|
## ✨ Acknowledgments
|
||||||
|
|
||||||
|
- [Pocket Flow](https://github.com/the-pocket/PocketFlow) - Framework used
|
||||||
|
- [Google Calendar API](https://developers.google.com/calendar) - Integration API
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
from pocketflow import Flow
|
||||||
|
from nodes import CreateCalendarEventNode, ListCalendarEventsNode, ListCalendarsNode
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
def create_calendar_flow():
|
||||||
|
"""Creates a flow to manage calendar events."""
|
||||||
|
# Create nodes
|
||||||
|
create_event_node = CreateCalendarEventNode()
|
||||||
|
list_events_node = ListCalendarEventsNode()
|
||||||
|
|
||||||
|
# Connect nodes
|
||||||
|
create_event_node - "success" >> list_events_node
|
||||||
|
create_event_node - "error" >> None
|
||||||
|
|
||||||
|
# Create flow
|
||||||
|
return Flow(start=create_event_node)
|
||||||
|
|
||||||
|
def list_calendars_flow():
|
||||||
|
"""Creates a flow to list all user calendars."""
|
||||||
|
list_calendars_node = ListCalendarsNode()
|
||||||
|
return Flow(start=list_calendars_node)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Example: List all calendars
|
||||||
|
print("=== Listing your calendars ===")
|
||||||
|
flow = list_calendars_flow()
|
||||||
|
shared = {}
|
||||||
|
flow.run(shared)
|
||||||
|
|
||||||
|
if 'available_calendars' in shared:
|
||||||
|
for cal in shared['available_calendars']:
|
||||||
|
print(f"- {cal.get('summary')}")
|
||||||
|
|
||||||
|
# Example: Create a simple event
|
||||||
|
print("\n=== Creating an example event ===")
|
||||||
|
flow = create_calendar_flow()
|
||||||
|
|
||||||
|
shared = {
|
||||||
|
'event_summary': 'Example Meeting',
|
||||||
|
'event_description': 'An example meeting created by PocketFlow',
|
||||||
|
'event_start_time': datetime.now() + timedelta(days=1),
|
||||||
|
'event_end_time': datetime.now() + timedelta(days=1, hours=1),
|
||||||
|
'days_to_list': 7
|
||||||
|
}
|
||||||
|
|
||||||
|
flow.run(shared)
|
||||||
|
|
||||||
|
if 'last_created_event' in shared:
|
||||||
|
print("Event created successfully!")
|
||||||
|
print(f"Event ID: {shared['last_created_event']['id']}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
from pocketflow import Node
|
||||||
|
from utils.google_calendar import create_event, list_events, list_calendar_lists
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
class CreateCalendarEventNode(Node):
|
||||||
|
def prep(self, shared):
|
||||||
|
"""Prepares the necessary data to create an event."""
|
||||||
|
return {
|
||||||
|
'summary': shared.get('event_summary'),
|
||||||
|
'description': shared.get('event_description'),
|
||||||
|
'start_time': shared.get('event_start_time'),
|
||||||
|
'end_time': shared.get('event_end_time')
|
||||||
|
}
|
||||||
|
|
||||||
|
def exec(self, event_data):
|
||||||
|
"""Creates a new calendar event."""
|
||||||
|
try:
|
||||||
|
event = create_event(
|
||||||
|
summary=event_data['summary'],
|
||||||
|
description=event_data['description'],
|
||||||
|
start_time=event_data['start_time'],
|
||||||
|
end_time=event_data['end_time']
|
||||||
|
)
|
||||||
|
return {'success': True, 'event': event}
|
||||||
|
except Exception as e:
|
||||||
|
return {'success': False, 'error': str(e)}
|
||||||
|
|
||||||
|
def post(self, shared, prep_res, exec_res):
|
||||||
|
"""Stores the event creation result."""
|
||||||
|
if exec_res['success']:
|
||||||
|
shared['last_created_event'] = exec_res['event']
|
||||||
|
return 'success'
|
||||||
|
else:
|
||||||
|
shared['error'] = exec_res['error']
|
||||||
|
return 'error'
|
||||||
|
|
||||||
|
class ListCalendarEventsNode(Node):
|
||||||
|
def prep(self, shared):
|
||||||
|
"""Prepares parameters to list events."""
|
||||||
|
return {
|
||||||
|
'days': shared.get('days_to_list', 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
def exec(self, params):
|
||||||
|
"""Lists calendar events."""
|
||||||
|
try:
|
||||||
|
events = list_events(days=params['days'])
|
||||||
|
return {'success': True, 'events': events}
|
||||||
|
except Exception as e:
|
||||||
|
return {'success': False, 'error': str(e)}
|
||||||
|
|
||||||
|
def post(self, shared, prep_res, exec_res):
|
||||||
|
"""Stores the list of events."""
|
||||||
|
if exec_res['success']:
|
||||||
|
shared['calendar_events'] = exec_res['events']
|
||||||
|
return 'success'
|
||||||
|
else:
|
||||||
|
shared['error'] = exec_res['error']
|
||||||
|
return 'error'
|
||||||
|
|
||||||
|
class ListCalendarsNode(Node):
|
||||||
|
def prep(self, shared):
|
||||||
|
"""No special preparation needed to list calendars."""
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def exec(self, params):
|
||||||
|
"""Lists all available calendars for the user."""
|
||||||
|
try:
|
||||||
|
calendars = list_calendar_lists()
|
||||||
|
return {'success': True, 'calendars': calendars}
|
||||||
|
except Exception as e:
|
||||||
|
return {'success': False, 'error': str(e)}
|
||||||
|
|
||||||
|
def post(self, shared, prep_res, exec_res):
|
||||||
|
"""Stores the list of calendars in the shared store."""
|
||||||
|
if exec_res['success']:
|
||||||
|
shared['available_calendars'] = exec_res['calendars']
|
||||||
|
return 'success'
|
||||||
|
else:
|
||||||
|
shared['error'] = exec_res['error']
|
||||||
|
return 'error'
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
from google.oauth2.credentials import Credentials
|
||||||
|
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||||
|
from google.auth.transport.requests import Request
|
||||||
|
from googleapiclient.discovery import build
|
||||||
|
import os.path
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
CALENDAR_ID = os.getenv('GOOGLE_CALENDAR_ID')
|
||||||
|
GOOGLE_APPLICATION_CREDENTIALS = os.getenv('GOOGLE_APPLICATION_CREDENTIALS')
|
||||||
|
TIMEZONE = os.getenv('TIMEZONE')
|
||||||
|
|
||||||
|
SCOPES = ['https://www.googleapis.com/auth/calendar']
|
||||||
|
|
||||||
|
def get_calendar_service():
|
||||||
|
"""Gets the authenticated Google Calendar service."""
|
||||||
|
creds = None
|
||||||
|
if os.path.exists('token.pickle'):
|
||||||
|
with open('token.pickle', 'rb') as token:
|
||||||
|
creds = pickle.load(token)
|
||||||
|
|
||||||
|
if not creds or not creds.valid:
|
||||||
|
if creds and creds.expired and creds.refresh_token:
|
||||||
|
creds.refresh(Request())
|
||||||
|
else:
|
||||||
|
flow = InstalledAppFlow.from_client_secrets_file(
|
||||||
|
GOOGLE_APPLICATION_CREDENTIALS, SCOPES)
|
||||||
|
creds = flow.run_local_server(port=0)
|
||||||
|
with open('token.pickle', 'wb') as token:
|
||||||
|
pickle.dump(creds, token)
|
||||||
|
|
||||||
|
return build('calendar', 'v3', credentials=creds)
|
||||||
|
|
||||||
|
def create_event(summary, description, start_time, end_time, timezone=TIMEZONE):
|
||||||
|
"""Creates a new event in Google Calendar."""
|
||||||
|
service = get_calendar_service()
|
||||||
|
|
||||||
|
event = {
|
||||||
|
'summary': summary,
|
||||||
|
'description': description,
|
||||||
|
'start': {
|
||||||
|
'dateTime': start_time.isoformat(),
|
||||||
|
'timeZone': timezone,
|
||||||
|
},
|
||||||
|
'end': {
|
||||||
|
'dateTime': end_time.isoformat(),
|
||||||
|
'timeZone': timezone,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
event = service.events().insert(calendarId=CALENDAR_ID, body=event).execute()
|
||||||
|
return event
|
||||||
|
|
||||||
|
def list_events(days=7):
|
||||||
|
"""Lists events for the next X days."""
|
||||||
|
service = get_calendar_service()
|
||||||
|
|
||||||
|
now = datetime.utcnow()
|
||||||
|
time_min = now.isoformat() + 'Z'
|
||||||
|
time_max = (now + timedelta(days=days)).isoformat() + 'Z'
|
||||||
|
|
||||||
|
events_result = service.events().list(
|
||||||
|
calendarId=CALENDAR_ID,
|
||||||
|
timeMin=time_min,
|
||||||
|
timeMax=time_max,
|
||||||
|
singleEvents=True,
|
||||||
|
orderBy='startTime'
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
return events_result.get('items', [])
|
||||||
|
|
||||||
|
def create_custom_calendar(calendar_name, description=""):
|
||||||
|
"""Creates a new custom calendar in Google Calendar."""
|
||||||
|
service = get_calendar_service()
|
||||||
|
|
||||||
|
calendar = {
|
||||||
|
'summary': calendar_name,
|
||||||
|
'description': description,
|
||||||
|
'timeZone': TIMEZONE
|
||||||
|
}
|
||||||
|
|
||||||
|
created_calendar = service.calendars().insert(body=calendar).execute()
|
||||||
|
return created_calendar
|
||||||
|
|
||||||
|
def list_calendar_lists():
|
||||||
|
"""Lists all available calendars for the user."""
|
||||||
|
service = get_calendar_service()
|
||||||
|
|
||||||
|
calendar_list = service.calendarList().list().execute()
|
||||||
|
return calendar_list.get('items', [])
|
||||||
Loading…
Reference in New Issue