wip
This commit is contained in:
61
hubstaff-cli/README.md
Normal file
61
hubstaff-cli/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Hubstaff CLI
|
||||
|
||||
This project is a command-line interface (CLI) tool for fetching timesheet data from the Hubstaff API. It provides a simple way to interact with the API and retrieve timesheet information formatted according to specified requirements.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Output Format](#output-format)
|
||||
- [Dependencies](#dependencies)
|
||||
|
||||
## Installation
|
||||
|
||||
1. Clone the repository:
|
||||
```
|
||||
git clone <repository-url>
|
||||
cd hubstaff-cli
|
||||
```
|
||||
|
||||
2. Install the required dependencies:
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. Set up your environment variables for the Hubstaff API:
|
||||
- Create a `.env` file in the root directory and add your Hubstaff API key:
|
||||
```
|
||||
HUBSTAFF_API_KEY=your_api_key_here
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
To run the timesheet data fetching script, execute the following command:
|
||||
|
||||
```
|
||||
python src/timesheet.py
|
||||
```
|
||||
|
||||
This will log in to the Hubstaff API and fetch the timesheet data for the specified user and date range.
|
||||
|
||||
## Output Format
|
||||
|
||||
The output will be displayed in a tabular format with the following columns:
|
||||
|
||||
- **Date**: The date of the time entry.
|
||||
- **Started At**: The time the entry started.
|
||||
- **Tracked (minutes)**: The total minutes tracked for the entry.
|
||||
|
||||
Example output:
|
||||
|
||||
```
|
||||
| Date | Started At | Tracked (minutes) |
|
||||
|------------|------------|--------------------|
|
||||
| 2023-10-01 | 09:00 | 120 |
|
||||
| 2023-10-01 | 13:00 | 60 |
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `requests`: For making HTTP requests to the Hubstaff API.
|
||||
- `python-dotenv`: For loading environment variables from a `.env` file.
|
||||
2
hubstaff-cli/requirements.txt
Normal file
2
hubstaff-cli/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
requests
|
||||
python-dotenv
|
||||
32
hubstaff-cli/src/services/hubstaff_api.py
Normal file
32
hubstaff-cli/src/services/hubstaff_api.py
Normal file
@@ -0,0 +1,32 @@
|
||||
class HubstaffAPI:
|
||||
def __init__(self, refresh_token: str):
|
||||
self.refresh_token = refresh_token
|
||||
self.base_url = "https://api.hubstaff.com/v2"
|
||||
self.bearer_token = None
|
||||
|
||||
async def fetch_bearer_token(self):
|
||||
# Logic to fetch bearer token using the refresh token
|
||||
pass
|
||||
|
||||
async def get_current_user(self):
|
||||
# Logic to get current user information
|
||||
pass
|
||||
|
||||
async def get_activities(self, organization_id: int, user_id: int, start: str, stop: str):
|
||||
# Logic to fetch activities from Hubstaff API
|
||||
pass
|
||||
|
||||
async def get_timesheets(self, organization_id: int, user_id: int, start: str, stop: str):
|
||||
# Logic to fetch timesheet data from Hubstaff API
|
||||
url = f"{self.base_url}/organizations/{organization_id}/users/{user_id}/timesheets"
|
||||
params = {
|
||||
"start": start,
|
||||
"stop": stop
|
||||
}
|
||||
# Make an API request to fetch timesheets
|
||||
response = await self._make_request(url, params)
|
||||
return response
|
||||
|
||||
async def _make_request(self, url: str, params: dict):
|
||||
# Logic to make an HTTP request to the Hubstaff API
|
||||
pass
|
||||
112
hubstaff-cli/src/timesheet.py
Normal file
112
hubstaff-cli/src/timesheet.py
Normal file
@@ -0,0 +1,112 @@
|
||||
from services.hubstaff_api import HubstaffAPI
|
||||
import os
|
||||
import shutil
|
||||
import asyncio
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
async def main():
|
||||
# Load the Hubstaff API key from environment variables
|
||||
api_key = os.getenv("HUBSTAFF_API_KEY")
|
||||
if not api_key:
|
||||
print("Error: HUBSTAFF_API_KEY environment variable not set.")
|
||||
return
|
||||
|
||||
# Load the Hubstaff organization ID from environment variables
|
||||
org_id = os.getenv("HUBSTAFF_ORG_ID")
|
||||
if not org_id:
|
||||
print("Error: HUBSTAFF_ORG_ID environment variable not set.")
|
||||
return
|
||||
|
||||
# Initialize the Hubstaff API client
|
||||
hubstaff = HubstaffAPI(refresh_token=api_key)
|
||||
|
||||
try:
|
||||
# Fetch user info
|
||||
user_info = await hubstaff.get_current_user()
|
||||
user_id = user_info["user"]["id"]
|
||||
|
||||
# Define the date range for the timesheet
|
||||
end_date = datetime.now()
|
||||
start_date = end_date - timedelta(days=7)
|
||||
|
||||
# Fetch timesheet data
|
||||
timesheet_data = await hubstaff.get_timesheets(
|
||||
user_id, start=start_date.isoformat(), stop=end_date.isoformat()
|
||||
)
|
||||
|
||||
# Check if timesheet_data is valid and "times" is a list before proceeding
|
||||
times = timesheet_data.get("times") if timesheet_data else None
|
||||
if not times or not isinstance(times, list):
|
||||
print("Error: No timesheet data found or invalid format received.")
|
||||
return
|
||||
|
||||
# Prepare invoice configuration based on timesheet data
|
||||
invoice_logo = os.getenv("INVOICE_LOGO", "/path/to/logo.png")
|
||||
invoice_from = os.getenv("INVOICE_FROM", "Default Company")
|
||||
invoice_to = os.getenv("INVOICE_TO", "Client Company")
|
||||
invoice_tax = float(os.getenv("INVOICE_TAX", "0.0"))
|
||||
invoice_rate = float(os.getenv("INVOICE_RATE", "25"))
|
||||
|
||||
config = {
|
||||
"logo": invoice_logo,
|
||||
"from": invoice_from,
|
||||
"to": invoice_to,
|
||||
"tax": invoice_tax,
|
||||
"items": [],
|
||||
"quantities": [],
|
||||
"rates": [],
|
||||
}
|
||||
|
||||
# For each timesheet entry, we'll use the note as item.
|
||||
# If note is missing or "N/A", we'll use the date instead.
|
||||
for entry in times:
|
||||
entry_date = datetime.fromtimestamp(int(entry["date"])).strftime("%Y-%m-%d")
|
||||
note = entry.get("note", "N/A")
|
||||
item = note if note != "N/A" else f"Work on {entry_date}"
|
||||
minutes = entry.get("minutes", 0)
|
||||
config["items"].append(item)
|
||||
config["quantities"].append(minutes)
|
||||
config["rates"].append(invoice_rate)
|
||||
|
||||
# Generate YAML output
|
||||
yaml_lines = [
|
||||
f"logo: {config['logo']}",
|
||||
f"from: {config['from']}",
|
||||
f"to: {config['to']}",
|
||||
f"tax: {config['tax']}",
|
||||
"items:",
|
||||
]
|
||||
for item in config["items"]:
|
||||
yaml_lines.append(f' - "{item}"')
|
||||
yaml_lines.append("quantities:")
|
||||
for qty in config["quantities"]:
|
||||
yaml_lines.append(f" - {qty}")
|
||||
yaml_lines.append("rates:")
|
||||
for rate in config["rates"]:
|
||||
yaml_lines.append(f" - {rate}")
|
||||
|
||||
yaml_content = "\n".join(yaml_lines)
|
||||
config_filename = "invoice_config.yaml"
|
||||
|
||||
with open(config_filename, "w") as f:
|
||||
f.write(yaml_content)
|
||||
|
||||
print(f"Invoice configuration written to {config_filename}")
|
||||
|
||||
# Call the invoice command if available locally
|
||||
if shutil.which("invoice"):
|
||||
os.system(
|
||||
f"invoice generate --import {config_filename} --output timesheet-invoice.pdf"
|
||||
)
|
||||
else:
|
||||
print(
|
||||
"Invoice command not found locally. Please install it to generate invoices."
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error fetching timesheet data: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user