sextile.visits

What has been read, and how often.

A log of page fetches, and the two questions a service asks of one: what has been looked at lately, and what gets looked at most. Both are the same shape on every service, because a page number is the framework’s own vocabulary – so the log, the middleware that writes it and the pages that read it are here, and what a service adds is only its numbering.

A protocol, and one implementation of it. Visits is what the middleware and the pages talk to; SqliteVisits is the one that keeps a file. It is the same arrangement as every other impure edge in this framework: narrow enough to fake in a test, and a service that wants its log somewhere else writes forty lines rather than going without the pages.

The record is the address. 52<id> is what the reader keyed, what the router can parse back, and what label_for can name – so there is nothing else to store and nothing that can come to disagree with it. A prefix filter is then a namespace filter, which is what a first digit already means.

The caller is a token, not an address. Counting readers needs to know how many and nothing else. A random token minted per connection answers that and reveals nothing about who.

A page that was not there is logged too. A count of pages fetched that quietly omitted the ones nobody could reach would be the wrong count, and the numbers readers key wrongly are worth knowing. They are kept out of what is read back: a page that does not exist does not belong on a list of popular ones.

sextile.visits.RETENTION: Final = datetime.timedelta(days=30)

How long a visit is kept. Thirty days is what “lately” means; it is a setting rather than a rule, since a service with more readers may want less of it.

class sextile.visits.SqliteVisits(connection, *, retention=datetime.timedelta(days=30))[source]

Bases: object

The log, in a file of its own.

A file of its own rather than a table in a service’s own database, because a service’s own database is often derived and rebuilt, whereas a log is the only copy of what it holds.

Parameters:
class sextile.visits.Visit(page, at, times=1)[source]

Bases: object

One page, and what the log has to say about it.

Parameters:
at: datetime

The last time it was fetched.

times: int = 1

How often, over whatever period was asked for.

class sextile.visits.Visits(*args, **kwargs)[source]

Bases: Protocol

A log of what has been read.

Every method is asynchronous, because the one implementation that keeps a file does its work in a thread and a page must not wait on a disk.

async record(page, *, caller, found, at=None)[source]

Note that a page was fetched.

Parameters:
  • page (PageAddress) – The address that was fetched.

  • caller (str) – The opaque token naming the connection.

  • found (bool) – Whether the address named a page, so a miss is not read back.

  • at (datetime | None) – When it was fetched, or the current time.

Return type:

None

async recent(limit, *, prefix='')[source]

Return the pages fetched most recently, newest first, one apiece.

Parameters:
  • limit (int) – The most to return.

  • prefix (str) – Only pages whose number begins with it, a namespace filter.

Return type:

Sequence[Visit]

async popular(limit, *, prefix='', since=None)[source]

Return the pages fetched most often, the most read first.

Parameters:
  • limit (int) – The most to return.

  • prefix (str) – Only pages whose number begins with it, a namespace filter.

  • since (datetime | None) – Count only fetches since then, or over the whole log.

Return type:

Sequence[Visit]

async callers(*, since=None)[source]

Return how many distinct callers have been seen.

Parameters:

since (datetime | None) – Count only callers seen since then, or over the whole log.

Return type:

int