import sqlite3 import re def migrate(db, habitlist, events): c = db.cursor() habits = simple_habits_list(c, habitlist) repetitions = get_all_repetitions(habits, events) for rep in repetitions: add_to_database( c, rep["id"], rep["timestamp"], 2 if "value" not in rep else rep["value"] ) LOOP_RANGE_VALUE_MULTIPLIER = 1000 def get_all_repetitions(habits, events): """Return list of all repetitions found of habits in events passed in. Parameters: habits (list): Collection of habits, with minimum necessary fields description and id. events (list): Collection of events, with minimum necessary field end. Returns: repetitions (list): Collection of events transformed into Loop repetitions. Contains fields id, timestamp, value (for ranges). """ repetitions = [] for event in events: for habit in habits: reps = tags_to_repetitions(habit, extract_tags(event["note"]), event["end"]) if reps: repetitions.extend(reps) return repetitions def tags_to_repetitions(habit, tags, timestamp): """Return a list of all repetitions generated from the tags and habits passed in. Parameters: habits (list): Collection of habits, with minimum necessary fields description and id. tags (list): Collection of tag tuples. Returns: repetitions (list): Collection of habits for which a corresponding tag has been found. If they correspond that means at the timestamp the habit has been checked in, and a repetition is created. Contains fields id, timestamp, value (for ranges). """ reps = [] for tag in tags: if habit["description"] in tag[0]: repetition = {"id": habit["id"], "timestamp": timestamp} if tag[1]: if "type" in habit and habit["type"] == 1: repetition["value"] = tag[1] * LOOP_RANGE_VALUE_MULTIPLIER reps.append(repetition) return reps def extract_tags(text, tagmarker="#"): """Return lists of tuples of all event tags found in text. Parameters: text (str): The text to search through. tagmarker (str): Optional character marking beginning of tag, defaults to '#'. Returns: tags (list): List of tuples in the form [('tag', '3'), ('anothertag', '')]. """ string_tags = re.findall(rf"{tagmarker}(\w+)(?:\((\d+)\))?", text) tags_with_int_counters = [] for tag in string_tags: tags_with_int_counters.append((tag[0], None if tag[1] == "" else int(tag[1]))) return tags_with_int_counters # TODO possibly just get rid of this entirely def simple_habits_list(c, habitlist): """Return simplified collection of habits, only carrying name, tag, id. Parameters: c (sqlite.db.cursor): SQL cursor of database to query. habitlist (list): Full habit collection to return a simplified view of. Returns: habitlist (list): Simple habitlist, habit name, tag, and id. Copy of original, changes do not affect original. """ simple = [] for h in habitlist: simple.append( { "name": h["name"], "description": h["description"], "id": fetch_habit_id(c, h["uuid"]), } ) if "type" in h: simple[-1]["type"] = h["type"] return simple def fetch_habit_id(c, uuid): """Return sqlite internal id for habit with uuid. Parameters: c (sqlite.db.cursor): SQL cursor of database to query. uuid (str): Unique id of habit to query for. Returns: id (int): SQLite internal id for habit queried for. """ c.execute("select id from Habits where uuid = ?", ([uuid])) id = c.fetchone() if id is not None: return id[0] # does not do: # ranges in habits (should check for habit being range, then taking #habit(3) number into account) # also, range trackers: multiply value by 1,000 # non-range habits but still #habit(3) number included, adding multiple? def add_to_database(cursor, habit_id, timestamp, value=2): try: cursor.execute( """ INSERT INTO Repetitions(id, habit, timestamp, value) VALUES (NULL, ?, ?, ?) """, (habit_id, timestamp, value), ) except sqlite3.IntegrityError: # TODO better error handling print(f"fail to register {habit_id}: timestamp {timestamp} not unique")