Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split post table into notifiable posts and context #85

Merged
merged 67 commits into from
Apr 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
3c37702
Notify of posts that the user already responded to
rossjrw Oct 1, 2023
5e7c82d
Create most of data structure migration
rossjrw Jan 17, 2024
d1c2176
Clarify a couple of join names
rossjrw Jan 17, 2024
3ce4099
Use new structure for post context cache
rossjrw Jan 17, 2024
5f95e37
Update wiki DB addition to new data format
rossjrw Jan 23, 2024
879a802
Add commented-out table drop
rossjrw Jan 23, 2024
696950c
Update devcontainer config until it works
rossjrw Jan 26, 2024
420773d
Return timestamps from RSS parsing
rossjrw Jan 26, 2024
d6ab091
Replace post existence with timestamp get
rossjrw Jan 26, 2024
b6847fa
Return only meta and one page from thread iterator
rossjrw Jan 26, 2024
aa8e861
Verify behaviour when post ID is not in thread
rossjrw Jan 26, 2024
608a924
Rewrite post and context storage methods
rossjrw Jan 28, 2024
188a1f4
Make count_pages also return the current page
rossjrw Jan 28, 2024
d94d53f
Rewrite DB post store method for new structure
rossjrw Jan 29, 2024
34ac649
Rewrite post acquisition for context-based structure
rossjrw Jan 29, 2024
d2d6c48
Begin writing user notification query
rossjrw Jan 30, 2024
e94dccc
Replace posts queries with big CTE selector
rossjrw Jan 31, 2024
22c4c50
Make wiki and thread joins mandatory
rossjrw Feb 1, 2024
5fa20e2
Stop counting notified threads
rossjrw Feb 1, 2024
2f3aa82
Make posts DB method use generic list query
rossjrw Feb 1, 2024
11e1334
Add TOML extension
rossjrw Feb 1, 2024
f0e34fb
Pluralise the digest subject
rossjrw Feb 1, 2024
7eebade
Conjoin lines
rossjrw Feb 1, 2024
7214d96
Remove auto posts from cached user config
rossjrw Feb 1, 2024
3037b34
Fix email links by moving them above the italics parser
rossjrw Feb 2, 2024
c74dafa
Test to ensure full interpolation of all languages
rossjrw Feb 2, 2024
9a36ac3
Add emails to interpolation test
rossjrw Feb 2, 2024
fd8be74
Rewrite en to remove auto subs count
rossjrw Feb 3, 2024
32aec01
Add link to user account in email
rossjrw Feb 3, 2024
3b25980
Test that links and italics work together
rossjrw Feb 3, 2024
c6edda6
Rewrite test data to match new structure
rossjrw Feb 3, 2024
c7464fb
Remove references to old tables
rossjrw Feb 4, 2024
284b51f
Fix down migration
rossjrw Feb 4, 2024
213c4d4
Fix select syntax
rossjrw Feb 4, 2024
0726aef
Make post subscriptions apply to parent posts
rossjrw Feb 4, 2024
b9706b0
Allow post replies to bypass thread unsubscriptions
rossjrw Feb 4, 2024
c55dc31
Add parent posts to test data
rossjrw Feb 4, 2024
4ae3a2d
Make tests fit the new data structure
rossjrw Feb 4, 2024
67498e0
Remove test for ignoring a responded-to post
rossjrw Feb 4, 2024
0a96ab4
Fix cache query name
rossjrw Feb 4, 2024
2547dbf
Merge a couple tests that were split by prior context
rossjrw Feb 4, 2024
8691bd0
Remove timestamp filter from post cache
rossjrw Feb 4, 2024
3b164a4
Add parent posts for second batch of test data
rossjrw Feb 4, 2024
794723a
Prevent italicising after colon
rossjrw Feb 5, 2024
a366de1
Modify digester for new structure
rossjrw Feb 5, 2024
6abe308
Add more formatting tests
rossjrw Feb 5, 2024
3934928
Amend post reply tag to not imply it's your post
rossjrw Feb 5, 2024
875c7fb
Replace subqueries with joins
rossjrw Feb 7, 2024
963e0a1
Move migration filter into delete query
rossjrw Feb 7, 2024
c918e32
Filter users last notified about current post
rossjrw Feb 8, 2024
0db407b
Add context timestamp index
rossjrw Feb 8, 2024
89aa3cc
Remove ownership from post replies
rossjrw Feb 13, 2024
4efb016
Fix deletion query
rossjrw Feb 9, 2024
fed4bb3
Remove test statements and wrap DML only in transaction
rossjrw Feb 9, 2024
75e9c97
Make post delete script also delete context
rossjrw Feb 9, 2024
06e7447
Split post deletion and context deletion
rossjrw Mar 13, 2024
bf36a05
Make now a module global
rossjrw Mar 28, 2024
8832d09
Rewrite post deletion check and syncer
rossjrw Apr 20, 2024
2fccfbf
Split interpolation tests by language (en/non-en)
rossjrw Apr 21, 2024
3f20afb
Do not notify of post replies in unsubbed threads
rossjrw Apr 21, 2024
fdfcb6e
Include test channel in initial posts
rossjrw Apr 24, 2024
38082b5
Add status to error message
rossjrw Apr 24, 2024
4dfe3a7
Raise an error if not logged in
rossjrw Apr 25, 2024
c5500ca
Raise error if login fails
rossjrw Apr 25, 2024
9008cc4
Pluralise digest subject
rossjrw Apr 25, 2024
e7dc8cd
Remove old parmaters from translations
rossjrw Apr 26, 2024
1a41ca0
Run deletion checks on every run
rossjrw Apr 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/devcontainers/python:0-3.8-bullseye
FROM mcr.microsoft.com/devcontainers/python:0-3.8

RUN apt-get install libxml2-dev libxslt-dev

Expand Down
31 changes: 20 additions & 11 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"name": "Python 3",
"build": { "dockerfile": "Dockerfile" },
"build": {
"dockerfile": "Dockerfile"
},
"features": {
"ghcr.io/devcontainers-contrib/features/poetry:2": {
"version": "latest"
Expand All @@ -12,27 +14,34 @@
"settings": {
"python.defaultInterpreterPath": "./.venv/bin/python",
"python.linting.pylintPath": "./.venv/bin/pylint",
"python.analysis.extraPaths": [".venv/bin"],
"python.linting.mypyEnabled": true,
"python.linting.enabled": true,
"python.linting.mypyArgs": ["--strict", "--show-column-numbers"],
"python.linting.mypyPath": "./.venv/bin/mypy",
"python.analysis.extraPaths": [
".venv/bin"
],
"python.formatting.blackPath": "./.venv/bin/black",
"black-formatter.path": [
"./.venv/bin/black"
],
"editor.defaultFormatter": "ms-python.black-formatter",
"mypy.runUsingActiveInterpreter": true
"mypy.runUsingActiveInterpreter": true,
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
},
"python.formatting.provider": "none",
"editor.formatOnSave": true,
"mypy-type-checker.args": [
"--strict"
],
"mypy-type-checker.importStrategy": "fromEnvironment"
},
"extensions": [
"ms-azuretools.vscode-docker",
"ms-python.black-formatter",
"matangover.mypy",
"ms-python.vscode-pylance",
"ms-python.python",
"ms-python.pylint"
"ms-python.pylint",
"ms-python.mypy-type-checker",
"tamasfe.even-better-toml"
]
}
},
"remoteUser": "root"
}
}
167 changes: 51 additions & 116 deletions config/lang.toml

Large diffs are not rendered by default.

18 changes: 14 additions & 4 deletions notifier/config/user.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
from operator import itemgetter
import re
from typing import List, Optional, Tuple, Union, cast
from typing import List, Optional, Tuple, TypedDict, Union, cast

import tomlkit
from tomlkit.exceptions import TOMLKitError
Expand Down Expand Up @@ -154,7 +155,9 @@ def parse_subscriptions(
# (Empty rows are permitted so this is valid)
continue
try:
thread_id, post_id = parse_thread_url(url)
thread_id, post_id = itemgetter("thread_id", "post_id")(
parse_thread_url(url)
)
except ValueError:
# The URL parser is extremely forgiving, so this should not
# happen; if it somehow does, discard it
Expand All @@ -165,11 +168,18 @@ def parse_subscriptions(
return subscriptions


def parse_thread_url(url: str) -> Tuple[str, Union[str, None]]:
ThreadPostIds = TypedDict(
"ThreadPostIds", {"thread_id": str, "post_id": Union[str, None]}
)


def parse_thread_url(
url: str,
) -> ThreadPostIds:
"""Parses a URL to a thread ID and optionally a post ID."""
pattern = re.compile(r"(t-[0-9]+)(?:.*#(post-[0-9]+))?")
match = pattern.search(url)
if not match:
raise ValueError("Thread URL does not match expected pattern")
thread_id, post_id = match.groups()
return thread_id, post_id
return {"thread_id": thread_id, "post_id": post_id}
89 changes: 45 additions & 44 deletions notifier/database/drivers/base.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
from abc import ABC, abstractmethod
from typing import Any, Iterable, List, Tuple
from typing import Any, List, Tuple

from notifier.types import (
ActivationLogDump,
CachedUserConfig,
ChannelLogDump,
LogDump,
NewPostsInfo,
RawPost,
NotifiablePost,
PostInfo,
PostMeta,
RawUserConfig,
SupportedWikiConfig,
ThreadInfo,
Context,
)


Expand All @@ -37,34 +38,15 @@ def create_tables(self) -> None:
"""Initial setup for the database."""

@abstractmethod
def find_new_posts(self, post_ids: Iterable[str]) -> List[str]:
"""From a list of post IDs, return those that are not already
present in the cache."""
def get_latest_post_timestamp(self) -> int:
"""Returns the timestamp of the latest stored post."""

@abstractmethod
def find_new_threads(self, thread_ids: Iterable[str]) -> List[str]:
"""From a list of thread IDs, return those that are not already
present in the cache."""

@abstractmethod
def mark_thread_as_deleted(self, thread_id: str) -> None:
"""Marks a thread as deleted, preventing its posts from appearing
in notifications."""

@abstractmethod
def mark_post_as_deleted(self, post_id: str) -> None:
"""Marks a post as deleted, preventing it from appearing in
notifications. Also mark its children as deleted, recursively."""

@abstractmethod
def get_new_posts_for_user(
def get_notifiable_posts_for_user(
self, user_id: str, timestamp_range: Tuple[int, int]
) -> NewPostsInfo:
) -> List[PostInfo]:
"""Get new posts for the users with the given ID made during the
given time range.

Returns a dict containing the thread posts and the post replies.
"""
given time range."""

@abstractmethod
def get_user_configs(self, frequency: str) -> List[CachedUserConfig]:
Expand All @@ -80,13 +62,20 @@ def count_user_configs(self) -> int:
"""Count the number of subscribed users."""

@abstractmethod
def get_notifiable_users(
self, frequency: str, post_lower_timestamp_limit: int
) -> List[str]:
def get_notifiable_users(self, frequency: str) -> List[str]:
"""Get the list of IDs for users subscribed to the given channel
frequency who have at least one notification waiting for them.
"""

@abstractmethod
def get_posts_to_check_for_deletion(
self, timestamp: int
) -> List[PostMeta]:
"""Get a list of posts to check for having potentially been deleted.

Timestamp is assumed to be time to check relative to.
"""

@abstractmethod
def store_user_configs(
self,
Expand Down Expand Up @@ -117,30 +106,42 @@ def store_user_last_notified(
def get_supported_wikis(self) -> List[SupportedWikiConfig]:
"""Get a list of supported wikis."""

@abstractmethod
def count_supported_wikis(self) -> int:
"""Count the number of supported wikis."""

@abstractmethod
def store_supported_wikis(self, wikis: List[SupportedWikiConfig]) -> None:
"""Stores a set of supported wikis in the database, overwriting any
that are already present."""

@abstractmethod
def store_thread(self, thread: ThreadInfo) -> None:
"""Store a thread.
def store_post(self, post: NotifiablePost) -> None:
"""Store a post."""

Doesn't matter if the thread or category is already known or not.
"""
@abstractmethod
def store_context_forum_category(
self, context_forum_category: Context.ForumCategory
) -> None:
"""Store a forum category for context."""

@abstractmethod
def store_thread_first_post(self, thread_id: str, post_id: str) -> None:
"""Store the relationship between a thread and the first post it
contains."""
def store_context_thread(self, context_thread: Context.Thread) -> None:
"""Store a thread for context."""

@abstractmethod
def store_post(self, post: RawPost) -> None:
"""Store a post."""
def store_context_parent_post(
self, context_parent_post: Context.ParentPost
) -> None:
"""Store a parent post for context."""

@abstractmethod
def delete_post(self, post_id: str) -> None:
"""Delete a post."""

@abstractmethod
def delete_non_notifiable_posts(self) -> None:
"""Delete posts that will not emit notifications."""

@abstractmethod
def delete_context_thread(self, thread_id: str) -> None:
"""Delete posts with the given thread context."""

@abstractmethod
def store_channel_log_dump(self, log: ChannelLogDump) -> None:
Expand Down
Loading
Loading