From 65ed14858c28facee8b206f72a8a39c5bb098b20 Mon Sep 17 00:00:00 2001 From: mst Date: Fri, 28 Feb 2025 07:29:14 +0300 Subject: [PATCH] type annotations and return types, gif emoji support --- functions.py | 68 +++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/functions.py b/functions.py index d3bc37c..1c17e2c 100644 --- a/functions.py +++ b/functions.py @@ -60,7 +60,7 @@ def saveJSON(dict, file_path): json.dump(dict, file, indent=4) # append to a json file -def appendToJSON(new_data, file_path): +def appendToJSON(new_data, file_path) -> bool: try: # open the file path = Path(file_path) @@ -80,16 +80,16 @@ def appendToJSON(new_data, file_path): cfg = loadJSON(const.configFile) -def formatRelativeTime(date_str): +def formatRelativeTime(date_str: str) -> str: date_format = "%Y-%m-%d %H:%M:%S" - past_date = datetime.strptime(date_str, date_format) + past_date = datetime.strptime(date_str, date_format).replace(tzinfo=None) now = datetime.now() time_difference = now - past_date return humanize.naturaltime(time_difference) -def formatRelativeTime2(date_str): +def formatRelativeTime2(date_str: str) -> str: date_format = "%Y-%m-%dT%H:%M:%SZ" past_date = None @@ -195,7 +195,8 @@ def getAllQuestions(limit: int = None, offset: int = None) -> dict: return combined, metadata -def addQuestion(from_who, question, cw, noAntispam=False): + +def addQuestion(from_who: str, question: str, cw: str, noAntispam: bool = False) -> dict: if cfg['antispam']['type'] == 'basic': antispam = request.form.get('antispam', '') @@ -345,15 +346,15 @@ def readPlainFile(file, split=False): else: return [] -def savePlainFile(file, contents): +def savePlainFile(file, contents) -> None: with open(file, 'w') as file: file.write(contents) -def getRandomWord(): +def getRandomWord() -> str: items = readPlainFile(const.antiSpamFile, split=True) return random.choice(items) -def trimContent(var, trim): +def trimContent(var, trim) -> str: trim = int(trim) if trim > 0: trimmed = var[:trim] + '…' if len(var) >= trim else var @@ -394,7 +395,7 @@ def find_emoji_path(emoji_name): head = to_snake_case(emoji_name).split('_')[0] if any(Path(EMOJI_BASE_PATH).glob(f'{head}.json')): for json_file in Path(EMOJI_BASE_PATH).glob('*.json'): - app.logger.debug("\n[CatAsk/functions/find_emoji_path] Using JSON meta file\n") + app.logger.debug("[CatAsk/functions/find_emoji_path] Using JSON meta file") pack_data = loadJSON(json_file) emojis = pack_data.get('emojis', []) @@ -407,12 +408,13 @@ def find_emoji_path(emoji_name): else: for address, dirs, files in os.walk(EMOJI_BASE_PATH): - app.logger.debug("\n[CatAsk/functions/find_emoji_path] Falling back to scanning directories\n") - if f"{emoji_name}.png" in files: - rel_dir = os.path.relpath(address, EMOJI_BASE_PATH) - emoji_path = os.path.join("static/emojis", rel_dir, f"{emoji_name}.png") - emoji_cache[emoji_name] = emoji_path - return emoji_path + app.logger.debug("[CatAsk/functions/find_emoji_path] Falling back to scanning directories") + for file in files: + if os.path.splitext(file)[0] == emoji_name: # Check if the filename matches the emoji_name + rel_dir = os.path.relpath(address, EMOJI_BASE_PATH) + emoji_path = os.path.join("static/emojis", rel_dir, file) # Use the actual file name + emoji_cache[emoji_name] = emoji_path + return emoji_path return None @@ -438,14 +440,14 @@ def emoji(md): if md.renderer and md.renderer.NAME == 'html': md.renderer.register('emoji', render_emoji) -def listEmojis(): +def listEmojis() -> list: emojis = [] emoji_base_path = Path.cwd() / 'static' / 'emojis' # Iterate over files that are directly in the emoji base path (not in subdirectories) for file in emoji_base_path.iterdir(): # Only include files, not directories - if file.is_file() and file.suffix in {'.png', '.jpg', '.jpeg', '.webp'}: + if file.is_file() and file.suffix in {'.png', '.jpg', '.jpeg', '.webp', '.gif'}: # Get the relative path and name for the emoji relative_path = os.path.relpath(file, emoji_base_path) emojis.append({ @@ -456,7 +458,7 @@ def listEmojis(): return emojis -def listEmojiPacks(): +def listEmojiPacks() -> list: emoji_packs = [] emoji_base_path = const.emojiPath @@ -473,7 +475,7 @@ def listEmojiPacks(): pack_data = loadJSON(meta_json_path) emoji_packs.append({ - 'name': pack_data.get('name', pack_dir.name).capitalize(), + 'name': pack_data.get('name', pack_dir.name), 'exportedAt': pack_data.get('exportedAt', 'Unknown'), 'preview_image': pack_data.get('preview_image', ''), 'website': pack_data.get('website', ''), @@ -486,13 +488,13 @@ def listEmojiPacks(): preview_image = None # Find the first image in the directory for preview for file in pack_dir.iterdir(): - if file.suffix in {'.png', '.jpg', '.jpeg', '.webp'}: + if file.suffix in {'.png', '.jpg', '.jpeg', '.webp', '.gif'}: preview_image = os.path.join('static/emojis', relative_path, file.name) break # Append pack info without meta.json emoji_packs.append({ - 'name': pack_dir.name.capitalize(), + 'name': pack_dir.name, 'preview_image': preview_image, 'relative_path': f'static/emojis/{relative_path}' }) @@ -500,10 +502,10 @@ def listEmojiPacks(): return emoji_packs -def processEmojis(meta_json_path): +def processEmojis(meta_json_path) -> list: emoji_metadata = loadJSON(meta_json_path) emojis = emoji_metadata.get('emojis', []) - pack_name = emoji_metadata['emojis'][0]['emoji']['category'].capitalize() + pack_name = emoji_metadata['emojis'][0]['emoji']['category'] exported_at = emoji_metadata.get('exportedAt', 'Unknown') website = emoji_metadata.get('host', '') preview_image = os.path.join('static/emojis', pack_name.lower(), emoji_metadata['emojis'][0]['fileName']) @@ -534,7 +536,7 @@ def processEmojis(meta_json_path): return processed_emojis -def renderMarkdown(text, allowed_tags=None): +def renderMarkdown(text: str, allowed_tags: bool = None) -> str: plugins = [ 'strikethrough', button, @@ -589,7 +591,7 @@ def renderMarkdown(text, allowed_tags=None): clean_html = cleaner.clean(html) return Markup(clean_html) -def generateMetadata(question=None, answer=None): +def generateMetadata(question: str = None, answer: str = None) -> dict: metadata = { 'title': cfg['instance']['title'], 'description': cfg['instance']['description'], @@ -609,23 +611,23 @@ def generateMetadata(question=None, answer=None): # return 'metadata' dictionary return metadata -allowedFileExtensions = {'png', 'jpg', 'jpeg', 'webp', 'bmp', 'jxl'} +allowedFileExtensions = {'png', 'jpg', 'jpeg', 'webp', 'bmp', 'jxl', 'gif'} allowedArchiveExtensions = {'zip', 'tar', 'gz', 'bz2', 'xz'} -def allowedFile(filename): +def allowedFile(filename: str) -> bool: return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowedFileExtensions -def allowedArchive(filename): +def allowedArchive(filename: str) -> bool: return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowedArchiveExtensions -def stripArchExtension(filename): +def stripArchExtension(filename: str) -> str: if filename.endswith(('.tar.gz', '.tar.bz2', '.tar.xz')): filename = filename.rsplit('.', 2)[0] else: filename = filename.rsplit('.', 1)[0] return filename -def generateFavicon(file_name): +def generateFavicon(file_name: str) -> None: sizes = { 'apple-touch-icon.png': (180, 180), 'android-chrome-192x192.png': (192, 192), @@ -645,7 +647,7 @@ def generateFavicon(file_name): resized_img_absolute_path = const.faviconDir / filename resized_img.save(resized_img_absolute_path) -def createExport(): +def createExport() -> dict: try: # just to test if connection works conn = connectToDb() @@ -783,14 +785,14 @@ def retrospringImport(export_file): conn.close() """ -def deleteExport(timestamp): +def deleteExport(timestamp: str) -> dict: try: export_file = Path('static') / 'exports' / f'export-{timestamp}.zip' data = loadJSON(const.exportsFile) data = [export for export in data if export["timestamp_esc"] != timestamp] export_file.unlink() saveJSON(data, const.exportsFile) - return {'message': f'Export {timestamp} deleted successfully.'} + return {'message': _('Export {} deleted successfully.').format(timestamp)} except Exception as e: return {'error': str(e)}, 500