feature (stats): Weekly and monthly goals.
Add a new editing interface for weekly and monthly goals. Textual viewing interface is currently a stub. Add `month_timestamp` and `week_timestamp` lion properties.
This commit is contained in:
@@ -146,6 +146,37 @@ class Lion:
|
|||||||
now = datetime.now(tz=self.timezone)
|
now = datetime.now(tz=self.timezone)
|
||||||
return now.replace(hour=0, minute=0, second=0, microsecond=0)
|
return now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def day_timestamp(self):
|
||||||
|
"""
|
||||||
|
EPOCH timestamp representing the current day for the user.
|
||||||
|
NOTE: This is the timestamp of the start of the current UTC day with the same date as the user's day.
|
||||||
|
This is *not* the start of the current user's day, either in UTC or their own timezone.
|
||||||
|
This may also not be the start of the current day in UTC (consider 23:00 for a user in UTC-2).
|
||||||
|
"""
|
||||||
|
now = datetime.now(tz=self.timezone)
|
||||||
|
day_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
return int(day_start.replace(tzinfo=pytz.utc).timestamp())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def week_timestamp(self):
|
||||||
|
"""
|
||||||
|
EPOCH timestamp representing the current week for the user.
|
||||||
|
"""
|
||||||
|
now = datetime.now(tz=self.timezone)
|
||||||
|
day_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
week_start = day_start - timedelta(days=day_start.weekday())
|
||||||
|
return int(week_start.replace(tzinfo=pytz.utc).timestamp())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def month_timestamp(self):
|
||||||
|
"""
|
||||||
|
EPOCH timestamp representing the current month for the user.
|
||||||
|
"""
|
||||||
|
now = datetime.now(tz=self.timezone)
|
||||||
|
month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
return int(month_start.replace(tzinfo=pytz.utc).timestamp())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def remaining_in_day(self):
|
def remaining_in_day(self):
|
||||||
return ((self.day_start + timedelta(days=1)) - datetime.now(self.timezone)).total_seconds()
|
return ((self.day_start + timedelta(days=1)) - datetime.now(self.timezone)).total_seconds()
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ from . import data
|
|||||||
from . import profile
|
from . import profile
|
||||||
from . import setprofile
|
from . import setprofile
|
||||||
from . import top_cmd
|
from . import top_cmd
|
||||||
|
from . import goals
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from data import Table
|
from cachetools import TTLCache
|
||||||
|
|
||||||
|
from data import Table, RowTable
|
||||||
|
|
||||||
|
|
||||||
profile_tags = Table('member_profile_tags', attach_as='profile_tags')
|
profile_tags = Table('member_profile_tags', attach_as='profile_tags')
|
||||||
@@ -11,3 +13,27 @@ def get_tags_for(guildid, userid):
|
|||||||
_extra="ORDER BY tagid ASC"
|
_extra="ORDER BY tagid ASC"
|
||||||
)
|
)
|
||||||
return [row['tag'] for row in rows]
|
return [row['tag'] for row in rows]
|
||||||
|
|
||||||
|
|
||||||
|
weekly_goals = RowTable(
|
||||||
|
'member_weekly_goals',
|
||||||
|
('guildid', 'userid', 'weekid', 'study_goal', 'task_goal'),
|
||||||
|
('guildid', 'userid', 'weekid'),
|
||||||
|
cache=TTLCache(5000, 60 * 60 * 24),
|
||||||
|
attach_as='weekly_goals'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: Not using a RowTable here since these will almost always be mass-selected
|
||||||
|
weekly_tasks = Table('member_weekly_goal_tasks')
|
||||||
|
|
||||||
|
|
||||||
|
monthly_goals = RowTable(
|
||||||
|
'member_monthly_goals',
|
||||||
|
('guildid', 'userid', 'monthid', 'study_goal', 'task_goal'),
|
||||||
|
('guildid', 'userid', 'monthid'),
|
||||||
|
cache=TTLCache(5000, 60 * 60 * 24),
|
||||||
|
attach_as='monthly_goals'
|
||||||
|
)
|
||||||
|
|
||||||
|
monthly_tasks = Table('member_monthly_goal_tasks')
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ async def cmd_setprofile(ctx, flags):
|
|||||||
# Ack with user
|
# Ack with user
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
colour=discord.Colour.green(),
|
colour=discord.Colour.green(),
|
||||||
description="Profile tags updated!"
|
title="Profile tags updated!"
|
||||||
)
|
)
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name="New tags",
|
name="New tags",
|
||||||
@@ -175,10 +175,14 @@ async def cmd_setprofile(ctx, flags):
|
|||||||
)
|
)
|
||||||
if deleted_rows:
|
if deleted_rows:
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name="Previous tags",
|
name="Replaced tags",
|
||||||
value='\n'.join(row['tag'].upper() for row in deleted_rows),
|
value='\n'.join(row['tag'].upper() for row in deleted_rows),
|
||||||
inline=False
|
inline=False
|
||||||
)
|
)
|
||||||
|
if len(to_add) == 1:
|
||||||
|
embed.set_footer(
|
||||||
|
text=f"TIP: Add multiple tags with {ctx.best_prefix}setprofile tag1, tag2, ..."
|
||||||
|
)
|
||||||
await ctx.reply(embed=embed)
|
await ctx.reply(embed=embed)
|
||||||
else:
|
else:
|
||||||
# No input was provided
|
# No input was provided
|
||||||
|
|||||||
@@ -21,6 +21,56 @@ CREATE TABLE member_profile_tags(
|
|||||||
_timestamp TIMESTAMPTZ DEFAULT now(),
|
_timestamp TIMESTAMPTZ DEFAULT now(),
|
||||||
FOREIGN KEY (guildid, userid) REFERENCES members (guildid, userid)
|
FOREIGN KEY (guildid, userid) REFERENCES members (guildid, userid)
|
||||||
);
|
);
|
||||||
|
CREATE INDEX member_profile_tags_members ON member_profile_tags (guildid, userid);
|
||||||
|
|
||||||
|
|
||||||
|
-- New member weekly and monthly goals
|
||||||
|
CREATE TABLE member_weekly_goals(
|
||||||
|
guildid BIGINT NOT NULL,
|
||||||
|
userid BIGINT NOT NULL,
|
||||||
|
weekid INTEGER NOT NULL, -- Epoch time of the start of the UTC week
|
||||||
|
study_goal INTEGER,
|
||||||
|
task_goal INTEGER,
|
||||||
|
_timestamp TIMESTAMPTZ DEFAULT now(),
|
||||||
|
PRIMARY KEY (guildid, userid, weekid),
|
||||||
|
FOREIGN KEY (guildid, userid) REFERENCES members (guildid, userid) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX member_weekly_goals_members ON member_weekly_goals (guildid, userid);
|
||||||
|
|
||||||
|
CREATE TABLE member_weekly_goal_tasks(
|
||||||
|
taskid SERIAL PRIMARY KEY,
|
||||||
|
guildid BIGINT NOT NULL,
|
||||||
|
userid BIGINT NOT NULL,
|
||||||
|
weekid INTEGER NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
completed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
_timestamp TIMESTAMPTZ DEFAULT now(),
|
||||||
|
FOREIGN KEY (weekid, guildid, userid) REFERENCES member_weekly_goals (weekid, guildid, userid) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX member_weekly_goal_tasks_members_weekly ON member_weekly_goal_tasks (guildid, userid, weekid);
|
||||||
|
|
||||||
|
CREATE TABLE member_monthly_goals(
|
||||||
|
guildid BIGINT NOT NULL,
|
||||||
|
userid BIGINT NOT NULL,
|
||||||
|
monthid INTEGER NOT NULL, -- Epoch time of the start of the UTC month
|
||||||
|
study_goal INTEGER,
|
||||||
|
task_goal INTEGER,
|
||||||
|
_timestamp TIMESTAMPTZ DEFAULT now(),
|
||||||
|
PRIMARY KEY (guildid, userid, monthid),
|
||||||
|
FOREIGN KEY (guildid, userid) REFERENCES members (guildid, userid) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX member_monthly_goals_members ON member_monthly_goals (guildid, userid);
|
||||||
|
|
||||||
|
CREATE TABLE member_monthly_goal_tasks(
|
||||||
|
taskid SERIAL PRIMARY KEY,
|
||||||
|
guildid BIGINT NOT NULL,
|
||||||
|
userid BIGINT NOT NULL,
|
||||||
|
monthid INTEGER NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
completed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
_timestamp TIMESTAMPTZ DEFAULT now(),
|
||||||
|
FOREIGN KEY (monthid, guildid, userid) REFERENCES member_monthly_goals (monthid, guildid, userid) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX member_monthly_goal_tasks_members_monthly ON member_monthly_goal_tasks (guildid, userid, monthid);
|
||||||
|
|
||||||
INSERT INTO VersionHistory (version, author) VALUES (7, 'v6-v7 migration');
|
INSERT INTO VersionHistory (version, author) VALUES (7, 'v6-v7 migration');
|
||||||
|
|||||||
@@ -692,9 +692,57 @@ CREATE TABLE member_profile_tags(
|
|||||||
_timestamp TIMESTAMPTZ DEFAULT now(),
|
_timestamp TIMESTAMPTZ DEFAULT now(),
|
||||||
FOREIGN KEY (guildid, userid) REFERENCES members (guildid, userid)
|
FOREIGN KEY (guildid, userid) REFERENCES members (guildid, userid)
|
||||||
);
|
);
|
||||||
|
CREATE INDEX member_profile_tags_members ON member_profile_tags (guildid, userid);
|
||||||
-- }}}
|
-- }}}
|
||||||
|
|
||||||
-- Member goals {{{
|
-- Member goals {{{
|
||||||
|
CREATE TABLE member_weekly_goals(
|
||||||
|
guildid BIGINT NOT NULL,
|
||||||
|
userid BIGINT NOT NULL,
|
||||||
|
weekid INTEGER NOT NULL, -- Epoch time of the start of the UTC week
|
||||||
|
study_goal INTEGER,
|
||||||
|
task_goal INTEGER,
|
||||||
|
_timestamp TIMESTAMPTZ DEFAULT now(),
|
||||||
|
PRIMARY KEY (guildid, userid, weekid),
|
||||||
|
FOREIGN KEY (guildid, userid) REFERENCES members (guildid, userid) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX member_weekly_goals_members ON member_weekly_goals (guildid, userid);
|
||||||
|
|
||||||
|
CREATE TABLE member_weekly_goal_tasks(
|
||||||
|
taskid SERIAL PRIMARY KEY,
|
||||||
|
guildid BIGINT NOT NULL,
|
||||||
|
userid BIGINT NOT NULL,
|
||||||
|
weekid INTEGER NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
completed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
_timestamp TIMESTAMPTZ DEFAULT now(),
|
||||||
|
FOREIGN KEY (weekid, guildid, userid) REFERENCES member_weekly_goals (weekid, guildid, userid) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX member_weekly_goal_tasks_members_weekly ON member_weekly_goal_tasks (guildid, userid, weekid);
|
||||||
|
|
||||||
|
CREATE TABLE member_monthly_goals(
|
||||||
|
guildid BIGINT NOT NULL,
|
||||||
|
userid BIGINT NOT NULL,
|
||||||
|
monthid INTEGER NOT NULL, -- Epoch time of the start of the UTC month
|
||||||
|
study_goal INTEGER,
|
||||||
|
task_goal INTEGER,
|
||||||
|
_timestamp TIMESTAMPTZ DEFAULT now(),
|
||||||
|
PRIMARY KEY (guildid, userid, monthid),
|
||||||
|
FOREIGN KEY (guildid, userid) REFERENCES members (guildid, userid) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX member_monthly_goals_members ON member_monthly_goals (guildid, userid);
|
||||||
|
|
||||||
|
CREATE TABLE member_monthly_goal_tasks(
|
||||||
|
taskid SERIAL PRIMARY KEY,
|
||||||
|
guildid BIGINT NOT NULL,
|
||||||
|
userid BIGINT NOT NULL,
|
||||||
|
monthid INTEGER NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
completed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
_timestamp TIMESTAMPTZ DEFAULT now(),
|
||||||
|
FOREIGN KEY (monthid, guildid, userid) REFERENCES member_monthly_goals (monthid, guildid, userid) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX member_monthly_goal_tasks_members_monthly ON member_monthly_goal_tasks (guildid, userid, monthid);
|
||||||
|
|
||||||
-- }}}
|
-- }}}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user