habitmove/loop/repetitions.py

131 lines
4.5 KiB
Python

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")