diff --git a/sijapi/routers/asr.py b/sijapi/routers/asr.py index ea2a2f3..f26cee3 100644 --- a/sijapi/routers/asr.py +++ b/sijapi/routers/asr.py @@ -81,6 +81,7 @@ async def transcribe_endpoint( # If we've reached this point, the transcription has taken too long return JSONResponse(content={"status": "timeout", "message": "Transcription is taking longer than expected. Please check back later."}, status_code=202) + async def transcribe_audio(file_path, params: TranscribeParams): debug(f"Transcribing audio file from {file_path}...") file_path = await convert_to_wav(file_path) diff --git a/sijapi/routers/img.py b/sijapi/routers/img.py index faadd1a..f878271 100644 --- a/sijapi/routers/img.py +++ b/sijapi/routers/img.py @@ -6,29 +6,20 @@ DEPENDS ON: *unimplemented. ''' -from fastapi import APIRouter, Request, Response, Query -from starlette.datastructures import Address +from fastapi import APIRouter, Request, Query from fastapi.responses import JSONResponse, RedirectResponse -from fastapi.staticfiles import StaticFiles -from aiohttp import ClientSession, ClientTimeout +from aiohttp import ClientSession import aiofiles from PIL import Image from pathlib import Path import uuid import json import yaml -import ipaddress import socket import subprocess -import os, re, io import random -from io import BytesIO -import base64 +import os import asyncio -import shutil -# from photoprism.Session import Session -# from photoprism.Photo import Photo -# from webdav3.client import Client from sijapi.routers.llm import query_ollama from sijapi import API, L, COMFYUI_URL, COMFYUI_OUTPUT_DIR, IMG_CONFIG_PATH, IMG_DIR, IMG_WORKFLOWS_DIR @@ -245,6 +236,7 @@ def get_scene(scene): return scene_data return None + # This returns the scene with the most trigger words present in the provided prompt, # or otherwise if none match it returns the first scene in the array - # meaning the first should be considered the default scene. @@ -269,11 +261,6 @@ def get_matching_scene(prompt): -import asyncio -import socket -import subprocess -from typing import Optional - async def ensure_comfy(retries: int = 4, timeout: float = 6.0): """ Ensures that ComfyUI is running, starting it if necessary. @@ -311,35 +298,6 @@ async def ensure_comfy(retries: int = 4, timeout: float = 6.0): crit(f"Failed to ensure ComfyUI is running after {retries} attempts with {timeout} second intervals.") raise RuntimeError(f"Failed to ensure ComfyUI is running after {retries} attempts with {timeout} second intervals.") -# async def upload_and_get_shareable_link(image_path): - # try: - # Set up the PhotoPrism session - # pp_session = Session(PHOTOPRISM_USER, PHOTOPRISM_PASS, PHOTOPRISM_URL, use_https=True) - # pp_session.create() - - # Start import - # photo = Photo(pp_session) - # photo.start_import(path=os.path.dirname(image_path)) - - # Give PhotoPrism some time to process the upload - # await asyncio.sleep(5) - - # Search for the uploaded photo - # photo_name = os.path.basename(image_path) - # search_results = photo.search(query=f"name:{photo_name}", count=1) - - # if search_results['photos']: - # photo_uuid = search_results['photos'][0]['uuid'] - # shareable_link = f"https://{PHOTOPRISM_URL}/p/{photo_uuid}" - # return shareable_link - # else: - # err("Could not find the uploaded photo details.") - # return None - # except Exception as e: - # err(f"Error in upload_and_get_shareable_link: {e}") - # return None - - @img.get("/image/{prompt_id}") async def get_image_status(prompt_id: str): @@ -455,7 +413,6 @@ Even more important, it finds and returns the key to the filepath where the file return found_key[0] - async def queue_prompt(workflow_data): await ensure_comfy() async with ClientSession() as session: @@ -465,4 +422,4 @@ async def queue_prompt(workflow_data): data = await response.json() return data.get('prompt_id') else: - raise Exception(f"Failed to queue prompt. Status code: {response.status}") + raise Exception(f"Failed to queue prompt. Status code: {response.status}") \ No newline at end of file diff --git a/sijapi/routers/news.py b/sijapi/routers/news.py index 6ce3b3f..24c6193 100644 --- a/sijapi/routers/news.py +++ b/sijapi/routers/news.py @@ -23,7 +23,6 @@ from sijapi import L, News, OBSIDIAN_VAULT_DIR, OBSIDIAN_RESOURCES_DIR, DEFAULT_ from sijapi.utilities import sanitize_filename, assemble_journal_path, assemble_archive_path from sijapi.routers import gis, llm, tts, note - news = APIRouter() logger = L.get_module_logger("news") def debug(text: str): logger.debug(text) @@ -407,188 +406,3 @@ async def save_file(file: UploadFile, folder: Path) -> Path: with open(file_path, 'wb') as f: shutil.copyfileobj(file.file, f) return file_path - - -deprecated = ''' -async def process_article2( - bg_tasks: BackgroundTasks, - parsed_content: Article, - tts_mode: str = "summary", - voice: str = DEFAULT_11L_VOICE -): - timestamp = dt_datetime.now().strftime('%b %d, %Y at %H:%M') - - readable_title = sanitize_filename(parsed_content.title or timestamp) - markdown_filename, relative_path = assemble_journal_path(dt_datetime.now(), subdir="Articles", filename=readable_title, extension=".md") - - try: - summary = await llm.summarize_text(parsed_content.clean_doc, "Summarize the provided text. Respond with the summary and nothing else. Do not otherwise acknowledge the request. Just provide the requested summary.") - summary = summary.replace('\n', ' ') # Remove line breaks - - if tts_mode == "full" or tts_mode == "content": - tts_text = parsed_content.clean_doc - elif tts_mode == "summary" or tts_mode == "excerpt": - tts_text = summary - else: - tts_text = None - - banner_markdown = '' - try: - banner_url = parsed_content.top_image - if banner_url != '': - banner_image = download_file(banner_url, Path(OBSIDIAN_VAULT_DIR / OBSIDIAN_RESOURCES_DIR)) - if banner_image: - banner_markdown = f"![[{OBSIDIAN_RESOURCES_DIR}/{banner_image}]]" - - except Exception as e: - err(f"No image found in article") - - authors = ', '.join('[[{}]]'.format(author) for author in parsed_content.authors) - published_date = parsed_content.publish_date - frontmatter = f"""--- -title: {readable_title} -authors: {authors} -published: {published_date} -added: {timestamp} -banner: "{banner_markdown}" -tags: - -""" - frontmatter += '\n'.join(f" - {tag}" for tag in parsed_content.tags) - frontmatter += '\n---\n' - - body = f"# {readable_title}\n\n" - if tts_text: - audio_filename = f"{published_date} {readable_title}" - try: - audio_path = await tts.generate_speech(bg_tasks=bg_tasks, text=tts_text, voice=voice, model="eleven_turbo_v2", podcast=True, title=audio_filename, - output_dir=Path(OBSIDIAN_VAULT_DIR) / OBSIDIAN_RESOURCES_DIR) - audio_ext = Path(audio_path).suffix - obsidian_link = f"![[{OBSIDIAN_RESOURCES_DIR}/{audio_filename}{audio_ext}]]" - body += f"{obsidian_link}\n\n" - except Exception as e: - err(f"Failed to generate TTS for np3k. {e}") - - try: - body += f"by {authors} in {parsed_content.canonical_link}" # update with method for getting the newspaper name - body += f"> [!summary]+\n" - body += f"> {summary}\n\n" - body += parsed_content["content"] - markdown_content = frontmatter + body - - except Exception as e: - err(f"Failed to combine elements of article markdown.") - - try: - with open(markdown_filename, 'w') as md_file: - md_file.write(markdown_content) - - info(f"Successfully saved to {markdown_filename}") - await note.add_to_daily_note(relative_path) - return markdown_filename - - except Exception as e: - err(f"Failed to write markdown file") - raise HTTPException(status_code=500, detail=str(e)) - - except Exception as e: - err(f"Failed to clip: {str(e)}") - raise HTTPException(status_code=500, detail=str(e)) - - -async def process_article( - bg_tasks: BackgroundTasks, - url: str, - title: Optional[str] = None, - encoding: str = 'utf-8', - source: Optional[str] = None, - tts_mode: str = "summary", - voice: str = DEFAULT_11L_VOICE -): - - timestamp = dt_datetime.now().strftime('%b %d, %Y at %H:%M') - - parsed_content = await parse_article(url, source) - if parsed_content is None: - return {"error": "Failed to retrieve content"} - - readable_title = sanitize_filename(title or parsed_content.get("title") or timestamp) - markdown_filename, relative_path = assemble_journal_path(dt_datetime.now(), subdir="Articles", filename=readable_title, extension=".md") - - try: - summary = await llm.summarize_text(parsed_content["content"], "Summarize the provided text. Respond with the summary and nothing else. Do not otherwise acknowledge the request. Just provide the requested summary.") - summary = summary.replace('\n', ' ') # Remove line breaks - - if tts_mode == "full" or tts_mode == "content": - tts_text = parsed_content["content"] - elif tts_mode == "summary" or tts_mode == "excerpt": - tts_text = summary - else: - tts_text = None - - banner_markdown = '' - try: - banner_url = parsed_content.get('image', '') - if banner_url != '': - banner_image = download_file(banner_url, Path(OBSIDIAN_VAULT_DIR / OBSIDIAN_RESOURCES_DIR)) - if banner_image: - banner_markdown = f"![[{OBSIDIAN_RESOURCES_DIR}/{banner_image}]]" - - except Exception as e: - err(f"No image found in article") - - authors = ', '.join('[[{}]]'.format(author) for author in parsed_content.get('authors', ['Unknown'])) - - frontmatter = f"""--- -title: {readable_title} -authors: {', '.join('[[{}]]'.format(author) for author in parsed_content.get('authors', ['Unknown']))} -published: {parsed_content.get('date_published', 'Unknown')} -added: {timestamp} -excerpt: {parsed_content.get('excerpt', '')} -banner: "{banner_markdown}" -tags: - -""" - frontmatter += '\n'.join(f" - {tag}" for tag in parsed_content.get('tags', [])) - frontmatter += '\n---\n' - - body = f"# {readable_title}\n\n" - - if tts_text: - datetime_str = dt_datetime.now().strftime("%Y%m%d%H%M%S") - audio_filename = f"{datetime_str} {readable_title}" - try: - audio_path = await tts.generate_speech(bg_tasks=bg_tasks, text=tts_text, voice=voice, model="eleven_turbo_v2", podcast=True, title=audio_filename, - output_dir=Path(OBSIDIAN_VAULT_DIR) / OBSIDIAN_RESOURCES_DIR) - audio_ext = Path(audio_path).suffix - obsidian_link = f"![[{OBSIDIAN_RESOURCES_DIR}/{audio_filename}{audio_ext}]]" - body += f"{obsidian_link}\n\n" - except Exception as e: - err(f"Failed to generate TTS for np3k. {e}") - - try: - body += f"by {authors} in [{parsed_content.get('domain', urlparse(url).netloc.replace('www.', ''))}]({url}).\n\n" - body += f"> [!summary]+\n" - body += f"> {summary}\n\n" - body += parsed_content["content"] - markdown_content = frontmatter + body - - except Exception as e: - err(f"Failed to combine elements of article markdown.") - - try: - with open(markdown_filename, 'w', encoding=encoding) as md_file: - md_file.write(markdown_content) - - info(f"Successfully saved to {markdown_filename}") - await note.add_to_daily_note(relative_path) - return markdown_filename - - except Exception as e: - err(f"Failed to write markdown file") - raise HTTPException(status_code=500, detail=str(e)) - - except Exception as e: - err(f"Failed to clip {url}: {str(e)}") - raise HTTPException(status_code=500, detail=str(e)) -''' \ No newline at end of file diff --git a/sijapi/routers/note.py b/sijapi/routers/note.py index 54b5813..173c03c 100644 --- a/sijapi/routers/note.py +++ b/sijapi/routers/note.py @@ -383,7 +383,6 @@ created: "{dt_datetime.now().strftime("%Y-%m-%d %H:%M:%S")}" - async def build_daily_timeslips(date): ''' @@ -956,17 +955,6 @@ async def format_events_as_markdown(event_data: Dict[str, Union[str, List[Dict[s if event['description']: description = event['description'] - # # This was intended to clean up the descriptions of Zoom and Teams events but is presently broken; should be an easy fix. - # if 'Zoom Meeting' in description: - # description_parts = description.split('---') - # if len(description_parts) > 2: - # description = description_parts[1].strip() - # if 'Microsoft Teams' in description: - # description_parts = description.split('---') - # if len(description_parts) > 2: - # event_data['description'] = description_parts[1].strip() - # description = remove_characters(description) - # description = remove_characters(description) if len(description) > 150: description = await llm.summarize_text(description, length_override=150) diff --git a/sijapi/routers/time.py b/sijapi/routers/time.py index 5f1672d..3c20319 100644 --- a/sijapi/routers/time.py +++ b/sijapi/routers/time.py @@ -572,10 +572,4 @@ def flag_emoji(country_code: str): @time.head("/time/") async def read_root(): - return {} - - -@time.get("/time/") -async def root(): - return {"message": "Ring, ring, ring, ring, ring, ring, ring. \n\n. Banana phone."} - + return {} \ No newline at end of file diff --git a/sijapi/routers/tts.py b/sijapi/routers/tts.py index 3a3951c..d6a0821 100644 --- a/sijapi/routers/tts.py +++ b/sijapi/routers/tts.py @@ -167,8 +167,6 @@ async def generate_speech( raise HTTPException(status_code=500, detail=f"Failed to generate speech: {str(e)}") - - async def get_model(voice: str = None, voice_file: UploadFile = None): if voice_file or (voice and await select_voice(voice)): return "xtts" diff --git a/sijapi/routers/weather.py b/sijapi/routers/weather.py index 55b091e..27eb1c8 100644 --- a/sijapi/routers/weather.py +++ b/sijapi/routers/weather.py @@ -2,6 +2,7 @@ Uses the VisualCrossing API and Postgres/PostGIS to source local weather forecasts and history. ''' import asyncio +import traceback from fastapi import APIRouter, HTTPException, Query from fastapi import HTTPException from fastapi.responses import JSONResponse @@ -166,7 +167,7 @@ async def store_weather_to_db(date_time: dt_datetime, weather_data: dict): try: daily_weather_query = ''' - INSERT INTO DailyWeather ( + INSERT INTO dailyweather ( sunrise, sunriseepoch, sunset, sunsetepoch, description, tempmax, tempmin, uvindex, winddir, windspeed, icon, last_updated, datetime, datetimeepoch, temp, feelslikemax, feelslikemin, feelslike, @@ -178,7 +179,7 @@ async def store_weather_to_db(date_time: dt_datetime, weather_data: dict): RETURNING id ''' - daily_weather_id = await API.execute_write_query(daily_weather_query, *daily_weather_params, table_name="DailyWeather") + daily_weather_id = await API.execute_write_query(daily_weather_query, *daily_weather_params, table_name="dailyweather") if 'hours' in day_data: debug(f"Processing hours now...") @@ -219,13 +220,13 @@ async def store_weather_to_db(date_time: dt_datetime, weather_data: dict): try: hourly_weather_query = ''' - INSERT INTO HourlyWeather (daily_weather_id, datetime, datetimeepoch, temp, feelslike, humidity, dew, precip, precipprob, + INSERT INTO hourlyweather (daily_weather_id, datetime, datetimeepoch, temp, feelslike, humidity, dew, precip, precipprob, preciptype, snow, snowdepth, windgust, windspeed, winddir, pressure, cloudcover, visibility, solarradiation, solarenergy, uvindex, severerisk, conditions, icon, stations, source) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26) RETURNING id ''' - hourly_weather_id = await API.execute_write_query(hourly_weather_query, *hourly_weather_params, table_name="HourlyWeather") + hourly_weather_id = await API.execute_write_query(hourly_weather_query, *hourly_weather_params, table_name="hourlyweather") debug(f"Done processing hourly_weather_id {hourly_weather_id}") except Exception as e: err(f"EXCEPTION: {e}") @@ -241,60 +242,42 @@ async def store_weather_to_db(date_time: dt_datetime, weather_data: dict): - async def get_weather_from_db(date_time: dt_datetime, latitude: float, longitude: float): warn(f"Using {date_time.strftime('%Y-%m-%d %H:%M:%S')} as our datetime in get_weather_from_db.") - async with API.get_connection() as conn: - query_date = date_time.date() - try: - # Query to get daily weather data - query = ''' - SELECT DW.* FROM DailyWeather DW - WHERE DW.datetime::date = $1 - AND ST_DWithin(DW.location::geography, ST_MakePoint($2,$3)::geography, 8046.72) - ORDER BY ST_Distance(DW.location, ST_MakePoint($4, $5)::geography) ASC - LIMIT 1 - ''' + query_date = date_time.date() + try: + # Query to get daily weather data + daily_query = ''' + SELECT DW.* FROM dailyweather DW + WHERE DW.datetime::date = $1 + AND ST_DWithin(DW.location::geography, ST_MakePoint($2,$3)::geography, 8046.72) + ORDER BY ST_Distance(DW.location, ST_MakePoint($4, $5)::geography) ASC + LIMIT 1 + ''' - daily_weather_record = await conn.fetchrow(query, query_date, longitude, latitude, longitude, latitude) - - if daily_weather_record is None: - debug(f"No daily weather data retrieved from database.") - return None - - # Convert asyncpg.Record to a mutable dictionary - daily_weather_data = dict(daily_weather_record) - # debug(f"Daily weather data prior to tz corrections: {daily_weather_data}") - # Now we can modify the dictionary - # tz = await GEO.tz_at(latitude, longitude) - # daily_weather_data['datetime'] = await gis.dt(daily_weather_data.get('datetime'), tz) - # daily_weather_data['sunrise'] = await gis.dt(daily_weather_data.get('sunrise'), tz) - # daily_weather_data['sunset'] = await gis.dt(daily_weather_data.get('sunset'), tz) - - # debug(f"Daily weather data after tz corrections: {daily_weather_data}") - - # Query to get hourly weather data - query = ''' - SELECT HW.* FROM HourlyWeather HW - WHERE HW.daily_weather_id = $1 - ''' - - hourly_weather_records = await conn.fetch(query, daily_weather_data['id']) - - hourly_weather_data = [] - for record in hourly_weather_records: - hour_data = dict(record) - # hour_data['datetime'] = await gis.dt(hour_data.get('datetime'), tz) - hourly_weather_data.append(hour_data) - - # debug(f"Hourly weather data after tz corrections: {hourly_weather_data}") - day = { - 'DailyWeather': daily_weather_data, - 'HourlyWeather': hourly_weather_data, - } - # debug(f"day: {day}") - return day - except Exception as e: - err(f"Unexpected error occurred: {e}") + daily_weather_records = await API.execute_read_query(daily_query, query_date, longitude, latitude, longitude, latitude, table_name='dailyweather') + if not daily_weather_records: + debug(f"No daily weather data retrieved from database.") + return None + daily_weather_data = daily_weather_records[0] # Get the first (and only) record + + # Query to get hourly weather data + hourly_query = ''' + SELECT HW.* FROM hourlyweather HW + WHERE HW.daily_weather_id = $1 + ''' + + hourly_weather_records = await API.execute_read_query(hourly_query, daily_weather_data['id'], table_name='hourlyweather') + + day = { + 'DailyWeather': daily_weather_data, + 'HourlyWeather': hourly_weather_records, + } + + return day + except Exception as e: + err(f"Unexpected error occurred in get_weather_from_db: {e}") + err(f"Traceback: {traceback.format_exc()}") + return None