implemented tick function and fixed the bloody hell database structure before :D
This commit is contained in:
		| @@ -1,6 +1,6 @@ | |||||||
| import os | import os | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler | from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler, CallbackContext | ||||||
| from telegram import InlineKeyboardButton, InlineKeyboardMarkup | from telegram import InlineKeyboardButton, InlineKeyboardMarkup | ||||||
|  |  | ||||||
| from .models import ListModel, set_db, db | from .models import ListModel, set_db, db | ||||||
| @@ -10,7 +10,7 @@ DB_DIR = MEDIA_DIR / "lists_db" | |||||||
| DB_DIR.mkdir(parents=True, exist_ok=True) | DB_DIR.mkdir(parents=True, exist_ok=True) | ||||||
|  |  | ||||||
|  |  | ||||||
| NAME, NEW, ACTION, ITEMADD, ITEMREMOVE = range(5) | NAME, NEW, ACTION, ITEMADD, ITEMREMOVE, ITEMTOGGLE = range(6) | ||||||
|  |  | ||||||
| from ..basehandler import BaseHandler | from ..basehandler import BaseHandler | ||||||
|  |  | ||||||
| @@ -21,6 +21,16 @@ class ListHandler(BaseHandler): | |||||||
|     def __init__(self, entry_string, models): |     def __init__(self, entry_string, models): | ||||||
|         self.journal_models = models # not needed here |         self.journal_models = models # not needed here | ||||||
|         self.entry_string = entry_string |         self.entry_string = entry_string | ||||||
|  |  | ||||||
|  |         self.list_overview_keyboard = [ | ||||||
|  |             [InlineKeyboardButton("Add item", callback_data="add")], | ||||||
|  |             [InlineKeyboardButton("Toggle item", callback_data="toggle")], | ||||||
|  |             [InlineKeyboardButton("Remove item", callback_data="remove")], | ||||||
|  |             [InlineKeyboardButton("Clear list", callback_data="clear")], | ||||||
|  |             [InlineKeyboardButton("Print list", callback_data="print")], | ||||||
|  |             [InlineKeyboardButton("Delete list", callback_data="delete")], | ||||||
|  |              | ||||||
|  |         ] | ||||||
|         self.handler = ConversationHandler( |         self.handler = ConversationHandler( | ||||||
|             entry_points=[CommandHandler(entry_string, self.entry_point)], |             entry_points=[CommandHandler(entry_string, self.entry_point)], | ||||||
|             states={ |             states={ | ||||||
| @@ -31,6 +41,7 @@ class ListHandler(BaseHandler): | |||||||
|                 NEW : [MessageHandler(filters.TEXT, callback=self.new_listname)], |                 NEW : [MessageHandler(filters.TEXT, callback=self.new_listname)], | ||||||
|                 ACTION: [ |                 ACTION: [ | ||||||
|                     CallbackQueryHandler(self.list_add, pattern="^add$"), |                     CallbackQueryHandler(self.list_add, pattern="^add$"), | ||||||
|  |                     CallbackQueryHandler(self.list_toggle, pattern="^toggle$"), | ||||||
|                     CallbackQueryHandler(self.list_remove, pattern="^remove$"), |                     CallbackQueryHandler(self.list_remove, pattern="^remove$"), | ||||||
|                     CallbackQueryHandler(self.list_clear, pattern="^clear$"), |                     CallbackQueryHandler(self.list_clear, pattern="^clear$"), | ||||||
|                     CallbackQueryHandler(self.list_delete, pattern="^delete$"), |                     CallbackQueryHandler(self.list_delete, pattern="^delete$"), | ||||||
| @@ -38,6 +49,7 @@ class ListHandler(BaseHandler): | |||||||
|                     CallbackQueryHandler(self.list_menu, pattern="^overview$"), |                     CallbackQueryHandler(self.list_menu, pattern="^overview$"), | ||||||
|                     ], |                     ], | ||||||
|                 ITEMADD : [MessageHandler(filters.TEXT, callback=self.list_add_item)], |                 ITEMADD : [MessageHandler(filters.TEXT, callback=self.list_add_item)], | ||||||
|  |                 ITEMTOGGLE: [CallbackQueryHandler(self.list_toggle_index)], | ||||||
|                 ITEMREMOVE : [CallbackQueryHandler(self.list_remove_index)] |                 ITEMREMOVE : [CallbackQueryHandler(self.list_remove_index)] | ||||||
|             }, |             }, | ||||||
|             fallbacks=[CommandHandler('list', self.entry_point)], |             fallbacks=[CommandHandler('list', self.entry_point)], | ||||||
| @@ -57,21 +69,14 @@ class ListHandler(BaseHandler): | |||||||
|         return NAME |         return NAME | ||||||
|  |  | ||||||
|  |  | ||||||
|     async def choose_list(self, update, context) -> None: |     async def choose_list(self, update, context: CallbackContext) -> None: | ||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         data = query.data |         data = query.data | ||||||
|         name = data.replace("list-","") |         name = data.replace("list-","") | ||||||
|         await query.answer() |         await query.answer() | ||||||
|         self.current_name = name |         context.user_data["current_list"] = ListModel.get(name = name) | ||||||
|  |  | ||||||
|         keyboard = [ |         reply_markup = InlineKeyboardMarkup(self.list_overview_keyboard) | ||||||
|             [InlineKeyboardButton("Add item", callback_data="add")], |  | ||||||
|             [InlineKeyboardButton("Remove item", callback_data="remove")], |  | ||||||
|             [InlineKeyboardButton("Clear list", callback_data="clear")], |  | ||||||
|             [InlineKeyboardButton("Print list", callback_data="print")], |  | ||||||
|             [InlineKeyboardButton("Delete list", callback_data="delete")], |  | ||||||
|         ] |  | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |  | ||||||
|  |  | ||||||
|         await query.edit_message_text("Very well. For " + name + " the following actions are available:", reply_markup=reply_markup) |         await query.edit_message_text("Very well. For " + name + " the following actions are available:", reply_markup=reply_markup) | ||||||
|         return ACTION |         return ACTION | ||||||
| @@ -81,16 +86,9 @@ class ListHandler(BaseHandler): | |||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         await query.answer() |         await query.answer() | ||||||
|  |  | ||||||
|         keyboard = [ |         reply_markup = InlineKeyboardMarkup(self.list_overview_keyboard) | ||||||
|             [InlineKeyboardButton("Add item", callback_data="add")], |  | ||||||
|             [InlineKeyboardButton("Remove item", callback_data="remove")], |  | ||||||
|             [InlineKeyboardButton("Clear list", callback_data="clear")], |  | ||||||
|             [InlineKeyboardButton("Print list", callback_data="print")], |  | ||||||
|             [InlineKeyboardButton("Delete list", callback_data="delete")], |  | ||||||
|         ] |  | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |  | ||||||
|  |  | ||||||
|         await query.edit_message_text("Very well. For " + self.current_name + " the following actions are available:", reply_markup=reply_markup) |         await query.edit_message_text(f"Very well. For {context.user_data['current_list'].name} the following actions are available:", reply_markup=reply_markup) | ||||||
|         return ACTION |         return ACTION | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -105,10 +103,9 @@ class ListHandler(BaseHandler): | |||||||
|         name = update.message.text |         name = update.message.text | ||||||
|         try: |         try: | ||||||
|             with db: |             with db: | ||||||
|                 ListModel.create(name = name) |                 context.user_data["current_list"] = ListModel.create(name = name) | ||||||
|             keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("To the menu!", callback_data="overview")]] |             keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("To the menu!", callback_data="overview")]] | ||||||
|             reply_markup = InlineKeyboardMarkup(keyboard) |             reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|             self.current_name = name |  | ||||||
|             await update.message.reply_text("Thanks. List " + name + " was successfully created.", reply_markup=reply_markup) |             await update.message.reply_text("Thanks. List " + name + " was successfully created.", reply_markup=reply_markup) | ||||||
|             return ACTION |             return ACTION | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
| @@ -121,15 +118,26 @@ class ListHandler(BaseHandler): | |||||||
|         await query.answer() |         await query.answer() | ||||||
|         await query.edit_message_text("What would you like to add?") |         await query.edit_message_text("What would you like to add?") | ||||||
|         return ITEMADD |         return ITEMADD | ||||||
|  |      | ||||||
|  |      | ||||||
|  |     async def list_toggle(self, update, context) -> None: | ||||||
|  |         query = update.callback_query | ||||||
|  |         await query.answer() | ||||||
|  |  | ||||||
|  |         list_object = context.user_data["current_list"] | ||||||
|  |         keyboard = [[InlineKeyboardButton(v, callback_data=k)] for k,v in list_object.content.items()] | ||||||
|  |         reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|  |  | ||||||
|  |         await query.edit_message_text("Which item would you like to toggle?", reply_markup = reply_markup) | ||||||
|  |         return ITEMTOGGLE | ||||||
|  |  | ||||||
|  |  | ||||||
|     async def list_remove(self, update, context) -> None: |     async def list_remove(self, update, context) -> None: | ||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         await query.answer() |         await query.answer() | ||||||
|         with db: |  | ||||||
|             list_object = ListModel.get(name = self.current_name) |  | ||||||
|  |  | ||||||
|         keyboard = [[InlineKeyboardButton(k, callback_data=i)] for i,k in enumerate(list_object.content_list)] |         list_object = context.user_data["current_list"] | ||||||
|  |         keyboard = [[InlineKeyboardButton(v, callback_data=k)] for k,v in list_object.content.items()] | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |         reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|  |  | ||||||
|         await query.edit_message_text("Which item would you like to remove?", reply_markup = reply_markup) |         await query.edit_message_text("Which item would you like to remove?", reply_markup = reply_markup) | ||||||
| @@ -139,63 +147,92 @@ class ListHandler(BaseHandler): | |||||||
|     async def list_clear(self, update, context) -> None: |     async def list_clear(self, update, context) -> None: | ||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         await query.answer() |         await query.answer() | ||||||
|         with db: |  | ||||||
|             ListModel.get(name = self.current_name).content_list = [] |         list_object = context.user_data["current_list"] | ||||||
|  |         list_object.content = {} | ||||||
|         keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] |         keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |         reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|         await query.edit_message_text("List " + self.current_name + " cleared", reply_markup=reply_markup) |         await query.edit_message_text(f"List {list_object.name} cleared", reply_markup=reply_markup) | ||||||
|         return ACTION |         return ACTION | ||||||
|  |  | ||||||
|  |  | ||||||
|     async def list_delete(self, update, context) -> None: |     async def list_delete(self, update, context) -> None: | ||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         await query.answer() |         await query.answer() | ||||||
|         with db: |         list_object = context.user_data["current_list"] | ||||||
|             ListModel.get(name = self.current_name).delete_instance() |         list_object.delete_instance() | ||||||
|         await query.edit_message_text("List " + self.current_name + " deleted") |         await query.edit_message_text(f"List {list_object.name} deleted") | ||||||
|         return ConversationHandler.END |         return ConversationHandler.END | ||||||
|  |  | ||||||
|  |  | ||||||
|     async def list_print(self, update, context) -> None: |     async def list_print(self, update, context) -> None: | ||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         await query.answer() |         await query.answer() | ||||||
|         with db: |         list_object = context.user_data["current_list"] | ||||||
|             it = ListModel.get(name = self.current_name).content_list |  | ||||||
|         if it: |         content_it = list_object.content.values() | ||||||
|             content = "·" + "\n· ".join(it) |         done_it = [ | ||||||
|  |             "· " if e is None \ | ||||||
|  |             else "✅ " if e \ | ||||||
|  |             else "❌ " \ | ||||||
|  |             for e in list_object.done_dict.values()] | ||||||
|  |         if content_it: | ||||||
|  |             msg_content = "\n".join([f"{d} {c}" for d, c in zip(done_it, content_it)]) | ||||||
|         else: |         else: | ||||||
|             content = "List empty" |             msg_content = "List empty" | ||||||
|          |          | ||||||
|         keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] |         keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |         reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|         await query.edit_message_text("Content of " + self.current_name + ":\n" + content, reply_markup=reply_markup) |         await query.edit_message_text(f"Content of {list_object.name}:\n{msg_content}", reply_markup=reply_markup) | ||||||
|         return ACTION |         return ACTION | ||||||
|  |  | ||||||
|  |  | ||||||
|     async def list_add_item(self, update, context) -> None: |     async def list_add_item(self, update, context) -> None: | ||||||
|         item = update.message.text |         item = update.message.text | ||||||
|         with db: |         list_object = context.user_data["current_list"] | ||||||
|             ListModel.get(name = self.current_name).content_list = ListModel.get(name = self.current_name).content_list + [item] |         new = list_object.content | ||||||
|  |         new.update({"random_key": item}) | ||||||
|  |         list_object.content = new | ||||||
|         # TODO test me! |         # TODO test me! | ||||||
|         keyboard = [[InlineKeyboardButton("Add some more", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] |         keyboard = [[InlineKeyboardButton("Add some more", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard) |         reply_markup = InlineKeyboardMarkup(keyboard) | ||||||
|         await update.message.reply_text("Added " + item, reply_markup=reply_markup) |         await update.message.reply_text(f"Added {item}", reply_markup=reply_markup) | ||||||
|         return ACTION |         return ACTION | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     async def list_toggle_index(self, update, context) -> None: | ||||||
|  |         query = update.callback_query | ||||||
|  |         toggle_key = int(query.data) | ||||||
|  |         await query.answer() | ||||||
|  |  | ||||||
|  |         list_object = context.user_data["current_list"] | ||||||
|  |         old = list_object.done_dict[toggle_key] | ||||||
|  |         # if all None or all False (first toggle or all false) then set all dones to False | ||||||
|  |         if not any(list_object.done_dict.values()): | ||||||
|  |             new_done_dict = dict.fromkeys(list_object.done_dict, False) | ||||||
|  |         else: new_done_dict = list_object.done_dict | ||||||
|  |         new_done_dict[toggle_key] = not old | ||||||
|  |         list_object.done_dict = new_done_dict | ||||||
|  |  | ||||||
|  |         keyboard = [[InlineKeyboardButton("Toggle another", callback_data="toggle"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] | ||||||
|  |         reply_markup = InlineKeyboardMarkup(keyboard)       | ||||||
|  |  | ||||||
|  |         await query.edit_message_text(f"Toggled {list_object.content[toggle_key]}", reply_markup=reply_markup) | ||||||
|  |         return ACTION | ||||||
|  |      | ||||||
|  |  | ||||||
|     async def list_remove_index(self, update, context) -> None: |     async def list_remove_index(self, update, context) -> None: | ||||||
|         query = update.callback_query |         query = update.callback_query | ||||||
|         ind = int(query.data) |         ind = int(query.data) | ||||||
|         await query.answer() |         await query.answer() | ||||||
|  |  | ||||||
|         with db: |         list_object = context.user_data["current_list"] | ||||||
|             list_object = ListModel.get(name = self.current_name) |         old = list_object.content | ||||||
|             old = list_object.content_list |         name = old.pop(ind) | ||||||
|             name = old.pop(ind) |         list_object.content = old | ||||||
|             list_object.content_list = old |  | ||||||
|  |  | ||||||
|         keyboard = [[InlineKeyboardButton("Remove another", callback_data="remove"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] |         keyboard = [[InlineKeyboardButton("Remove another", callback_data="remove"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] | ||||||
|         reply_markup = InlineKeyboardMarkup(keyboard)       |         reply_markup = InlineKeyboardMarkup(keyboard)       | ||||||
|  |  | ||||||
|         await query.edit_message_text("Removed " + name, reply_markup=reply_markup) |         await query.edit_message_text(f"Removed {name}", reply_markup=reply_markup) | ||||||
|         return ACTION |         return ACTION | ||||||
|   | |||||||
| @@ -1,30 +1,67 @@ | |||||||
| from peewee import * | from peewee import * | ||||||
| import json |  | ||||||
|  |  | ||||||
| db = DatabaseProxy() | db = DatabaseProxy() | ||||||
|  |  | ||||||
| class BaseModel(Model): | class BaseModel(Model): | ||||||
|     class Meta: |     class Meta: | ||||||
|         database = db |         database = db | ||||||
|         db_table = 'lists' |  | ||||||
|  |  | ||||||
| class ListModel(BaseModel): | class ListModel(BaseModel): | ||||||
|     name = CharField(unique=True) |     name = CharField(default="") | ||||||
|     content = TextField(default="") # unlimited length, use to serialise list into |  | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def content_list(self): |     def content(self) -> dict: | ||||||
|         return json.loads(self.content or '[]') |         return {e.id: e.entry for e in self.entries} | ||||||
|      |      | ||||||
|     @content_list.setter |     @content.setter | ||||||
|     def content_list(self, list_content): |     def content(self, new_content: dict): | ||||||
|         self.content = json.dumps(list_content) |         old_content = self.content | ||||||
|         with db: |         if len(old_content) < len(new_content): | ||||||
|             self.save() |             # we assume: only 1 item added (last item) | ||||||
|  |             new_item = list(new_content.values())[-1] | ||||||
|  |             ListEntryModel.create(list_model=self, entry = new_item) | ||||||
|  |  | ||||||
|  |         elif len(old_content) > len(new_content): | ||||||
|  |             to_delete_ids = set(old_content.keys()) - set(new_content.keys()) | ||||||
|  |             ListEntryModel.delete().where(ListEntryModel.id.in_(list(to_delete_ids))).execute() | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def done_dict(self): | ||||||
|  |         return {e.id: e.done for e in self.entries} | ||||||
|  |      | ||||||
|  |     @done_dict.setter | ||||||
|  |     def done_dict(self, new_done: dict): | ||||||
|  |         old_done_dict = self.done_dict | ||||||
|  |         for k,d in new_done.items(): | ||||||
|  |             if d != old_done_dict[k]: | ||||||
|  |                 ListEntryModel.update(done = d).where(ListEntryModel.id == k).execute() | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class ListEntryModel(BaseModel): | ||||||
|  |     list_model = ForeignKeyField(ListModel, backref="entries", on_delete="CASCADE") | ||||||
|  |     entry = TextField(default="") | ||||||
|  |     done = BooleanField(default=None, null=True) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # class ListModel(BaseModel): | ||||||
|  | #     name = CharField(unique=True) | ||||||
|  | #     content = TextField(default="") # unlimited length, use to serialise list into | ||||||
|  |  | ||||||
|  | #     @property | ||||||
|  | #     def content_list(self): | ||||||
|  | #         return json.loads(self.content or '[]') | ||||||
|  |      | ||||||
|  | #     @content_list.setter | ||||||
|  | #     def content_list(self, list_content): | ||||||
|  | #         self.content = json.dumps(list_content) | ||||||
|  | #         with db: | ||||||
|  | #             self.save() | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def set_db(db_path): | def set_db(db_path): | ||||||
|     db.initialize(SqliteDatabase(db_path)) |     db.initialize(SqliteDatabase(db_path)) | ||||||
|     with db: |     with db: | ||||||
|         db.create_tables([ListModel], safe=True) |         db.create_tables([ListModel, ListEntryModel], safe=True) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Lia Schöneweiß
					Lia Schöneweiß