All checks were successful
CI / lint-and-test (push) Successful in 17s
- Created a new `CHANGELOG.md` file to document all notable changes to the project, adhering to the Keep a Changelog format. - Updated `CONTRIBUTING.md` to include instructions for building and previewing documentation using MkDocs. - Added `mkdocs.yml` configuration for documentation generation, including navigation structure and theme settings. - Enhanced various documentation files, including API reference, architecture overview, configuration reference, and runbook, to provide comprehensive guidance for users and developers. - Included new sections in the README for changelog and documentation links, improving accessibility to project information.
16650 lines
693 KiB
HTML
16650 lines
693 KiB
HTML
|
||
<!doctype html>
|
||
<html lang="en" class="no-js">
|
||
<head>
|
||
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
|
||
<meta name="description" content="Telegram bot for team duty shift calendar and group reminder">
|
||
|
||
|
||
|
||
<link rel="canonical" href="https://github.com/your-org/duty-teller/api-reference/">
|
||
|
||
|
||
<link rel="prev" href="../runbook/">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="icon" href="../assets/images/favicon.png">
|
||
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.2">
|
||
|
||
|
||
|
||
<title>API Reference - Duty Teller</title>
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="../assets/stylesheets/main.484c7ddc.min.css">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
|
||
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="../assets/_mkdocstrings.css">
|
||
|
||
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||
|
||
|
||
|
||
|
||
|
||
</head>
|
||
|
||
|
||
<body dir="ltr">
|
||
|
||
|
||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||
<label class="md-overlay" for="__drawer"></label>
|
||
<div data-md-component="skip">
|
||
|
||
|
||
<a href="#api-reference" class="md-skip">
|
||
Skip to content
|
||
</a>
|
||
|
||
</div>
|
||
<div data-md-component="announce">
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<header class="md-header md-header--shadow" data-md-component="header">
|
||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||
<a href=".." title="Duty Teller" class="md-header__button md-logo" aria-label="Duty Teller" data-md-component="logo">
|
||
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||
|
||
</a>
|
||
<label class="md-header__button md-icon" for="__drawer">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||
</label>
|
||
<div class="md-header__title" data-md-component="header-title">
|
||
<div class="md-header__ellipsis">
|
||
<div class="md-header__topic">
|
||
<span class="md-ellipsis">
|
||
Duty Teller
|
||
</span>
|
||
</div>
|
||
<div class="md-header__topic" data-md-component="header-topic">
|
||
<span class="md-ellipsis">
|
||
|
||
API Reference
|
||
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-header__button md-icon" for="__search">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||
</label>
|
||
<div class="md-search" data-md-component="search" role="dialog">
|
||
<label class="md-search__overlay" for="__search"></label>
|
||
<div class="md-search__inner" role="search">
|
||
<form class="md-search__form" name="search">
|
||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||
<label class="md-search__icon md-icon" for="__search">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
|
||
</label>
|
||
<nav class="md-search__options" aria-label="Search">
|
||
|
||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
||
</button>
|
||
</nav>
|
||
|
||
</form>
|
||
<div class="md-search__output">
|
||
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
|
||
<div class="md-search-result" data-md-component="search-result">
|
||
<div class="md-search-result__meta">
|
||
Initializing search
|
||
</div>
|
||
<ol class="md-search-result__list" role="presentation"></ol>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
</nav>
|
||
|
||
</header>
|
||
|
||
<div class="md-container" data-md-component="container">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<main class="md-main" data-md-component="main">
|
||
<div class="md-main__inner md-grid">
|
||
|
||
|
||
|
||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||
<div class="md-sidebar__scrollwrap">
|
||
<div class="md-sidebar__inner">
|
||
|
||
|
||
|
||
|
||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||
<label class="md-nav__title" for="__drawer">
|
||
<a href=".." title="Duty Teller" class="md-nav__button md-logo" aria-label="Duty Teller" data-md-component="logo">
|
||
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||
|
||
</a>
|
||
Duty Teller
|
||
</label>
|
||
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href=".." class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Home
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../configuration/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Configuration
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../architecture/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Architecture
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../import-format/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Import format
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../runbook/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
Runbook
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--active">
|
||
|
||
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
API Reference
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
<a href="./" class="md-nav__link md-nav__link--active">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
|
||
|
||
API Reference
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
|
||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__title" for="__toc">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
Table of contents
|
||
</label>
|
||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#configuration" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Configuration
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
config
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="config">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.Settings" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Settings
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Settings">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.Settings.from_env" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
from_env
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.can_access_miniapp" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
can_access_miniapp
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.can_access_miniapp_by_phone" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
can_access_miniapp_by_phone
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.is_admin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
is_admin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.is_admin_by_phone" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
is_admin_by_phone
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.normalize_phone" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
normalize_phone
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.require_bot_token" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
require_bot_token
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#api-fastapi-and-auth" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
API (FastAPI and auth)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
api
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.app" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
app
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="app">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.app.get_personal_calendar_ical" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_personal_calendar_ical
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.dependencies" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
dependencies
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="dependencies">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.dependencies.fetch_duties_response" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
fetch_duties_response
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.dependencies.get_authenticated_username" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_authenticated_username
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.dependencies.get_db_session" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_db_session
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.dependencies.get_validated_dates" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_validated_dates
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.dependencies.require_miniapp_username" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
require_miniapp_username
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.telegram_auth" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
telegram_auth
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="telegram_auth">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.telegram_auth.validate_init_data" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
validate_init_data
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.telegram_auth.validate_init_data_with_reason" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
validate_init_data_with_reason
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.calendar_ics" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
calendar_ics
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="calendar_ics">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.calendar_ics.get_calendar_events" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_calendar_events
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.personal_calendar_ics" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
personal_calendar_ics
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="personal_calendar_ics">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.personal_calendar_ics.build_personal_ics" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
build_personal_ics
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#database" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Database
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
db
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="db">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.Base" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Base
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.Duty" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Duty
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.DutyCreate" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyCreate
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.DutyInDb" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyInDb
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.DutyWithUser" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyWithUser
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.User" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
User
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.UserCreate" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
UserCreate
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.UserInDb" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
UserInDb
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.delete_duties_in_range" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
delete_duties_in_range
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.get_duties" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_duties
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.get_engine" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_engine
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.get_or_create_user" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_or_create_user
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.get_or_create_user_by_full_name" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_or_create_user_by_full_name
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.get_session" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_session
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.get_session_factory" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_session_factory
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.init_db" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
init_db
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.insert_duty" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
insert_duty
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.session_scope" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
session_scope
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.set_user_phone" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
set_user_phone
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.update_user_display_name" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
update_user_display_name
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.models" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
models
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="models">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.models.Base" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Base
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.models.CalendarSubscriptionToken" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
CalendarSubscriptionToken
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.models.Duty" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Duty
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.models.GroupDutyPin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GroupDutyPin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.models.User" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
User
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
schemas
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="schemas">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.CalendarEvent" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
CalendarEvent
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.DutyBase" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyBase
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.DutyCreate" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyCreate
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.DutyInDb" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyInDb
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.DutyWithUser" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyWithUser
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.UserBase" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
UserBase
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.UserCreate" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
UserCreate
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.UserInDb" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
UserInDb
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.session" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
session
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="session">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.session.get_engine" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_engine
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.session.get_session" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_session
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.session.get_session_factory" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_session_factory
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.session.session_scope" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
session_scope
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
repository
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="repository">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.create_calendar_token" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
create_calendar_token
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.delete_duties_in_range" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
delete_duties_in_range
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.delete_group_duty_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
delete_group_duty_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_all_group_duty_pin_chat_ids" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_all_group_duty_pin_chat_ids
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_current_duty" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_current_duty
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_duties" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_duties
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_duties_for_user" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_duties_for_user
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_group_duty_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_group_duty_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_next_shift_end" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_next_shift_end
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_or_create_user" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_or_create_user
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_or_create_user_by_full_name" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_or_create_user_by_full_name
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_user_by_calendar_token" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_user_by_calendar_token
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_user_by_telegram_id" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_user_by_telegram_id
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.insert_duty" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
insert_duty
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.save_group_duty_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
save_group_duty_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.set_user_phone" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
set_user_phone
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.update_user_display_name" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
update_user_display_name
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#services" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Services
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
services
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="services">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.delete_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
delete_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.format_duty_message" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
format_duty_message
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.get_all_pin_chat_ids" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_all_pin_chat_ids
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.get_duty_message_text" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_duty_message_text
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.get_message_id" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_message_id
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.get_next_shift_end_utc" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_next_shift_end_utc
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.run_import" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
run_import
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.save_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
save_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.import_service" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
import_service
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="import_service">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.import_service.run_import" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
run_import
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
group_duty_pin_service
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="group_duty_pin_service">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.delete_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
delete_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.format_duty_message" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
format_duty_message
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.get_all_pin_chat_ids" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_all_pin_chat_ids
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.get_duty_message_text" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_duty_message_text
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.get_message_id" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_message_id
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.get_next_shift_end_utc" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_next_shift_end_utc
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.save_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
save_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#handlers" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Handlers
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
handlers
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="handlers">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.register_handlers" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
register_handlers
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.commands" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
commands
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="commands">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.commands.calendar_link" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
calendar_link
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.commands.help_cmd" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
help_cmd
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.commands.set_phone" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
set_phone
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.commands.start" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
start
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.import_duty_schedule" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
import_duty_schedule
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="import_duty_schedule">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.import_duty_schedule.handle_duty_schedule_document" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
handle_duty_schedule_document
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.import_duty_schedule.handle_handover_time_text" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
handle_handover_time_text
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.import_duty_schedule.import_duty_schedule_cmd" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
import_duty_schedule_cmd
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.group_duty_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
group_duty_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="group_duty_pin">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.group_duty_pin.my_chat_member_handler" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
my_chat_member_handler
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.group_duty_pin.pin_duty_cmd" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
pin_duty_cmd
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.group_duty_pin.restore_group_pin_jobs" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
restore_group_pin_jobs
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.group_duty_pin.update_group_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
update_group_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.errors" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
errors
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="errors">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.errors.error_handler" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
error_handler
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#importers" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Importers
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.importers" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
importers
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.importers.duty_schedule" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
duty_schedule
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="duty_schedule">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.importers.duty_schedule.DutyScheduleEntry" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyScheduleEntry
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.importers.duty_schedule.DutyScheduleParseError" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyScheduleParseError
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.importers.duty_schedule.DutyScheduleResult" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyScheduleResult
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.importers.duty_schedule.parse_duty_schedule" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
parse_duty_schedule
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||
<div class="md-sidebar__scrollwrap">
|
||
<div class="md-sidebar__inner">
|
||
|
||
|
||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__title" for="__toc">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
Table of contents
|
||
</label>
|
||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#configuration" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Configuration
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
config
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="config">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.Settings" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Settings
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="Settings">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.Settings.from_env" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
from_env
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.can_access_miniapp" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
can_access_miniapp
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.can_access_miniapp_by_phone" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
can_access_miniapp_by_phone
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.is_admin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
is_admin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.is_admin_by_phone" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
is_admin_by_phone
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.normalize_phone" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
normalize_phone
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.config.require_bot_token" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
require_bot_token
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#api-fastapi-and-auth" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
API (FastAPI and auth)
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
api
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.app" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
app
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="app">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.app.get_personal_calendar_ical" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_personal_calendar_ical
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.dependencies" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
dependencies
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="dependencies">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.dependencies.fetch_duties_response" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
fetch_duties_response
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.dependencies.get_authenticated_username" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_authenticated_username
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.dependencies.get_db_session" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_db_session
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.dependencies.get_validated_dates" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_validated_dates
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.dependencies.require_miniapp_username" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
require_miniapp_username
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.telegram_auth" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
telegram_auth
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="telegram_auth">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.telegram_auth.validate_init_data" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
validate_init_data
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.telegram_auth.validate_init_data_with_reason" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
validate_init_data_with_reason
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.calendar_ics" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
calendar_ics
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="calendar_ics">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.calendar_ics.get_calendar_events" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_calendar_events
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.personal_calendar_ics" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
personal_calendar_ics
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="personal_calendar_ics">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.api.personal_calendar_ics.build_personal_ics" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
build_personal_ics
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#database" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Database
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
db
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="db">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.Base" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Base
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.Duty" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Duty
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.DutyCreate" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyCreate
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.DutyInDb" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyInDb
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.DutyWithUser" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyWithUser
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.User" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
User
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.UserCreate" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
UserCreate
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.UserInDb" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
UserInDb
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.delete_duties_in_range" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
delete_duties_in_range
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.get_duties" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_duties
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.get_engine" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_engine
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.get_or_create_user" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_or_create_user
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.get_or_create_user_by_full_name" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_or_create_user_by_full_name
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.get_session" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_session
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.get_session_factory" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_session_factory
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.init_db" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
init_db
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.insert_duty" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
insert_duty
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.session_scope" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
session_scope
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.set_user_phone" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
set_user_phone
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.update_user_display_name" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
update_user_display_name
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.models" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
models
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="models">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.models.Base" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Base
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.models.CalendarSubscriptionToken" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
CalendarSubscriptionToken
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.models.Duty" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Duty
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.models.GroupDutyPin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
GroupDutyPin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.models.User" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
User
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
schemas
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="schemas">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.CalendarEvent" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
CalendarEvent
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.DutyBase" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyBase
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.DutyCreate" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyCreate
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.DutyInDb" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyInDb
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.DutyWithUser" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyWithUser
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.UserBase" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
UserBase
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.UserCreate" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
UserCreate
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.schemas.UserInDb" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
UserInDb
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.session" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
session
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="session">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.session.get_engine" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_engine
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.session.get_session" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_session
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.session.get_session_factory" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_session_factory
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.session.session_scope" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
session_scope
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
repository
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="repository">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.create_calendar_token" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
create_calendar_token
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.delete_duties_in_range" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
delete_duties_in_range
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.delete_group_duty_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
delete_group_duty_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_all_group_duty_pin_chat_ids" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_all_group_duty_pin_chat_ids
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_current_duty" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_current_duty
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_duties" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_duties
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_duties_for_user" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_duties_for_user
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_group_duty_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_group_duty_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_next_shift_end" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_next_shift_end
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_or_create_user" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_or_create_user
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_or_create_user_by_full_name" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_or_create_user_by_full_name
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_user_by_calendar_token" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_user_by_calendar_token
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.get_user_by_telegram_id" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_user_by_telegram_id
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.insert_duty" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
insert_duty
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.save_group_duty_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
save_group_duty_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.set_user_phone" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
set_user_phone
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.db.repository.update_user_display_name" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
update_user_display_name
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#services" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Services
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
services
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="services">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.delete_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
delete_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.format_duty_message" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
format_duty_message
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.get_all_pin_chat_ids" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_all_pin_chat_ids
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.get_duty_message_text" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_duty_message_text
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.get_message_id" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_message_id
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.get_next_shift_end_utc" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_next_shift_end_utc
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.run_import" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
run_import
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.save_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
save_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.import_service" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
import_service
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="import_service">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.import_service.run_import" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
run_import
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
group_duty_pin_service
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="group_duty_pin_service">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.delete_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
delete_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.format_duty_message" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
format_duty_message
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.get_all_pin_chat_ids" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_all_pin_chat_ids
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.get_duty_message_text" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_duty_message_text
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.get_message_id" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_message_id
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.get_next_shift_end_utc" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
get_next_shift_end_utc
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.services.group_duty_pin_service.save_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
save_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#handlers" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Handlers
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
handlers
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="handlers">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.register_handlers" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
register_handlers
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.commands" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
commands
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="commands">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.commands.calendar_link" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
calendar_link
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.commands.help_cmd" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
help_cmd
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.commands.set_phone" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
set_phone
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.commands.start" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
start
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.import_duty_schedule" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
import_duty_schedule
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="import_duty_schedule">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.import_duty_schedule.handle_duty_schedule_document" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
handle_duty_schedule_document
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.import_duty_schedule.handle_handover_time_text" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
handle_handover_time_text
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.import_duty_schedule.import_duty_schedule_cmd" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
import_duty_schedule_cmd
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.group_duty_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
group_duty_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="group_duty_pin">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.group_duty_pin.my_chat_member_handler" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
my_chat_member_handler
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.group_duty_pin.pin_duty_cmd" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
pin_duty_cmd
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.group_duty_pin.restore_group_pin_jobs" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
restore_group_pin_jobs
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.group_duty_pin.update_group_pin" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
update_group_pin
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.errors" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
errors
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="errors">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.handlers.errors.error_handler" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
error_handler
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#importers" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
Importers
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.importers" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
importers
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.importers.duty_schedule" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
duty_schedule
|
||
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="duty_schedule">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.importers.duty_schedule.DutyScheduleEntry" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyScheduleEntry
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.importers.duty_schedule.DutyScheduleParseError" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyScheduleParseError
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.importers.duty_schedule.DutyScheduleResult" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
DutyScheduleResult
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#duty_teller.importers.duty_schedule.parse_duty_schedule" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
|
||
parse_duty_schedule
|
||
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="md-content" data-md-component="content">
|
||
|
||
<article class="md-content__inner md-typeset">
|
||
|
||
|
||
|
||
|
||
|
||
<h1 id="api-reference">API Reference</h1>
|
||
<p>Generated from the <code>duty_teller</code> package. The following subpackages and modules are included.</p>
|
||
<h2 id="configuration">Configuration</h2>
|
||
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.config" class="doc doc-heading">
|
||
<code>duty_teller.config</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Load configuration from environment (e.g. .env via python-dotenv).</p>
|
||
<p>BOT_TOKEN is not validated on import; call require_bot_token() in the entry point
|
||
when running the bot.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.config.Settings" class="doc doc-heading">
|
||
<code>Settings</code>
|
||
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-dataclass"><code>dataclass</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
|
||
|
||
<p>Injectable settings built from environment. Used in tests or when env is overridden.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/config.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 55</span>
|
||
<span class="normal"> 56</span>
|
||
<span class="normal"> 57</span>
|
||
<span class="normal"> 58</span>
|
||
<span class="normal"> 59</span>
|
||
<span class="normal"> 60</span>
|
||
<span class="normal"> 61</span>
|
||
<span class="normal"> 62</span>
|
||
<span class="normal"> 63</span>
|
||
<span class="normal"> 64</span>
|
||
<span class="normal"> 65</span>
|
||
<span class="normal"> 66</span>
|
||
<span class="normal"> 67</span>
|
||
<span class="normal"> 68</span>
|
||
<span class="normal"> 69</span>
|
||
<span class="normal"> 70</span>
|
||
<span class="normal"> 71</span>
|
||
<span class="normal"> 72</span>
|
||
<span class="normal"> 73</span>
|
||
<span class="normal"> 74</span>
|
||
<span class="normal"> 75</span>
|
||
<span class="normal"> 76</span>
|
||
<span class="normal"> 77</span>
|
||
<span class="normal"> 78</span>
|
||
<span class="normal"> 79</span>
|
||
<span class="normal"> 80</span>
|
||
<span class="normal"> 81</span>
|
||
<span class="normal"> 82</span>
|
||
<span class="normal"> 83</span>
|
||
<span class="normal"> 84</span>
|
||
<span class="normal"> 85</span>
|
||
<span class="normal"> 86</span>
|
||
<span class="normal"> 87</span>
|
||
<span class="normal"> 88</span>
|
||
<span class="normal"> 89</span>
|
||
<span class="normal"> 90</span>
|
||
<span class="normal"> 91</span>
|
||
<span class="normal"> 92</span>
|
||
<span class="normal"> 93</span>
|
||
<span class="normal"> 94</span>
|
||
<span class="normal"> 95</span>
|
||
<span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span>
|
||
<span class="normal">101</span>
|
||
<span class="normal">102</span>
|
||
<span class="normal">103</span>
|
||
<span class="normal">104</span>
|
||
<span class="normal">105</span>
|
||
<span class="normal">106</span>
|
||
<span class="normal">107</span>
|
||
<span class="normal">108</span>
|
||
<span class="normal">109</span>
|
||
<span class="normal">110</span>
|
||
<span class="normal">111</span>
|
||
<span class="normal">112</span>
|
||
<span class="normal">113</span>
|
||
<span class="normal">114</span>
|
||
<span class="normal">115</span>
|
||
<span class="normal">116</span>
|
||
<span class="normal">117</span>
|
||
<span class="normal">118</span>
|
||
<span class="normal">119</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@dataclass</span><span class="p">(</span><span class="n">frozen</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">Settings</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Injectable settings built from environment. Used in tests or when env is overridden."""</span>
|
||
|
||
<span class="n">bot_token</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">database_url</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">mini_app_base_url</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">http_port</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">allowed_usernames</span><span class="p">:</span> <span class="nb">set</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
|
||
<span class="n">admin_usernames</span><span class="p">:</span> <span class="nb">set</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
|
||
<span class="n">allowed_phones</span><span class="p">:</span> <span class="nb">set</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
|
||
<span class="n">admin_phones</span><span class="p">:</span> <span class="nb">set</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
|
||
<span class="n">mini_app_skip_auth</span><span class="p">:</span> <span class="nb">bool</span>
|
||
<span class="n">init_data_max_age_seconds</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">cors_origins</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
|
||
<span class="n">external_calendar_ics_url</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">duty_display_tz</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">default_language</span><span class="p">:</span> <span class="nb">str</span>
|
||
|
||
<span class="nd">@classmethod</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">from_env</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span> <span class="o">-></span> <span class="s2">"Settings"</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Build Settings from current environment (same logic as module-level variables).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Settings instance with all fields populated from env.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">bot_token</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"BOT_TOKEN"</span><span class="p">)</span> <span class="ow">or</span> <span class="s2">""</span>
|
||
<span class="n">raw_allowed</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"ALLOWED_USERNAMES"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="n">allowed</span> <span class="o">=</span> <span class="p">{</span>
|
||
<span class="n">s</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">lstrip</span><span class="p">(</span><span class="s2">"@"</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">raw_allowed</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">","</span><span class="p">)</span> <span class="k">if</span> <span class="n">s</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="p">}</span>
|
||
<span class="n">raw_admin</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"ADMIN_USERNAMES"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="n">admin</span> <span class="o">=</span> <span class="p">{</span>
|
||
<span class="n">s</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">lstrip</span><span class="p">(</span><span class="s2">"@"</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">raw_admin</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">","</span><span class="p">)</span> <span class="k">if</span> <span class="n">s</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="p">}</span>
|
||
<span class="n">allowed_phones</span> <span class="o">=</span> <span class="n">_parse_phone_list</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"ALLOWED_PHONES"</span><span class="p">,</span> <span class="s2">""</span><span class="p">))</span>
|
||
<span class="n">admin_phones</span> <span class="o">=</span> <span class="n">_parse_phone_list</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"ADMIN_PHONES"</span><span class="p">,</span> <span class="s2">""</span><span class="p">))</span>
|
||
<span class="n">raw_cors</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"CORS_ORIGINS"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="n">cors</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="p">[</span><span class="n">_o</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">_o</span> <span class="ow">in</span> <span class="n">raw_cors</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">","</span><span class="p">)</span> <span class="k">if</span> <span class="n">_o</span><span class="o">.</span><span class="n">strip</span><span class="p">()]</span>
|
||
<span class="k">if</span> <span class="n">raw_cors</span> <span class="ow">and</span> <span class="n">raw_cors</span> <span class="o">!=</span> <span class="s2">"*"</span>
|
||
<span class="k">else</span> <span class="p">[</span><span class="s2">"*"</span><span class="p">]</span>
|
||
<span class="p">)</span>
|
||
<span class="k">return</span> <span class="bp">cls</span><span class="p">(</span>
|
||
<span class="n">bot_token</span><span class="o">=</span><span class="n">bot_token</span><span class="p">,</span>
|
||
<span class="n">database_url</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"DATABASE_URL"</span><span class="p">,</span> <span class="s2">"sqlite:///data/duty_teller.db"</span><span class="p">),</span>
|
||
<span class="n">mini_app_base_url</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"MINI_APP_BASE_URL"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">rstrip</span><span class="p">(</span><span class="s2">"/"</span><span class="p">),</span>
|
||
<span class="n">http_port</span><span class="o">=</span><span class="nb">int</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"HTTP_PORT"</span><span class="p">,</span> <span class="s2">"8080"</span><span class="p">)),</span>
|
||
<span class="n">allowed_usernames</span><span class="o">=</span><span class="n">allowed</span><span class="p">,</span>
|
||
<span class="n">admin_usernames</span><span class="o">=</span><span class="n">admin</span><span class="p">,</span>
|
||
<span class="n">allowed_phones</span><span class="o">=</span><span class="n">allowed_phones</span><span class="p">,</span>
|
||
<span class="n">admin_phones</span><span class="o">=</span><span class="n">admin_phones</span><span class="p">,</span>
|
||
<span class="n">mini_app_skip_auth</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"MINI_APP_SKIP_AUTH"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="ow">in</span> <span class="p">(</span><span class="s2">"1"</span><span class="p">,</span> <span class="s2">"true"</span><span class="p">,</span> <span class="s2">"yes"</span><span class="p">),</span>
|
||
<span class="n">init_data_max_age_seconds</span><span class="o">=</span><span class="nb">int</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"INIT_DATA_MAX_AGE_SECONDS"</span><span class="p">,</span> <span class="s2">"0"</span><span class="p">)),</span>
|
||
<span class="n">cors_origins</span><span class="o">=</span><span class="n">cors</span><span class="p">,</span>
|
||
<span class="n">external_calendar_ics_url</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span>
|
||
<span class="s2">"EXTERNAL_CALENDAR_ICS_URL"</span><span class="p">,</span> <span class="s2">""</span>
|
||
<span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">(),</span>
|
||
<span class="n">duty_display_tz</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"DUTY_DISPLAY_TZ"</span><span class="p">,</span> <span class="s2">"Europe/Moscow"</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="ow">or</span> <span class="s2">"Europe/Moscow"</span><span class="p">,</span>
|
||
<span class="n">default_language</span><span class="o">=</span><span class="n">_normalize_default_language</span><span class="p">(</span>
|
||
<span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"DEFAULT_LANGUAGE"</span><span class="p">,</span> <span class="s2">"en"</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="p">),</span>
|
||
<span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h4 id="duty_teller.config.Settings.from_env" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">from_env</span><span class="p">()</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-classmethod"><code>classmethod</code></small>
|
||
</span>
|
||
|
||
</h4>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Build Settings from current environment (same logic as module-level variables).</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="Settings
|
||
|
||
|
||
|
||
dataclass
|
||
(duty_teller.config.Settings)" href="#duty_teller.config.Settings">Settings</a></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Settings instance with all fields populated from env.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/config.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 74</span>
|
||
<span class="normal"> 75</span>
|
||
<span class="normal"> 76</span>
|
||
<span class="normal"> 77</span>
|
||
<span class="normal"> 78</span>
|
||
<span class="normal"> 79</span>
|
||
<span class="normal"> 80</span>
|
||
<span class="normal"> 81</span>
|
||
<span class="normal"> 82</span>
|
||
<span class="normal"> 83</span>
|
||
<span class="normal"> 84</span>
|
||
<span class="normal"> 85</span>
|
||
<span class="normal"> 86</span>
|
||
<span class="normal"> 87</span>
|
||
<span class="normal"> 88</span>
|
||
<span class="normal"> 89</span>
|
||
<span class="normal"> 90</span>
|
||
<span class="normal"> 91</span>
|
||
<span class="normal"> 92</span>
|
||
<span class="normal"> 93</span>
|
||
<span class="normal"> 94</span>
|
||
<span class="normal"> 95</span>
|
||
<span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span>
|
||
<span class="normal">101</span>
|
||
<span class="normal">102</span>
|
||
<span class="normal">103</span>
|
||
<span class="normal">104</span>
|
||
<span class="normal">105</span>
|
||
<span class="normal">106</span>
|
||
<span class="normal">107</span>
|
||
<span class="normal">108</span>
|
||
<span class="normal">109</span>
|
||
<span class="normal">110</span>
|
||
<span class="normal">111</span>
|
||
<span class="normal">112</span>
|
||
<span class="normal">113</span>
|
||
<span class="normal">114</span>
|
||
<span class="normal">115</span>
|
||
<span class="normal">116</span>
|
||
<span class="normal">117</span>
|
||
<span class="normal">118</span>
|
||
<span class="normal">119</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@classmethod</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">from_env</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span> <span class="o">-></span> <span class="s2">"Settings"</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Build Settings from current environment (same logic as module-level variables).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Settings instance with all fields populated from env.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">bot_token</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"BOT_TOKEN"</span><span class="p">)</span> <span class="ow">or</span> <span class="s2">""</span>
|
||
<span class="n">raw_allowed</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"ALLOWED_USERNAMES"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="n">allowed</span> <span class="o">=</span> <span class="p">{</span>
|
||
<span class="n">s</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">lstrip</span><span class="p">(</span><span class="s2">"@"</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">raw_allowed</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">","</span><span class="p">)</span> <span class="k">if</span> <span class="n">s</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="p">}</span>
|
||
<span class="n">raw_admin</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"ADMIN_USERNAMES"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="n">admin</span> <span class="o">=</span> <span class="p">{</span>
|
||
<span class="n">s</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">lstrip</span><span class="p">(</span><span class="s2">"@"</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">raw_admin</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">","</span><span class="p">)</span> <span class="k">if</span> <span class="n">s</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="p">}</span>
|
||
<span class="n">allowed_phones</span> <span class="o">=</span> <span class="n">_parse_phone_list</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"ALLOWED_PHONES"</span><span class="p">,</span> <span class="s2">""</span><span class="p">))</span>
|
||
<span class="n">admin_phones</span> <span class="o">=</span> <span class="n">_parse_phone_list</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"ADMIN_PHONES"</span><span class="p">,</span> <span class="s2">""</span><span class="p">))</span>
|
||
<span class="n">raw_cors</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"CORS_ORIGINS"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="n">cors</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="p">[</span><span class="n">_o</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">_o</span> <span class="ow">in</span> <span class="n">raw_cors</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">","</span><span class="p">)</span> <span class="k">if</span> <span class="n">_o</span><span class="o">.</span><span class="n">strip</span><span class="p">()]</span>
|
||
<span class="k">if</span> <span class="n">raw_cors</span> <span class="ow">and</span> <span class="n">raw_cors</span> <span class="o">!=</span> <span class="s2">"*"</span>
|
||
<span class="k">else</span> <span class="p">[</span><span class="s2">"*"</span><span class="p">]</span>
|
||
<span class="p">)</span>
|
||
<span class="k">return</span> <span class="bp">cls</span><span class="p">(</span>
|
||
<span class="n">bot_token</span><span class="o">=</span><span class="n">bot_token</span><span class="p">,</span>
|
||
<span class="n">database_url</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"DATABASE_URL"</span><span class="p">,</span> <span class="s2">"sqlite:///data/duty_teller.db"</span><span class="p">),</span>
|
||
<span class="n">mini_app_base_url</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"MINI_APP_BASE_URL"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">rstrip</span><span class="p">(</span><span class="s2">"/"</span><span class="p">),</span>
|
||
<span class="n">http_port</span><span class="o">=</span><span class="nb">int</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"HTTP_PORT"</span><span class="p">,</span> <span class="s2">"8080"</span><span class="p">)),</span>
|
||
<span class="n">allowed_usernames</span><span class="o">=</span><span class="n">allowed</span><span class="p">,</span>
|
||
<span class="n">admin_usernames</span><span class="o">=</span><span class="n">admin</span><span class="p">,</span>
|
||
<span class="n">allowed_phones</span><span class="o">=</span><span class="n">allowed_phones</span><span class="p">,</span>
|
||
<span class="n">admin_phones</span><span class="o">=</span><span class="n">admin_phones</span><span class="p">,</span>
|
||
<span class="n">mini_app_skip_auth</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"MINI_APP_SKIP_AUTH"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="ow">in</span> <span class="p">(</span><span class="s2">"1"</span><span class="p">,</span> <span class="s2">"true"</span><span class="p">,</span> <span class="s2">"yes"</span><span class="p">),</span>
|
||
<span class="n">init_data_max_age_seconds</span><span class="o">=</span><span class="nb">int</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"INIT_DATA_MAX_AGE_SECONDS"</span><span class="p">,</span> <span class="s2">"0"</span><span class="p">)),</span>
|
||
<span class="n">cors_origins</span><span class="o">=</span><span class="n">cors</span><span class="p">,</span>
|
||
<span class="n">external_calendar_ics_url</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span>
|
||
<span class="s2">"EXTERNAL_CALENDAR_ICS_URL"</span><span class="p">,</span> <span class="s2">""</span>
|
||
<span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">(),</span>
|
||
<span class="n">duty_display_tz</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"DUTY_DISPLAY_TZ"</span><span class="p">,</span> <span class="s2">"Europe/Moscow"</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="ow">or</span> <span class="s2">"Europe/Moscow"</span><span class="p">,</span>
|
||
<span class="n">default_language</span><span class="o">=</span><span class="n">_normalize_default_language</span><span class="p">(</span>
|
||
<span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">"DEFAULT_LANGUAGE"</span><span class="p">,</span> <span class="s2">"en"</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="p">),</span>
|
||
<span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.config.can_access_miniapp" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">can_access_miniapp</span><span class="p">(</span><span class="n">username</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Check if username is allowed to open the calendar Miniapp.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>username</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram username (with or without @; case-insensitive).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="bool">bool</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>True if in ALLOWED_USERNAMES or ADMIN_USERNAMES.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/config.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">172</span>
|
||
<span class="normal">173</span>
|
||
<span class="normal">174</span>
|
||
<span class="normal">175</span>
|
||
<span class="normal">176</span>
|
||
<span class="normal">177</span>
|
||
<span class="normal">178</span>
|
||
<span class="normal">179</span>
|
||
<span class="normal">180</span>
|
||
<span class="normal">181</span>
|
||
<span class="normal">182</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">can_access_miniapp</span><span class="p">(</span><span class="n">username</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Check if username is allowed to open the calendar Miniapp.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> username: Telegram username (with or without @; case-insensitive).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> True if in ALLOWED_USERNAMES or ADMIN_USERNAMES.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">u</span> <span class="o">=</span> <span class="p">(</span><span class="n">username</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
|
||
<span class="k">return</span> <span class="n">u</span> <span class="ow">in</span> <span class="n">ALLOWED_USERNAMES</span> <span class="ow">or</span> <span class="n">u</span> <span class="ow">in</span> <span class="n">ADMIN_USERNAMES</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.config.can_access_miniapp_by_phone" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">can_access_miniapp_by_phone</span><span class="p">(</span><span class="n">phone</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Check if phone (set via /set_phone) is allowed to open the Miniapp.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>phone</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Raw phone string or None.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="bool">bool</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>True if normalized phone is in ALLOWED_PHONES or ADMIN_PHONES.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/config.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">185</span>
|
||
<span class="normal">186</span>
|
||
<span class="normal">187</span>
|
||
<span class="normal">188</span>
|
||
<span class="normal">189</span>
|
||
<span class="normal">190</span>
|
||
<span class="normal">191</span>
|
||
<span class="normal">192</span>
|
||
<span class="normal">193</span>
|
||
<span class="normal">194</span>
|
||
<span class="normal">195</span>
|
||
<span class="normal">196</span>
|
||
<span class="normal">197</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">can_access_miniapp_by_phone</span><span class="p">(</span><span class="n">phone</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Check if phone (set via /set_phone) is allowed to open the Miniapp.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> phone: Raw phone string or None.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> True if normalized phone is in ALLOWED_PHONES or ADMIN_PHONES.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">normalized</span> <span class="o">=</span> <span class="n">normalize_phone</span><span class="p">(</span><span class="n">phone</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">normalized</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="kc">False</span>
|
||
<span class="k">return</span> <span class="n">normalized</span> <span class="ow">in</span> <span class="n">ALLOWED_PHONES</span> <span class="ow">or</span> <span class="n">normalized</span> <span class="ow">in</span> <span class="n">ADMIN_PHONES</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.config.is_admin" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">is_admin</span><span class="p">(</span><span class="n">username</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Check if Telegram username is in ADMIN_USERNAMES.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>username</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram username (with or without @; case-insensitive).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="bool">bool</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>True if in ADMIN_USERNAMES.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/config.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">160</span>
|
||
<span class="normal">161</span>
|
||
<span class="normal">162</span>
|
||
<span class="normal">163</span>
|
||
<span class="normal">164</span>
|
||
<span class="normal">165</span>
|
||
<span class="normal">166</span>
|
||
<span class="normal">167</span>
|
||
<span class="normal">168</span>
|
||
<span class="normal">169</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">is_admin</span><span class="p">(</span><span class="n">username</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Check if Telegram username is in ADMIN_USERNAMES.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> username: Telegram username (with or without @; case-insensitive).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> True if in ADMIN_USERNAMES.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="n">username</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="ow">in</span> <span class="n">ADMIN_USERNAMES</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.config.is_admin_by_phone" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">is_admin_by_phone</span><span class="p">(</span><span class="n">phone</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Check if phone is in ADMIN_PHONES.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>phone</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Raw phone string or None.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="bool">bool</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>True if normalized phone is in ADMIN_PHONES.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/config.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">200</span>
|
||
<span class="normal">201</span>
|
||
<span class="normal">202</span>
|
||
<span class="normal">203</span>
|
||
<span class="normal">204</span>
|
||
<span class="normal">205</span>
|
||
<span class="normal">206</span>
|
||
<span class="normal">207</span>
|
||
<span class="normal">208</span>
|
||
<span class="normal">209</span>
|
||
<span class="normal">210</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">is_admin_by_phone</span><span class="p">(</span><span class="n">phone</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Check if phone is in ADMIN_PHONES.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> phone: Raw phone string or None.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> True if normalized phone is in ADMIN_PHONES.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">normalized</span> <span class="o">=</span> <span class="n">normalize_phone</span><span class="p">(</span><span class="n">phone</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="nb">bool</span><span class="p">(</span><span class="n">normalized</span> <span class="ow">and</span> <span class="n">normalized</span> <span class="ow">in</span> <span class="n">ADMIN_PHONES</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.config.normalize_phone" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">normalize_phone</span><span class="p">(</span><span class="n">phone</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return phone as digits only (spaces, +, parentheses, dashes removed).</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>phone</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Raw phone string or None.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Digits-only string, or empty string if None or empty.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/config.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span>
|
||
<span class="normal">31</span>
|
||
<span class="normal">32</span>
|
||
<span class="normal">33</span>
|
||
<span class="normal">34</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">normalize_phone</span><span class="p">(</span><span class="n">phone</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Return phone as digits only (spaces, +, parentheses, dashes removed).</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> phone: Raw phone string or None.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Digits-only string, or empty string if None or empty.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">phone</span> <span class="ow">or</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">phone</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="s2">""</span>
|
||
<span class="k">return</span> <span class="n">_PHONE_DIGITS_RE</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="s2">""</span><span class="p">,</span> <span class="n">phone</span><span class="o">.</span><span class="n">strip</span><span class="p">())</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.config.require_bot_token" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">require_bot_token</span><span class="p">()</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Raise SystemExit with a clear message if BOT_TOKEN is not set.</p>
|
||
<p>Call from the application entry point (e.g. main.py or duty_teller.run) so the
|
||
process exits with a helpful message instead of failing later.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Raises:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="SystemExit">SystemExit</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>If BOT_TOKEN is empty.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/config.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">213</span>
|
||
<span class="normal">214</span>
|
||
<span class="normal">215</span>
|
||
<span class="normal">216</span>
|
||
<span class="normal">217</span>
|
||
<span class="normal">218</span>
|
||
<span class="normal">219</span>
|
||
<span class="normal">220</span>
|
||
<span class="normal">221</span>
|
||
<span class="normal">222</span>
|
||
<span class="normal">223</span>
|
||
<span class="normal">224</span>
|
||
<span class="normal">225</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">require_bot_token</span><span class="p">()</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Raise SystemExit with a clear message if BOT_TOKEN is not set.</span>
|
||
|
||
<span class="sd"> Call from the application entry point (e.g. main.py or duty_teller.run) so the</span>
|
||
<span class="sd"> process exits with a helpful message instead of failing later.</span>
|
||
|
||
<span class="sd"> Raises:</span>
|
||
<span class="sd"> SystemExit: If BOT_TOKEN is empty.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">BOT_TOKEN</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">SystemExit</span><span class="p">(</span>
|
||
<span class="s2">"BOT_TOKEN is not set. Copy .env.example to .env and set your token from @BotFather."</span>
|
||
<span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div><h2 id="api-fastapi-and-auth">API (FastAPI and auth)</h2>
|
||
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.api" class="doc doc-heading">
|
||
<code>duty_teller.api</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>HTTP API for the calendar Mini App: duties, calendar events, and static webapp.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.api.app" class="doc doc-heading">
|
||
<code>duty_teller.api.app</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>FastAPI app: /api/duties, /api/calendar-events, personal ICS, and static webapp at /app.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.api.app.get_personal_calendar_ical" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_personal_calendar_ical</span><span class="p">(</span><span class="n">token</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="n">Depends</span><span class="p">(</span><span class="n">get_db_session</span><span class="p">))</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return ICS calendar with only the subscribing user's duties.
|
||
No Telegram auth; access is by secret token in the URL.</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/api/app.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 74</span>
|
||
<span class="normal"> 75</span>
|
||
<span class="normal"> 76</span>
|
||
<span class="normal"> 77</span>
|
||
<span class="normal"> 78</span>
|
||
<span class="normal"> 79</span>
|
||
<span class="normal"> 80</span>
|
||
<span class="normal"> 81</span>
|
||
<span class="normal"> 82</span>
|
||
<span class="normal"> 83</span>
|
||
<span class="normal"> 84</span>
|
||
<span class="normal"> 85</span>
|
||
<span class="normal"> 86</span>
|
||
<span class="normal"> 87</span>
|
||
<span class="normal"> 88</span>
|
||
<span class="normal"> 89</span>
|
||
<span class="normal"> 90</span>
|
||
<span class="normal"> 91</span>
|
||
<span class="normal"> 92</span>
|
||
<span class="normal"> 93</span>
|
||
<span class="normal"> 94</span>
|
||
<span class="normal"> 95</span>
|
||
<span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span>
|
||
<span class="s2">"/api/calendar/ical/</span><span class="si">{token}</span><span class="s2">.ics"</span><span class="p">,</span>
|
||
<span class="n">summary</span><span class="o">=</span><span class="s2">"Personal calendar ICS"</span><span class="p">,</span>
|
||
<span class="n">description</span><span class="o">=</span><span class="s2">"Returns an ICS calendar with only the subscribing user's duties. No Telegram auth; access is by secret token in the URL."</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">get_personal_calendar_ical</span><span class="p">(</span>
|
||
<span class="n">token</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_db_session</span><span class="p">),</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">Response</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""</span>
|
||
<span class="sd"> Return ICS calendar with only the subscribing user's duties.</span>
|
||
<span class="sd"> No Telegram auth; access is by secret token in the URL.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">get_user_by_calendar_token</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">token</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">user</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s2">"Not found"</span><span class="p">)</span>
|
||
<span class="n">today</span> <span class="o">=</span> <span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">()</span>
|
||
<span class="n">from_date</span> <span class="o">=</span> <span class="p">(</span><span class="n">today</span> <span class="o">-</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">365</span><span class="p">))</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-</span><span class="si">%d</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="n">to_date</span> <span class="o">=</span> <span class="p">(</span><span class="n">today</span> <span class="o">+</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">365</span> <span class="o">*</span> <span class="mi">2</span><span class="p">))</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-</span><span class="si">%d</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="n">duties_with_name</span> <span class="o">=</span> <span class="n">get_duties_for_user</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">,</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="n">from_date</span><span class="o">=</span><span class="n">from_date</span><span class="p">,</span> <span class="n">to_date</span><span class="o">=</span><span class="n">to_date</span>
|
||
<span class="p">)</span>
|
||
<span class="n">ics_bytes</span> <span class="o">=</span> <span class="n">build_personal_ics</span><span class="p">(</span><span class="n">duties_with_name</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">Response</span><span class="p">(</span>
|
||
<span class="n">content</span><span class="o">=</span><span class="n">ics_bytes</span><span class="p">,</span>
|
||
<span class="n">media_type</span><span class="o">=</span><span class="s2">"text/calendar; charset=utf-8"</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.api.dependencies" class="doc doc-heading">
|
||
<code>duty_teller.api.dependencies</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>FastAPI dependencies: DB session, Miniapp auth (initData/allowlist), date validation.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.api.dependencies.fetch_duties_response" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">fetch_duties_response</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">from_date</span><span class="p">,</span> <span class="n">to_date</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Load duties in range and return as DutyWithUser list for API response.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>from_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Start date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>to_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>End date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="list">list</span>[<a class="autorefs autorefs-internal" title="DutyWithUser (duty_teller.db.schemas.DutyWithUser)" href="#duty_teller.db.schemas.DutyWithUser">DutyWithUser</a>]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>List of DutyWithUser (id, user_id, start_at, end_at, full_name, event_type).</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/api/dependencies.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">180</span>
|
||
<span class="normal">181</span>
|
||
<span class="normal">182</span>
|
||
<span class="normal">183</span>
|
||
<span class="normal">184</span>
|
||
<span class="normal">185</span>
|
||
<span class="normal">186</span>
|
||
<span class="normal">187</span>
|
||
<span class="normal">188</span>
|
||
<span class="normal">189</span>
|
||
<span class="normal">190</span>
|
||
<span class="normal">191</span>
|
||
<span class="normal">192</span>
|
||
<span class="normal">193</span>
|
||
<span class="normal">194</span>
|
||
<span class="normal">195</span>
|
||
<span class="normal">196</span>
|
||
<span class="normal">197</span>
|
||
<span class="normal">198</span>
|
||
<span class="normal">199</span>
|
||
<span class="normal">200</span>
|
||
<span class="normal">201</span>
|
||
<span class="normal">202</span>
|
||
<span class="normal">203</span>
|
||
<span class="normal">204</span>
|
||
<span class="normal">205</span>
|
||
<span class="normal">206</span>
|
||
<span class="normal">207</span>
|
||
<span class="normal">208</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">fetch_duties_response</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">from_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">to_date</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">DutyWithUser</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Load duties in range and return as DutyWithUser list for API response.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> from_date: Start date YYYY-MM-DD.</span>
|
||
<span class="sd"> to_date: End date YYYY-MM-DD.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> List of DutyWithUser (id, user_id, start_at, end_at, full_name, event_type).</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">rows</span> <span class="o">=</span> <span class="n">get_duties</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">from_date</span><span class="o">=</span><span class="n">from_date</span><span class="p">,</span> <span class="n">to_date</span><span class="o">=</span><span class="n">to_date</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="p">[</span>
|
||
<span class="n">DutyWithUser</span><span class="p">(</span>
|
||
<span class="nb">id</span><span class="o">=</span><span class="n">duty</span><span class="o">.</span><span class="n">id</span><span class="p">,</span>
|
||
<span class="n">user_id</span><span class="o">=</span><span class="n">duty</span><span class="o">.</span><span class="n">user_id</span><span class="p">,</span>
|
||
<span class="n">start_at</span><span class="o">=</span><span class="n">duty</span><span class="o">.</span><span class="n">start_at</span><span class="p">,</span>
|
||
<span class="n">end_at</span><span class="o">=</span><span class="n">duty</span><span class="o">.</span><span class="n">end_at</span><span class="p">,</span>
|
||
<span class="n">full_name</span><span class="o">=</span><span class="n">full_name</span><span class="p">,</span>
|
||
<span class="n">event_type</span><span class="o">=</span><span class="p">(</span>
|
||
<span class="n">duty</span><span class="o">.</span><span class="n">event_type</span>
|
||
<span class="k">if</span> <span class="n">duty</span><span class="o">.</span><span class="n">event_type</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">"duty"</span><span class="p">,</span> <span class="s2">"unavailable"</span><span class="p">,</span> <span class="s2">"vacation"</span><span class="p">)</span>
|
||
<span class="k">else</span> <span class="s2">"duty"</span>
|
||
<span class="p">),</span>
|
||
<span class="p">)</span>
|
||
<span class="k">for</span> <span class="n">duty</span><span class="p">,</span> <span class="n">full_name</span> <span class="ow">in</span> <span class="n">rows</span>
|
||
<span class="p">]</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.api.dependencies.get_authenticated_username" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_authenticated_username</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">x_telegram_init_data</span><span class="p">,</span> <span class="n">session</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return identifier for miniapp auth (username or full_name or id:...); empty if skip-auth.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>request</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="fastapi.Request">Request</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>FastAPI request (client host for private-IP bypass).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>x_telegram_init_data</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Raw X-Telegram-Init-Data header value.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session (for phone allowlist lookup).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Username, full_name, or "id:<telegram_id>"; empty string if MINI_APP_SKIP_AUTH</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>or private IP and no initData.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Raises:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="fastapi.HTTPException">HTTPException</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>403 if initData missing/invalid or user not in allowlist.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/api/dependencies.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">123</span>
|
||
<span class="normal">124</span>
|
||
<span class="normal">125</span>
|
||
<span class="normal">126</span>
|
||
<span class="normal">127</span>
|
||
<span class="normal">128</span>
|
||
<span class="normal">129</span>
|
||
<span class="normal">130</span>
|
||
<span class="normal">131</span>
|
||
<span class="normal">132</span>
|
||
<span class="normal">133</span>
|
||
<span class="normal">134</span>
|
||
<span class="normal">135</span>
|
||
<span class="normal">136</span>
|
||
<span class="normal">137</span>
|
||
<span class="normal">138</span>
|
||
<span class="normal">139</span>
|
||
<span class="normal">140</span>
|
||
<span class="normal">141</span>
|
||
<span class="normal">142</span>
|
||
<span class="normal">143</span>
|
||
<span class="normal">144</span>
|
||
<span class="normal">145</span>
|
||
<span class="normal">146</span>
|
||
<span class="normal">147</span>
|
||
<span class="normal">148</span>
|
||
<span class="normal">149</span>
|
||
<span class="normal">150</span>
|
||
<span class="normal">151</span>
|
||
<span class="normal">152</span>
|
||
<span class="normal">153</span>
|
||
<span class="normal">154</span>
|
||
<span class="normal">155</span>
|
||
<span class="normal">156</span>
|
||
<span class="normal">157</span>
|
||
<span class="normal">158</span>
|
||
<span class="normal">159</span>
|
||
<span class="normal">160</span>
|
||
<span class="normal">161</span>
|
||
<span class="normal">162</span>
|
||
<span class="normal">163</span>
|
||
<span class="normal">164</span>
|
||
<span class="normal">165</span>
|
||
<span class="normal">166</span>
|
||
<span class="normal">167</span>
|
||
<span class="normal">168</span>
|
||
<span class="normal">169</span>
|
||
<span class="normal">170</span>
|
||
<span class="normal">171</span>
|
||
<span class="normal">172</span>
|
||
<span class="normal">173</span>
|
||
<span class="normal">174</span>
|
||
<span class="normal">175</span>
|
||
<span class="normal">176</span>
|
||
<span class="normal">177</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_authenticated_username</span><span class="p">(</span>
|
||
<span class="n">request</span><span class="p">:</span> <span class="n">Request</span><span class="p">,</span>
|
||
<span class="n">x_telegram_init_data</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Return identifier for miniapp auth (username or full_name or id:...); empty if skip-auth.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> request: FastAPI request (client host for private-IP bypass).</span>
|
||
<span class="sd"> x_telegram_init_data: Raw X-Telegram-Init-Data header value.</span>
|
||
<span class="sd"> session: DB session (for phone allowlist lookup).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Username, full_name, or "id:<telegram_id>"; empty string if MINI_APP_SKIP_AUTH</span>
|
||
<span class="sd"> or private IP and no initData.</span>
|
||
|
||
<span class="sd"> Raises:</span>
|
||
<span class="sd"> HTTPException: 403 if initData missing/invalid or user not in allowlist.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">if</span> <span class="n">config</span><span class="o">.</span><span class="n">MINI_APP_SKIP_AUTH</span><span class="p">:</span>
|
||
<span class="n">log</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s2">"allowing without any auth check (MINI_APP_SKIP_AUTH is set)"</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="s2">""</span>
|
||
<span class="n">init_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">x_telegram_init_data</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">init_data</span><span class="p">:</span>
|
||
<span class="n">client_host</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">client</span><span class="o">.</span><span class="n">host</span> <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">client</span> <span class="k">else</span> <span class="kc">None</span>
|
||
<span class="k">if</span> <span class="n">_is_private_client</span><span class="p">(</span><span class="n">client_host</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="s2">""</span>
|
||
<span class="n">log</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s2">"no X-Telegram-Init-Data header (client=</span><span class="si">%s</span><span class="s2">)"</span><span class="p">,</span> <span class="n">client_host</span><span class="p">)</span>
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">_lang_from_accept_language</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"Accept-Language"</span><span class="p">))</span>
|
||
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">403</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"api.open_from_telegram"</span><span class="p">))</span>
|
||
<span class="n">max_age</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">INIT_DATA_MAX_AGE_SECONDS</span> <span class="ow">or</span> <span class="kc">None</span>
|
||
<span class="n">telegram_user_id</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">auth_reason</span><span class="p">,</span> <span class="n">lang</span> <span class="o">=</span> <span class="n">validate_init_data_with_reason</span><span class="p">(</span>
|
||
<span class="n">init_data</span><span class="p">,</span> <span class="n">config</span><span class="o">.</span><span class="n">BOT_TOKEN</span><span class="p">,</span> <span class="n">max_age_seconds</span><span class="o">=</span><span class="n">max_age</span>
|
||
<span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">auth_reason</span> <span class="o">!=</span> <span class="s2">"ok"</span><span class="p">:</span>
|
||
<span class="n">log</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s2">"initData validation failed: </span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span> <span class="n">auth_reason</span><span class="p">)</span>
|
||
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span>
|
||
<span class="n">status_code</span><span class="o">=</span><span class="mi">403</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">_auth_error_detail</span><span class="p">(</span><span class="n">auth_reason</span><span class="p">,</span> <span class="n">lang</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">username</span> <span class="ow">and</span> <span class="n">config</span><span class="o">.</span><span class="n">can_access_miniapp</span><span class="p">(</span><span class="n">username</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">username</span>
|
||
<span class="n">failed_phone</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="k">if</span> <span class="n">telegram_user_id</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">get_user_by_telegram_id</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">user</span> <span class="ow">and</span> <span class="n">user</span><span class="o">.</span><span class="n">phone</span> <span class="ow">and</span> <span class="n">config</span><span class="o">.</span><span class="n">can_access_miniapp_by_phone</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">phone</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">username</span> <span class="ow">or</span> <span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">full_name</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">)</span> <span class="ow">or</span> <span class="sa">f</span><span class="s2">"id:</span><span class="si">{</span><span class="n">telegram_user_id</span><span class="si">}</span><span class="s2">"</span>
|
||
<span class="k">if</span> <span class="n">user</span> <span class="ow">and</span> <span class="n">user</span><span class="o">.</span><span class="n">phone</span><span class="p">:</span>
|
||
<span class="n">failed_phone</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">normalize_phone</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">phone</span><span class="p">)</span>
|
||
<span class="n">log</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span>
|
||
<span class="s2">"username/phone not in allowlist (username=</span><span class="si">%s</span><span class="s2">, telegram_id=</span><span class="si">%s</span><span class="s2">, phone=</span><span class="si">%s</span><span class="s2">)"</span><span class="p">,</span>
|
||
<span class="n">username</span><span class="p">,</span>
|
||
<span class="n">telegram_user_id</span><span class="p">,</span>
|
||
<span class="n">failed_phone</span> <span class="k">if</span> <span class="n">failed_phone</span> <span class="k">else</span> <span class="s2">"—"</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">403</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"api.access_denied"</span><span class="p">))</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.api.dependencies.get_db_session" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_db_session</span><span class="p">()</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Yield a DB session for the request; closed automatically by FastAPI.</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/api/dependencies.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">86</span>
|
||
<span class="normal">87</span>
|
||
<span class="normal">88</span>
|
||
<span class="normal">89</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_db_session</span><span class="p">()</span> <span class="o">-></span> <span class="n">Generator</span><span class="p">[</span><span class="n">Session</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Yield a DB session for the request; closed automatically by FastAPI."""</span>
|
||
<span class="k">with</span> <span class="n">session_scope</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">DATABASE_URL</span><span class="p">)</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||
<span class="k">yield</span> <span class="n">session</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.api.dependencies.get_validated_dates" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_validated_dates</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">from_date</span><span class="o">=</span><span class="n">Query</span><span class="p">(</span><span class="o">...</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s1">'ISO date YYYY-MM-DD'</span><span class="p">,</span> <span class="n">alias</span><span class="o">=</span><span class="s1">'from'</span><span class="p">),</span> <span class="n">to_date</span><span class="o">=</span><span class="n">Query</span><span class="p">(</span><span class="o">...</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s1">'ISO date YYYY-MM-DD'</span><span class="p">,</span> <span class="n">alias</span><span class="o">=</span><span class="s1">'to'</span><span class="p">))</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Validate from/to date query params; use Accept-Language for error messages.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>request</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="fastapi.Request">Request</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>FastAPI request (for Accept-Language).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>from_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Start date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code><span title="fastapi.Query">Query</span>(..., description='ISO date YYYY-MM-DD', alias='from')</code>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>to_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>End date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code><span title="fastapi.Query">Query</span>(..., description='ISO date YYYY-MM-DD', alias='to')</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="tuple">tuple</span>[<span title="str">str</span>, <span title="str">str</span>]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>(from_date, to_date) as strings.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Raises:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="fastapi.HTTPException">HTTPException</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>400 if format invalid or from_date > to_date.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/api/dependencies.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">63</span>
|
||
<span class="normal">64</span>
|
||
<span class="normal">65</span>
|
||
<span class="normal">66</span>
|
||
<span class="normal">67</span>
|
||
<span class="normal">68</span>
|
||
<span class="normal">69</span>
|
||
<span class="normal">70</span>
|
||
<span class="normal">71</span>
|
||
<span class="normal">72</span>
|
||
<span class="normal">73</span>
|
||
<span class="normal">74</span>
|
||
<span class="normal">75</span>
|
||
<span class="normal">76</span>
|
||
<span class="normal">77</span>
|
||
<span class="normal">78</span>
|
||
<span class="normal">79</span>
|
||
<span class="normal">80</span>
|
||
<span class="normal">81</span>
|
||
<span class="normal">82</span>
|
||
<span class="normal">83</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_validated_dates</span><span class="p">(</span>
|
||
<span class="n">request</span><span class="p">:</span> <span class="n">Request</span><span class="p">,</span>
|
||
<span class="n">from_date</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="o">...</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s2">"ISO date YYYY-MM-DD"</span><span class="p">,</span> <span class="n">alias</span><span class="o">=</span><span class="s2">"from"</span><span class="p">),</span>
|
||
<span class="n">to_date</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="o">...</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s2">"ISO date YYYY-MM-DD"</span><span class="p">,</span> <span class="n">alias</span><span class="o">=</span><span class="s2">"to"</span><span class="p">),</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Validate from/to date query params; use Accept-Language for error messages.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> request: FastAPI request (for Accept-Language).</span>
|
||
<span class="sd"> from_date: Start date YYYY-MM-DD.</span>
|
||
<span class="sd"> to_date: End date YYYY-MM-DD.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> (from_date, to_date) as strings.</span>
|
||
|
||
<span class="sd"> Raises:</span>
|
||
<span class="sd"> HTTPException: 400 if format invalid or from_date > to_date.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">_lang_from_accept_language</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"Accept-Language"</span><span class="p">))</span>
|
||
<span class="n">_validate_duty_dates</span><span class="p">(</span><span class="n">from_date</span><span class="p">,</span> <span class="n">to_date</span><span class="p">,</span> <span class="n">lang</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="n">from_date</span><span class="p">,</span> <span class="n">to_date</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.api.dependencies.require_miniapp_username" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">require_miniapp_username</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">x_telegram_init_data</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="n">Depends</span><span class="p">(</span><span class="n">get_db_session</span><span class="p">))</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>FastAPI dependency: require valid Miniapp auth; return username/identifier.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Raises:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="fastapi.HTTPException">HTTPException</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>403 if initData missing/invalid or user not in allowlist.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/api/dependencies.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 92</span>
|
||
<span class="normal"> 93</span>
|
||
<span class="normal"> 94</span>
|
||
<span class="normal"> 95</span>
|
||
<span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span>
|
||
<span class="normal">101</span>
|
||
<span class="normal">102</span>
|
||
<span class="normal">103</span>
|
||
<span class="normal">104</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">require_miniapp_username</span><span class="p">(</span>
|
||
<span class="n">request</span><span class="p">:</span> <span class="n">Request</span><span class="p">,</span>
|
||
<span class="n">x_telegram_init_data</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span>
|
||
<span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">,</span> <span class="n">Header</span><span class="p">(</span><span class="n">alias</span><span class="o">=</span><span class="s2">"X-Telegram-Init-Data"</span><span class="p">)</span>
|
||
<span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_db_session</span><span class="p">),</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""FastAPI dependency: require valid Miniapp auth; return username/identifier.</span>
|
||
|
||
<span class="sd"> Raises:</span>
|
||
<span class="sd"> HTTPException: 403 if initData missing/invalid or user not in allowlist.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">return</span> <span class="n">get_authenticated_username</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">x_telegram_init_data</span><span class="p">,</span> <span class="n">session</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.api.telegram_auth" class="doc doc-heading">
|
||
<code>duty_teller.api.telegram_auth</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Validate Telegram Web App initData and extract user username.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.api.telegram_auth.validate_init_data" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">validate_init_data</span><span class="p">(</span><span class="n">init_data</span><span class="p">,</span> <span class="n">bot_token</span><span class="p">,</span> <span class="n">max_age_seconds</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Validate Telegram Web App initData and return username if valid.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>init_data</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Raw initData string from tgWebAppData.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>bot_token</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Bot token (must match the bot that signed the data).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>max_age_seconds</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Reject if auth_date older than this; None to disable.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>None</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Username (lowercase, no @) or None if validation fails.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/api/telegram_auth.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">13</span>
|
||
<span class="normal">14</span>
|
||
<span class="normal">15</span>
|
||
<span class="normal">16</span>
|
||
<span class="normal">17</span>
|
||
<span class="normal">18</span>
|
||
<span class="normal">19</span>
|
||
<span class="normal">20</span>
|
||
<span class="normal">21</span>
|
||
<span class="normal">22</span>
|
||
<span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span>
|
||
<span class="normal">31</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">validate_init_data</span><span class="p">(</span>
|
||
<span class="n">init_data</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">bot_token</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">max_age_seconds</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Validate Telegram Web App initData and return username if valid.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> init_data: Raw initData string from tgWebAppData.</span>
|
||
<span class="sd"> bot_token: Bot token (must match the bot that signed the data).</span>
|
||
<span class="sd"> max_age_seconds: Reject if auth_date older than this; None to disable.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Username (lowercase, no @) or None if validation fails.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">_</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">validate_init_data_with_reason</span><span class="p">(</span>
|
||
<span class="n">init_data</span><span class="p">,</span> <span class="n">bot_token</span><span class="p">,</span> <span class="n">max_age_seconds</span>
|
||
<span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">username</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.api.telegram_auth.validate_init_data_with_reason" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">validate_init_data_with_reason</span><span class="p">(</span><span class="n">init_data</span><span class="p">,</span> <span class="n">bot_token</span><span class="p">,</span> <span class="n">max_age_seconds</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Validate initData signature and return user id, username, reason, and lang.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>init_data</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Raw initData string from tgWebAppData.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>bot_token</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Bot token (must match the bot that signed the data).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>max_age_seconds</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Reject if auth_date older than this; None to disable.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>None</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="int">int</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Tuple (telegram_user_id, username, reason, lang). reason is one of: "ok",</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>"empty", "no_hash", "hash_mismatch", "auth_date_expired", "no_user",</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>"user_invalid", "no_user_id". lang is from user.language_code normalized</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>to 'ru' or 'en'; 'en' when no user. On success: (user.id, username or None,</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="tuple">tuple</span>[<span title="int">int</span> | None, <span title="str">str</span> | None, <span title="str">str</span>, <span title="str">str</span>]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>"ok", lang).</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/api/telegram_auth.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 42</span>
|
||
<span class="normal"> 43</span>
|
||
<span class="normal"> 44</span>
|
||
<span class="normal"> 45</span>
|
||
<span class="normal"> 46</span>
|
||
<span class="normal"> 47</span>
|
||
<span class="normal"> 48</span>
|
||
<span class="normal"> 49</span>
|
||
<span class="normal"> 50</span>
|
||
<span class="normal"> 51</span>
|
||
<span class="normal"> 52</span>
|
||
<span class="normal"> 53</span>
|
||
<span class="normal"> 54</span>
|
||
<span class="normal"> 55</span>
|
||
<span class="normal"> 56</span>
|
||
<span class="normal"> 57</span>
|
||
<span class="normal"> 58</span>
|
||
<span class="normal"> 59</span>
|
||
<span class="normal"> 60</span>
|
||
<span class="normal"> 61</span>
|
||
<span class="normal"> 62</span>
|
||
<span class="normal"> 63</span>
|
||
<span class="normal"> 64</span>
|
||
<span class="normal"> 65</span>
|
||
<span class="normal"> 66</span>
|
||
<span class="normal"> 67</span>
|
||
<span class="normal"> 68</span>
|
||
<span class="normal"> 69</span>
|
||
<span class="normal"> 70</span>
|
||
<span class="normal"> 71</span>
|
||
<span class="normal"> 72</span>
|
||
<span class="normal"> 73</span>
|
||
<span class="normal"> 74</span>
|
||
<span class="normal"> 75</span>
|
||
<span class="normal"> 76</span>
|
||
<span class="normal"> 77</span>
|
||
<span class="normal"> 78</span>
|
||
<span class="normal"> 79</span>
|
||
<span class="normal"> 80</span>
|
||
<span class="normal"> 81</span>
|
||
<span class="normal"> 82</span>
|
||
<span class="normal"> 83</span>
|
||
<span class="normal"> 84</span>
|
||
<span class="normal"> 85</span>
|
||
<span class="normal"> 86</span>
|
||
<span class="normal"> 87</span>
|
||
<span class="normal"> 88</span>
|
||
<span class="normal"> 89</span>
|
||
<span class="normal"> 90</span>
|
||
<span class="normal"> 91</span>
|
||
<span class="normal"> 92</span>
|
||
<span class="normal"> 93</span>
|
||
<span class="normal"> 94</span>
|
||
<span class="normal"> 95</span>
|
||
<span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span>
|
||
<span class="normal">101</span>
|
||
<span class="normal">102</span>
|
||
<span class="normal">103</span>
|
||
<span class="normal">104</span>
|
||
<span class="normal">105</span>
|
||
<span class="normal">106</span>
|
||
<span class="normal">107</span>
|
||
<span class="normal">108</span>
|
||
<span class="normal">109</span>
|
||
<span class="normal">110</span>
|
||
<span class="normal">111</span>
|
||
<span class="normal">112</span>
|
||
<span class="normal">113</span>
|
||
<span class="normal">114</span>
|
||
<span class="normal">115</span>
|
||
<span class="normal">116</span>
|
||
<span class="normal">117</span>
|
||
<span class="normal">118</span>
|
||
<span class="normal">119</span>
|
||
<span class="normal">120</span>
|
||
<span class="normal">121</span>
|
||
<span class="normal">122</span>
|
||
<span class="normal">123</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">validate_init_data_with_reason</span><span class="p">(</span>
|
||
<span class="n">init_data</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">bot_token</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">max_age_seconds</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">,</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">,</span> <span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Validate initData signature and return user id, username, reason, and lang.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> init_data: Raw initData string from tgWebAppData.</span>
|
||
<span class="sd"> bot_token: Bot token (must match the bot that signed the data).</span>
|
||
<span class="sd"> max_age_seconds: Reject if auth_date older than this; None to disable.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Tuple (telegram_user_id, username, reason, lang). reason is one of: "ok",</span>
|
||
<span class="sd"> "empty", "no_hash", "hash_mismatch", "auth_date_expired", "no_user",</span>
|
||
<span class="sd"> "user_invalid", "no_user_id". lang is from user.language_code normalized</span>
|
||
<span class="sd"> to 'ru' or 'en'; 'en' when no user. On success: (user.id, username or None,</span>
|
||
<span class="sd"> "ok", lang).</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">init_data</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">bot_token</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"empty"</span><span class="p">,</span> <span class="s2">"en"</span><span class="p">)</span>
|
||
<span class="n">init_data</span> <span class="o">=</span> <span class="n">init_data</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="n">params</span> <span class="o">=</span> <span class="p">{}</span>
|
||
<span class="k">for</span> <span class="n">part</span> <span class="ow">in</span> <span class="n">init_data</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"&"</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="s2">"="</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">part</span><span class="p">:</span>
|
||
<span class="k">continue</span>
|
||
<span class="n">key</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">value</span> <span class="o">=</span> <span class="n">part</span><span class="o">.</span><span class="n">partition</span><span class="p">(</span><span class="s2">"="</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">key</span><span class="p">:</span>
|
||
<span class="k">continue</span>
|
||
<span class="n">params</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
|
||
<span class="n">hash_val</span> <span class="o">=</span> <span class="n">params</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">"hash"</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">hash_val</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"no_hash"</span><span class="p">,</span> <span class="s2">"en"</span><span class="p">)</span>
|
||
<span class="n">data_pairs</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">params</span><span class="o">.</span><span class="n">items</span><span class="p">())</span>
|
||
<span class="c1"># Data-check string: key=value with URL-decoded values (per Telegram example)</span>
|
||
<span class="n">data_string</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">=</span><span class="si">{</span><span class="n">unquote</span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">data_pairs</span><span class="p">)</span>
|
||
<span class="c1"># HMAC-SHA256(key=WebAppData, message=bot_token) per reference implementations</span>
|
||
<span class="n">secret_key</span> <span class="o">=</span> <span class="n">hmac</span><span class="o">.</span><span class="n">new</span><span class="p">(</span>
|
||
<span class="sa">b</span><span class="s2">"WebAppData"</span><span class="p">,</span>
|
||
<span class="n">msg</span><span class="o">=</span><span class="n">bot_token</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span>
|
||
<span class="n">digestmod</span><span class="o">=</span><span class="n">hashlib</span><span class="o">.</span><span class="n">sha256</span><span class="p">,</span>
|
||
<span class="p">)</span><span class="o">.</span><span class="n">digest</span><span class="p">()</span>
|
||
<span class="n">computed</span> <span class="o">=</span> <span class="n">hmac</span><span class="o">.</span><span class="n">new</span><span class="p">(</span>
|
||
<span class="n">secret_key</span><span class="p">,</span>
|
||
<span class="n">msg</span><span class="o">=</span><span class="n">data_string</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span>
|
||
<span class="n">digestmod</span><span class="o">=</span><span class="n">hashlib</span><span class="o">.</span><span class="n">sha256</span><span class="p">,</span>
|
||
<span class="p">)</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">hmac</span><span class="o">.</span><span class="n">compare_digest</span><span class="p">(</span><span class="n">computed</span><span class="o">.</span><span class="n">lower</span><span class="p">(),</span> <span class="n">hash_val</span><span class="o">.</span><span class="n">lower</span><span class="p">()):</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"hash_mismatch"</span><span class="p">,</span> <span class="s2">"en"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">max_age_seconds</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">max_age_seconds</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
|
||
<span class="n">auth_date_raw</span> <span class="o">=</span> <span class="n">params</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"auth_date"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">auth_date_raw</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"auth_date_expired"</span><span class="p">,</span> <span class="s2">"en"</span><span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">auth_date</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="n">auth_date_raw</span><span class="p">))</span>
|
||
<span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"auth_date_expired"</span><span class="p">,</span> <span class="s2">"en"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">auth_date</span> <span class="o">></span> <span class="n">max_age_seconds</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"auth_date_expired"</span><span class="p">,</span> <span class="s2">"en"</span><span class="p">)</span>
|
||
<span class="n">user_raw</span> <span class="o">=</span> <span class="n">params</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"user"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">user_raw</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"no_user"</span><span class="p">,</span> <span class="s2">"en"</span><span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">unquote</span><span class="p">(</span><span class="n">user_raw</span><span class="p">))</span>
|
||
<span class="k">except</span> <span class="p">(</span><span class="n">json</span><span class="o">.</span><span class="n">JSONDecodeError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"user_invalid"</span><span class="p">,</span> <span class="s2">"en"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="nb">dict</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"user_invalid"</span><span class="p">,</span> <span class="s2">"en"</span><span class="p">)</span>
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">_normalize_lang</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"language_code"</span><span class="p">))</span>
|
||
<span class="n">raw_id</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"id"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">raw_id</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"no_user_id"</span><span class="p">,</span> <span class="n">lang</span><span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">telegram_user_id</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">raw_id</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="p">(</span><span class="ne">TypeError</span><span class="p">,</span> <span class="ne">ValueError</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">"no_user_id"</span><span class="p">,</span> <span class="n">lang</span><span class="p">)</span>
|
||
<span class="n">username</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"username"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">username</span> <span class="ow">and</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
|
||
<span class="n">username</span> <span class="o">=</span> <span class="n">username</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">lstrip</span><span class="p">(</span><span class="s2">"@"</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">username</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="n">telegram_user_id</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="s2">"ok"</span><span class="p">,</span> <span class="n">lang</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.api.calendar_ics" class="doc doc-heading">
|
||
<code>duty_teller.api.calendar_ics</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Fetch and parse external ICS calendar; in-memory cache with 7-day TTL.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.api.calendar_ics.get_calendar_events" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_calendar_events</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">from_date</span><span class="p">,</span> <span class="n">to_date</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Fetch ICS from URL and return events in the given date range.</p>
|
||
<p>Uses in-memory cache with TTL 7 days. Recurring events are skipped.
|
||
On fetch or parse error returns an empty list.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>url</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>URL of the ICS calendar.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>from_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Start date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>to_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>End date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="list">list</span>[<span title="dict">dict</span>]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>List of dicts with keys "date" (YYYY-MM-DD) and "summary". Empty on error.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/api/calendar_ics.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">100</span>
|
||
<span class="normal">101</span>
|
||
<span class="normal">102</span>
|
||
<span class="normal">103</span>
|
||
<span class="normal">104</span>
|
||
<span class="normal">105</span>
|
||
<span class="normal">106</span>
|
||
<span class="normal">107</span>
|
||
<span class="normal">108</span>
|
||
<span class="normal">109</span>
|
||
<span class="normal">110</span>
|
||
<span class="normal">111</span>
|
||
<span class="normal">112</span>
|
||
<span class="normal">113</span>
|
||
<span class="normal">114</span>
|
||
<span class="normal">115</span>
|
||
<span class="normal">116</span>
|
||
<span class="normal">117</span>
|
||
<span class="normal">118</span>
|
||
<span class="normal">119</span>
|
||
<span class="normal">120</span>
|
||
<span class="normal">121</span>
|
||
<span class="normal">122</span>
|
||
<span class="normal">123</span>
|
||
<span class="normal">124</span>
|
||
<span class="normal">125</span>
|
||
<span class="normal">126</span>
|
||
<span class="normal">127</span>
|
||
<span class="normal">128</span>
|
||
<span class="normal">129</span>
|
||
<span class="normal">130</span>
|
||
<span class="normal">131</span>
|
||
<span class="normal">132</span>
|
||
<span class="normal">133</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_calendar_events</span><span class="p">(</span>
|
||
<span class="n">url</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">from_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">to_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">dict</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Fetch ICS from URL and return events in the given date range.</span>
|
||
|
||
<span class="sd"> Uses in-memory cache with TTL 7 days. Recurring events are skipped.</span>
|
||
<span class="sd"> On fetch or parse error returns an empty list.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> url: URL of the ICS calendar.</span>
|
||
<span class="sd"> from_date: Start date YYYY-MM-DD.</span>
|
||
<span class="sd"> to_date: End date YYYY-MM-DD.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> List of dicts with keys "date" (YYYY-MM-DD) and "summary". Empty on error.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">url</span> <span class="ow">or</span> <span class="n">from_date</span> <span class="o">></span> <span class="n">to_date</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">[]</span>
|
||
|
||
<span class="n">now</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">timestamp</span><span class="p">()</span>
|
||
<span class="n">raw</span><span class="p">:</span> <span class="nb">bytes</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="k">if</span> <span class="n">url</span> <span class="ow">in</span> <span class="n">_ics_cache</span><span class="p">:</span>
|
||
<span class="n">cached_at</span><span class="p">,</span> <span class="n">cached_raw</span> <span class="o">=</span> <span class="n">_ics_cache</span><span class="p">[</span><span class="n">url</span><span class="p">]</span>
|
||
<span class="k">if</span> <span class="n">now</span> <span class="o">-</span> <span class="n">cached_at</span> <span class="o"><</span> <span class="n">CACHE_TTL_SECONDS</span><span class="p">:</span>
|
||
<span class="n">raw</span> <span class="o">=</span> <span class="n">cached_raw</span>
|
||
<span class="k">if</span> <span class="n">raw</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">raw</span> <span class="o">=</span> <span class="n">_fetch_ics</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">raw</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="p">[]</span>
|
||
<span class="n">_ics_cache</span><span class="p">[</span><span class="n">url</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">raw</span><span class="p">)</span>
|
||
|
||
<span class="k">return</span> <span class="n">_get_events_from_ics</span><span class="p">(</span><span class="n">raw</span><span class="p">,</span> <span class="n">from_date</span><span class="p">,</span> <span class="n">to_date</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.api.personal_calendar_ics" class="doc doc-heading">
|
||
<code>duty_teller.api.personal_calendar_ics</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Generate ICS calendar containing only one user's duties (for subscription link).</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.api.personal_calendar_ics.build_personal_ics" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">build_personal_ics</span><span class="p">(</span><span class="n">duties_with_name</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Build a VCALENDAR (ICS) with one VEVENT per duty.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>duties_with_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="list">list</span>[<span title="tuple">tuple</span>[<a class="autorefs autorefs-internal" title="Duty (duty_teller.db.models.Duty)" href="#duty_teller.db.models.Duty">Duty</a>, <span title="str">str</span>]]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>List of (Duty, full_name). full_name is available for
|
||
DESCRIPTION; SUMMARY is taken from event_type (duty/unavailable/vacation).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="bytes">bytes</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>ICS file content as bytes (UTF-8).</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/api/personal_calendar_ics.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span>
|
||
<span class="normal">31</span>
|
||
<span class="normal">32</span>
|
||
<span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span>
|
||
<span class="normal">38</span>
|
||
<span class="normal">39</span>
|
||
<span class="normal">40</span>
|
||
<span class="normal">41</span>
|
||
<span class="normal">42</span>
|
||
<span class="normal">43</span>
|
||
<span class="normal">44</span>
|
||
<span class="normal">45</span>
|
||
<span class="normal">46</span>
|
||
<span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span>
|
||
<span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">build_personal_ics</span><span class="p">(</span><span class="n">duties_with_name</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">tuple</span><span class="p">[</span><span class="n">Duty</span><span class="p">,</span> <span class="nb">str</span><span class="p">]])</span> <span class="o">-></span> <span class="nb">bytes</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Build a VCALENDAR (ICS) with one VEVENT per duty.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> duties_with_name: List of (Duty, full_name). full_name is available for</span>
|
||
<span class="sd"> DESCRIPTION; SUMMARY is taken from event_type (duty/unavailable/vacation).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> ICS file content as bytes (UTF-8).</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">cal</span> <span class="o">=</span> <span class="n">Calendar</span><span class="p">()</span>
|
||
<span class="n">cal</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"prodid"</span><span class="p">,</span> <span class="s2">"-//Duty Teller//Personal Calendar//EN"</span><span class="p">)</span>
|
||
<span class="n">cal</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"version"</span><span class="p">,</span> <span class="s2">"2.0"</span><span class="p">)</span>
|
||
<span class="n">cal</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"calscale"</span><span class="p">,</span> <span class="s2">"GREGORIAN"</span><span class="p">)</span>
|
||
|
||
<span class="k">for</span> <span class="n">duty</span><span class="p">,</span> <span class="n">_full_name</span> <span class="ow">in</span> <span class="n">duties_with_name</span><span class="p">:</span>
|
||
<span class="n">event</span> <span class="o">=</span> <span class="n">Event</span><span class="p">()</span>
|
||
<span class="n">start_dt</span> <span class="o">=</span> <span class="n">_parse_utc_iso</span><span class="p">(</span><span class="n">duty</span><span class="o">.</span><span class="n">start_at</span><span class="p">)</span>
|
||
<span class="n">end_dt</span> <span class="o">=</span> <span class="n">_parse_utc_iso</span><span class="p">(</span><span class="n">duty</span><span class="o">.</span><span class="n">end_at</span><span class="p">)</span>
|
||
<span class="c1"># Ensure timezone-aware for icalendar</span>
|
||
<span class="k">if</span> <span class="n">start_dt</span><span class="o">.</span><span class="n">tzinfo</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">start_dt</span> <span class="o">=</span> <span class="n">start_dt</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">tzinfo</span><span class="o">=</span><span class="n">timezone</span><span class="o">.</span><span class="n">utc</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">end_dt</span><span class="o">.</span><span class="n">tzinfo</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">end_dt</span> <span class="o">=</span> <span class="n">end_dt</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">tzinfo</span><span class="o">=</span><span class="n">timezone</span><span class="o">.</span><span class="n">utc</span><span class="p">)</span>
|
||
<span class="n">event</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"dtstart"</span><span class="p">,</span> <span class="n">start_dt</span><span class="p">)</span>
|
||
<span class="n">event</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"dtend"</span><span class="p">,</span> <span class="n">end_dt</span><span class="p">)</span>
|
||
<span class="n">summary</span> <span class="o">=</span> <span class="n">SUMMARY_BY_TYPE</span><span class="o">.</span><span class="n">get</span><span class="p">(</span>
|
||
<span class="n">duty</span><span class="o">.</span><span class="n">event_type</span> <span class="k">if</span> <span class="n">duty</span><span class="o">.</span><span class="n">event_type</span> <span class="k">else</span> <span class="s2">"duty"</span><span class="p">,</span> <span class="s2">"Duty"</span>
|
||
<span class="p">)</span>
|
||
<span class="n">event</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"summary"</span><span class="p">,</span> <span class="n">summary</span><span class="p">)</span>
|
||
<span class="n">event</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"uid"</span><span class="p">,</span> <span class="sa">f</span><span class="s2">"duty-</span><span class="si">{</span><span class="n">duty</span><span class="o">.</span><span class="n">id</span><span class="si">}</span><span class="s2">@duty-teller"</span><span class="p">)</span>
|
||
<span class="n">event</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"dtstamp"</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">timezone</span><span class="o">.</span><span class="n">utc</span><span class="p">))</span>
|
||
<span class="n">cal</span><span class="o">.</span><span class="n">add_component</span><span class="p">(</span><span class="n">event</span><span class="p">)</span>
|
||
|
||
<span class="k">return</span> <span class="n">cal</span><span class="o">.</span><span class="n">to_ical</span><span class="p">()</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div><h2 id="database">Database</h2>
|
||
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.db" class="doc doc-heading">
|
||
<code>duty_teller.db</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Database layer: SQLAlchemy models, Pydantic schemas, repository, init.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.Base" class="doc doc-heading">
|
||
<code>Base</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><span title="sqlalchemy.orm.DeclarativeBase">DeclarativeBase</span></code></p>
|
||
|
||
|
||
|
||
<p>Declarative base for all models.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/models.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 7</span>
|
||
<span class="normal"> 8</span>
|
||
<span class="normal"> 9</span>
|
||
<span class="normal">10</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">Base</span><span class="p">(</span><span class="n">DeclarativeBase</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Declarative base for all models."""</span>
|
||
|
||
<span class="k">pass</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.Duty" class="doc doc-heading">
|
||
<code>Duty</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="Base (duty_teller.db.models.Base)" href="#duty_teller.db.models.Base">Base</a></code></p>
|
||
|
||
|
||
|
||
<p>Single duty/unavailable/vacation slot (UTC start_at/end_at, event_type).</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/models.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span>
|
||
<span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">Duty</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Single duty/unavailable/vacation slot (UTC start_at/end_at, event_type)."""</span>
|
||
|
||
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">"duties"</span>
|
||
|
||
<span class="nb">id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">autoincrement</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">user_id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span>
|
||
<span class="n">Integer</span><span class="p">,</span> <span class="n">ForeignKey</span><span class="p">(</span><span class="s2">"users.id"</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span>
|
||
<span class="p">)</span>
|
||
<span class="c1"># UTC, ISO 8601 with Z suffix (e.g. 2025-01-15T09:00:00Z)</span>
|
||
<span class="n">start_at</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="n">end_at</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="c1"># duty | unavailable | vacation</span>
|
||
<span class="n">event_type</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">server_default</span><span class="o">=</span><span class="s2">"duty"</span><span class="p">)</span>
|
||
|
||
<span class="n">user</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="s2">"User"</span><span class="p">]</span> <span class="o">=</span> <span class="n">relationship</span><span class="p">(</span><span class="s2">"User"</span><span class="p">,</span> <span class="n">back_populates</span><span class="o">=</span><span class="s2">"duties"</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.DutyCreate" class="doc doc-heading">
|
||
<code>DutyCreate</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="DutyBase (duty_teller.db.schemas.DutyBase)" href="#duty_teller.db.schemas.DutyBase">DutyBase</a></code></p>
|
||
|
||
|
||
|
||
<p>Duty creation payload.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">40</span>
|
||
<span class="normal">41</span>
|
||
<span class="normal">42</span>
|
||
<span class="normal">43</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">DutyCreate</span><span class="p">(</span><span class="n">DutyBase</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Duty creation payload."""</span>
|
||
|
||
<span class="k">pass</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.DutyInDb" class="doc doc-heading">
|
||
<code>DutyInDb</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="DutyBase (duty_teller.db.schemas.DutyBase)" href="#duty_teller.db.schemas.DutyBase">DutyBase</a></code></p>
|
||
|
||
|
||
|
||
<p>Duty as stored in DB (includes id).</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">46</span>
|
||
<span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">DutyInDb</span><span class="p">(</span><span class="n">DutyBase</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Duty as stored in DB (includes id)."""</span>
|
||
|
||
<span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>
|
||
|
||
<span class="n">model_config</span> <span class="o">=</span> <span class="n">ConfigDict</span><span class="p">(</span><span class="n">from_attributes</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.DutyWithUser" class="doc doc-heading">
|
||
<code>DutyWithUser</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="DutyInDb (duty_teller.db.schemas.DutyInDb)" href="#duty_teller.db.schemas.DutyInDb">DutyInDb</a></code></p>
|
||
|
||
|
||
|
||
<p>Duty with full_name and event_type for calendar display.</p>
|
||
<p>event_type: only these values are returned; unknown DB values are mapped to "duty" in the API.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span>
|
||
<span class="normal">63</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">DutyWithUser</span><span class="p">(</span><span class="n">DutyInDb</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Duty with full_name and event_type for calendar display.</span>
|
||
|
||
<span class="sd"> event_type: only these values are returned; unknown DB values are mapped to "duty" in the API.</span>
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="n">full_name</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">event_type</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"duty"</span><span class="p">,</span> <span class="s2">"unavailable"</span><span class="p">,</span> <span class="s2">"vacation"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"duty"</span>
|
||
|
||
<span class="n">model_config</span> <span class="o">=</span> <span class="n">ConfigDict</span><span class="p">(</span><span class="n">from_attributes</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.User" class="doc doc-heading">
|
||
<code>User</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="Base (duty_teller.db.models.Base)" href="#duty_teller.db.models.Base">Base</a></code></p>
|
||
|
||
|
||
|
||
<p>Telegram user and display name; may have telegram_user_id=None for import-only users.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/models.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">13</span>
|
||
<span class="normal">14</span>
|
||
<span class="normal">15</span>
|
||
<span class="normal">16</span>
|
||
<span class="normal">17</span>
|
||
<span class="normal">18</span>
|
||
<span class="normal">19</span>
|
||
<span class="normal">20</span>
|
||
<span class="normal">21</span>
|
||
<span class="normal">22</span>
|
||
<span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span>
|
||
<span class="normal">31</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Telegram user and display name; may have telegram_user_id=None for import-only users."""</span>
|
||
|
||
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">"users"</span>
|
||
|
||
<span class="nb">id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">autoincrement</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">telegram_user_id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span>
|
||
<span class="n">BigInteger</span><span class="p">,</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">True</span>
|
||
<span class="p">)</span>
|
||
<span class="n">full_name</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="n">username</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">first_name</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">last_name</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">phone</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">name_manually_edited</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span>
|
||
<span class="n">Boolean</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">server_default</span><span class="o">=</span><span class="s2">"0"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">False</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="n">duties</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">list</span><span class="p">[</span><span class="s2">"Duty"</span><span class="p">]]</span> <span class="o">=</span> <span class="n">relationship</span><span class="p">(</span><span class="s2">"Duty"</span><span class="p">,</span> <span class="n">back_populates</span><span class="o">=</span><span class="s2">"user"</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.UserCreate" class="doc doc-heading">
|
||
<code>UserCreate</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="UserBase (duty_teller.db.schemas.UserBase)" href="#duty_teller.db.schemas.UserBase">UserBase</a></code></p>
|
||
|
||
|
||
|
||
<p>User creation payload including Telegram user id.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">17</span>
|
||
<span class="normal">18</span>
|
||
<span class="normal">19</span>
|
||
<span class="normal">20</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">UserCreate</span><span class="p">(</span><span class="n">UserBase</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""User creation payload including Telegram user id."""</span>
|
||
|
||
<span class="n">telegram_user_id</span><span class="p">:</span> <span class="nb">int</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.UserInDb" class="doc doc-heading">
|
||
<code>UserInDb</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="UserBase (duty_teller.db.schemas.UserBase)" href="#duty_teller.db.schemas.UserBase">UserBase</a></code></p>
|
||
|
||
|
||
|
||
<p>User as stored in DB (includes id and telegram_user_id).</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">UserInDb</span><span class="p">(</span><span class="n">UserBase</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""User as stored in DB (includes id and telegram_user_id)."""</span>
|
||
|
||
<span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">telegram_user_id</span><span class="p">:</span> <span class="nb">int</span>
|
||
|
||
<span class="n">model_config</span> <span class="o">=</span> <span class="n">ConfigDict</span><span class="p">(</span><span class="n">from_attributes</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.delete_duties_in_range" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">delete_duties_in_range</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">user_id</span><span class="p">,</span> <span class="n">from_date</span><span class="p">,</span> <span class="n">to_date</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Delete all duties of the user that overlap the given date range.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>from_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Start date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>to_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>End date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Number of duties deleted.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">138</span>
|
||
<span class="normal">139</span>
|
||
<span class="normal">140</span>
|
||
<span class="normal">141</span>
|
||
<span class="normal">142</span>
|
||
<span class="normal">143</span>
|
||
<span class="normal">144</span>
|
||
<span class="normal">145</span>
|
||
<span class="normal">146</span>
|
||
<span class="normal">147</span>
|
||
<span class="normal">148</span>
|
||
<span class="normal">149</span>
|
||
<span class="normal">150</span>
|
||
<span class="normal">151</span>
|
||
<span class="normal">152</span>
|
||
<span class="normal">153</span>
|
||
<span class="normal">154</span>
|
||
<span class="normal">155</span>
|
||
<span class="normal">156</span>
|
||
<span class="normal">157</span>
|
||
<span class="normal">158</span>
|
||
<span class="normal">159</span>
|
||
<span class="normal">160</span>
|
||
<span class="normal">161</span>
|
||
<span class="normal">162</span>
|
||
<span class="normal">163</span>
|
||
<span class="normal">164</span>
|
||
<span class="normal">165</span>
|
||
<span class="normal">166</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">delete_duties_in_range</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="n">from_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">to_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Delete all duties of the user that overlap the given date range.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> user_id: User id.</span>
|
||
<span class="sd"> from_date: Start date YYYY-MM-DD.</span>
|
||
<span class="sd"> to_date: End date YYYY-MM-DD.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Number of duties deleted.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">to_next</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">datetime</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">to_date</span> <span class="o">+</span> <span class="s2">"T00:00:00"</span><span class="p">)</span> <span class="o">+</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
|
||
<span class="p">)</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-</span><span class="si">%d</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="n">q</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Duty</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">user_id</span> <span class="o">==</span> <span class="n">user_id</span><span class="p">,</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">start_at</span> <span class="o"><</span> <span class="n">to_next</span><span class="p">,</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">end_at</span> <span class="o">>=</span> <span class="n">from_date</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">count</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">count</span><span class="p">()</span>
|
||
<span class="n">q</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">synchronize_session</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="k">return</span> <span class="n">count</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.get_duties" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_duties</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">from_date</span><span class="p">,</span> <span class="n">to_date</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return duties overlapping the given date range with user full_name.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>from_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Start date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>to_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>End date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="list">list</span>[<span title="tuple">tuple</span>[<a class="autorefs autorefs-internal" title="Duty (duty_teller.db.models.Duty)" href="#duty_teller.db.models.Duty">Duty</a>, <span title="str">str</span>]]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>List of (Duty, full_name) tuples.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">169</span>
|
||
<span class="normal">170</span>
|
||
<span class="normal">171</span>
|
||
<span class="normal">172</span>
|
||
<span class="normal">173</span>
|
||
<span class="normal">174</span>
|
||
<span class="normal">175</span>
|
||
<span class="normal">176</span>
|
||
<span class="normal">177</span>
|
||
<span class="normal">178</span>
|
||
<span class="normal">179</span>
|
||
<span class="normal">180</span>
|
||
<span class="normal">181</span>
|
||
<span class="normal">182</span>
|
||
<span class="normal">183</span>
|
||
<span class="normal">184</span>
|
||
<span class="normal">185</span>
|
||
<span class="normal">186</span>
|
||
<span class="normal">187</span>
|
||
<span class="normal">188</span>
|
||
<span class="normal">189</span>
|
||
<span class="normal">190</span>
|
||
<span class="normal">191</span>
|
||
<span class="normal">192</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_duties</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">from_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">to_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">tuple</span><span class="p">[</span><span class="n">Duty</span><span class="p">,</span> <span class="nb">str</span><span class="p">]]:</span>
|
||
<span class="w"> </span><span class="sd">"""Return duties overlapping the given date range with user full_name.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> from_date: Start date YYYY-MM-DD.</span>
|
||
<span class="sd"> to_date: End date YYYY-MM-DD.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> List of (Duty, full_name) tuples.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">to_date_next</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">datetime</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">to_date</span> <span class="o">+</span> <span class="s2">"T00:00:00"</span><span class="p">)</span> <span class="o">+</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
|
||
<span class="p">)</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-</span><span class="si">%d</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="n">q</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Duty</span><span class="p">,</span> <span class="n">User</span><span class="o">.</span><span class="n">full_name</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">User</span><span class="p">,</span> <span class="n">Duty</span><span class="o">.</span><span class="n">user_id</span> <span class="o">==</span> <span class="n">User</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">Duty</span><span class="o">.</span><span class="n">start_at</span> <span class="o"><</span> <span class="n">to_date_next</span><span class="p">,</span> <span class="n">Duty</span><span class="o">.</span><span class="n">end_at</span> <span class="o">>=</span> <span class="n">from_date</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="n">q</span><span class="o">.</span><span class="n">all</span><span class="p">())</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.get_engine" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_engine</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return cached SQLAlchemy engine for the given URL (one per process).</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/session.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">42</span>
|
||
<span class="normal">43</span>
|
||
<span class="normal">44</span>
|
||
<span class="normal">45</span>
|
||
<span class="normal">46</span>
|
||
<span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_engine</span><span class="p">(</span><span class="n">database_url</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Return cached SQLAlchemy engine for the given URL (one per process)."""</span>
|
||
<span class="k">global</span> <span class="n">_engine</span>
|
||
<span class="k">if</span> <span class="n">_engine</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">_engine</span> <span class="o">=</span> <span class="n">create_engine</span><span class="p">(</span>
|
||
<span class="n">database_url</span><span class="p">,</span>
|
||
<span class="n">connect_args</span><span class="o">=</span><span class="p">{</span><span class="s2">"check_same_thread"</span><span class="p">:</span> <span class="kc">False</span><span class="p">}</span>
|
||
<span class="k">if</span> <span class="s2">"sqlite"</span> <span class="ow">in</span> <span class="n">database_url</span>
|
||
<span class="k">else</span> <span class="p">{},</span>
|
||
<span class="n">echo</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">_engine</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.get_or_create_user" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_or_create_user</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">,</span> <span class="n">full_name</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">first_name</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">last_name</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Get or create user by Telegram user ID.</p>
|
||
<p>On create, name fields come from Telegram. On update: username is always
|
||
synced; full_name, first_name, last_name are updated only if
|
||
name_manually_edited is False (otherwise existing display name is kept).</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>telegram_user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram user id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>full_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Display full name.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>username</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram username (optional).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>None</code>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>first_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram first name (optional).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>None</code>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>last_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram last name (optional).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>None</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="User (duty_teller.db.models.User)" href="#duty_teller.db.models.User">User</a></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User instance (created or updated).</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span>
|
||
<span class="normal">31</span>
|
||
<span class="normal">32</span>
|
||
<span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span>
|
||
<span class="normal">38</span>
|
||
<span class="normal">39</span>
|
||
<span class="normal">40</span>
|
||
<span class="normal">41</span>
|
||
<span class="normal">42</span>
|
||
<span class="normal">43</span>
|
||
<span class="normal">44</span>
|
||
<span class="normal">45</span>
|
||
<span class="normal">46</span>
|
||
<span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span>
|
||
<span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span>
|
||
<span class="normal">63</span>
|
||
<span class="normal">64</span>
|
||
<span class="normal">65</span>
|
||
<span class="normal">66</span>
|
||
<span class="normal">67</span>
|
||
<span class="normal">68</span>
|
||
<span class="normal">69</span>
|
||
<span class="normal">70</span>
|
||
<span class="normal">71</span>
|
||
<span class="normal">72</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_or_create_user</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">telegram_user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="n">full_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">first_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">last_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">User</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Get or create user by Telegram user ID.</span>
|
||
|
||
<span class="sd"> On create, name fields come from Telegram. On update: username is always</span>
|
||
<span class="sd"> synced; full_name, first_name, last_name are updated only if</span>
|
||
<span class="sd"> name_manually_edited is False (otherwise existing display name is kept).</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> telegram_user_id: Telegram user id.</span>
|
||
<span class="sd"> full_name: Display full name.</span>
|
||
<span class="sd"> username: Telegram username (optional).</span>
|
||
<span class="sd"> first_name: Telegram first name (optional).</span>
|
||
<span class="sd"> last_name: Telegram last name (optional).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> User instance (created or updated).</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">get_user_by_telegram_id</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">user</span><span class="p">:</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">username</span> <span class="o">=</span> <span class="n">username</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="o">.</span><span class="n">name_manually_edited</span><span class="p">:</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">full_name</span> <span class="o">=</span> <span class="n">full_name</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">first_name</span> <span class="o">=</span> <span class="n">first_name</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">last_name</span> <span class="o">=</span> <span class="n">last_name</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span>
|
||
<span class="n">telegram_user_id</span><span class="o">=</span><span class="n">telegram_user_id</span><span class="p">,</span>
|
||
<span class="n">full_name</span><span class="o">=</span><span class="n">full_name</span><span class="p">,</span>
|
||
<span class="n">username</span><span class="o">=</span><span class="n">username</span><span class="p">,</span>
|
||
<span class="n">first_name</span><span class="o">=</span><span class="n">first_name</span><span class="p">,</span>
|
||
<span class="n">last_name</span><span class="o">=</span><span class="n">last_name</span><span class="p">,</span>
|
||
<span class="n">name_manually_edited</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.get_or_create_user_by_full_name" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_or_create_user_by_full_name</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">full_name</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Find user by exact full_name or create one (for duty-schedule import).</p>
|
||
<p>New users have telegram_user_id=None and name_manually_edited=True.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>full_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Exact full name to match or set.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="User (duty_teller.db.models.User)" href="#duty_teller.db.models.User">User</a></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User instance (existing or newly created).</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 75</span>
|
||
<span class="normal"> 76</span>
|
||
<span class="normal"> 77</span>
|
||
<span class="normal"> 78</span>
|
||
<span class="normal"> 79</span>
|
||
<span class="normal"> 80</span>
|
||
<span class="normal"> 81</span>
|
||
<span class="normal"> 82</span>
|
||
<span class="normal"> 83</span>
|
||
<span class="normal"> 84</span>
|
||
<span class="normal"> 85</span>
|
||
<span class="normal"> 86</span>
|
||
<span class="normal"> 87</span>
|
||
<span class="normal"> 88</span>
|
||
<span class="normal"> 89</span>
|
||
<span class="normal"> 90</span>
|
||
<span class="normal"> 91</span>
|
||
<span class="normal"> 92</span>
|
||
<span class="normal"> 93</span>
|
||
<span class="normal"> 94</span>
|
||
<span class="normal"> 95</span>
|
||
<span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span>
|
||
<span class="normal">101</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_or_create_user_by_full_name</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">full_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">User</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Find user by exact full_name or create one (for duty-schedule import).</span>
|
||
|
||
<span class="sd"> New users have telegram_user_id=None and name_manually_edited=True.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> full_name: Exact full name to match or set.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> User instance (existing or newly created).</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">User</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">full_name</span> <span class="o">==</span> <span class="n">full_name</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="n">user</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span>
|
||
<span class="n">telegram_user_id</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">full_name</span><span class="o">=</span><span class="n">full_name</span><span class="p">,</span>
|
||
<span class="n">username</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">first_name</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">last_name</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">name_manually_edited</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.get_session" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_session</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Create a new session from the factory for the given URL.</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/session.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">65</span>
|
||
<span class="normal">66</span>
|
||
<span class="normal">67</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_session</span><span class="p">(</span><span class="n">database_url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">Session</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Create a new session from the factory for the given URL."""</span>
|
||
<span class="k">return</span> <span class="n">get_session_factory</span><span class="p">(</span><span class="n">database_url</span><span class="p">)()</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.get_session_factory" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_session_factory</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return cached session factory for the given URL (one per process).</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/session.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_session_factory</span><span class="p">(</span><span class="n">database_url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">sessionmaker</span><span class="p">[</span><span class="n">Session</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Return cached session factory for the given URL (one per process)."""</span>
|
||
<span class="k">global</span> <span class="n">_SessionLocal</span>
|
||
<span class="k">if</span> <span class="n">_SessionLocal</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">engine</span> <span class="o">=</span> <span class="n">get_engine</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span>
|
||
<span class="n">_SessionLocal</span> <span class="o">=</span> <span class="n">sessionmaker</span><span class="p">(</span><span class="n">autocommit</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">autoflush</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">_SessionLocal</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.init_db" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">init_db</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Create all tables from SQLAlchemy metadata.</p>
|
||
<p>Prefer Alembic migrations for schema changes in production.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>database_url</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>SQLAlchemy database URL.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/__init__.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span>
|
||
<span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">init_db</span><span class="p">(</span><span class="n">database_url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Create all tables from SQLAlchemy metadata.</span>
|
||
|
||
<span class="sd"> Prefer Alembic migrations for schema changes in production.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> database_url: SQLAlchemy database URL.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">engine</span> <span class="o">=</span> <span class="n">get_engine</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span>
|
||
<span class="n">Base</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">create_all</span><span class="p">(</span><span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.insert_duty" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">insert_duty</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">user_id</span><span class="p">,</span> <span class="n">start_at</span><span class="p">,</span> <span class="n">end_at</span><span class="p">,</span> <span class="n">event_type</span><span class="o">=</span><span class="s1">'duty'</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Create a duty record.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>start_at</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Start time UTC, ISO 8601 with Z (e.g. 2025-01-15T09:00:00Z).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>end_at</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>End time UTC, ISO 8601 with Z.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>event_type</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>One of "duty", "unavailable", "vacation". Default "duty".</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>'duty'</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="Duty (duty_teller.db.models.Duty)" href="#duty_teller.db.models.Duty">Duty</a></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Created Duty instance.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">288</span>
|
||
<span class="normal">289</span>
|
||
<span class="normal">290</span>
|
||
<span class="normal">291</span>
|
||
<span class="normal">292</span>
|
||
<span class="normal">293</span>
|
||
<span class="normal">294</span>
|
||
<span class="normal">295</span>
|
||
<span class="normal">296</span>
|
||
<span class="normal">297</span>
|
||
<span class="normal">298</span>
|
||
<span class="normal">299</span>
|
||
<span class="normal">300</span>
|
||
<span class="normal">301</span>
|
||
<span class="normal">302</span>
|
||
<span class="normal">303</span>
|
||
<span class="normal">304</span>
|
||
<span class="normal">305</span>
|
||
<span class="normal">306</span>
|
||
<span class="normal">307</span>
|
||
<span class="normal">308</span>
|
||
<span class="normal">309</span>
|
||
<span class="normal">310</span>
|
||
<span class="normal">311</span>
|
||
<span class="normal">312</span>
|
||
<span class="normal">313</span>
|
||
<span class="normal">314</span>
|
||
<span class="normal">315</span>
|
||
<span class="normal">316</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">insert_duty</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="n">start_at</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">end_at</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">event_type</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"duty"</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">Duty</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Create a duty record.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> user_id: User id.</span>
|
||
<span class="sd"> start_at: Start time UTC, ISO 8601 with Z (e.g. 2025-01-15T09:00:00Z).</span>
|
||
<span class="sd"> end_at: End time UTC, ISO 8601 with Z.</span>
|
||
<span class="sd"> event_type: One of "duty", "unavailable", "vacation". Default "duty".</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Created Duty instance.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">duty</span> <span class="o">=</span> <span class="n">Duty</span><span class="p">(</span>
|
||
<span class="n">user_id</span><span class="o">=</span><span class="n">user_id</span><span class="p">,</span>
|
||
<span class="n">start_at</span><span class="o">=</span><span class="n">start_at</span><span class="p">,</span>
|
||
<span class="n">end_at</span><span class="o">=</span><span class="n">end_at</span><span class="p">,</span>
|
||
<span class="n">event_type</span><span class="o">=</span><span class="n">event_type</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">duty</span><span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">duty</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">duty</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.session_scope" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">session_scope</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Context manager that yields a session; rolls back on exception, closes on exit.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>database_url</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>SQLAlchemy database URL.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Yields:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Session instance. Caller must not use it after exit.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/session.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">22</span>
|
||
<span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span>
|
||
<span class="normal">31</span>
|
||
<span class="normal">32</span>
|
||
<span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span>
|
||
<span class="normal">38</span>
|
||
<span class="normal">39</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@contextmanager</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">session_scope</span><span class="p">(</span><span class="n">database_url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">Generator</span><span class="p">[</span><span class="n">Session</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Context manager that yields a session; rolls back on exception, closes on exit.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> database_url: SQLAlchemy database URL.</span>
|
||
|
||
<span class="sd"> Yields:</span>
|
||
<span class="sd"> Session instance. Caller must not use it after exit.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">session</span> <span class="o">=</span> <span class="n">get_session</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">yield</span> <span class="n">session</span>
|
||
<span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">rollback</span><span class="p">()</span>
|
||
<span class="k">raise</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.set_user_phone" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">set_user_phone</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">,</span> <span class="n">phone</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Set or clear phone for user by Telegram user id.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>telegram_user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram user id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>phone</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Phone string or None to clear.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="User (duty_teller.db.models.User)" href="#duty_teller.db.models.User">User</a> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Updated User or None if not found.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">453</span>
|
||
<span class="normal">454</span>
|
||
<span class="normal">455</span>
|
||
<span class="normal">456</span>
|
||
<span class="normal">457</span>
|
||
<span class="normal">458</span>
|
||
<span class="normal">459</span>
|
||
<span class="normal">460</span>
|
||
<span class="normal">461</span>
|
||
<span class="normal">462</span>
|
||
<span class="normal">463</span>
|
||
<span class="normal">464</span>
|
||
<span class="normal">465</span>
|
||
<span class="normal">466</span>
|
||
<span class="normal">467</span>
|
||
<span class="normal">468</span>
|
||
<span class="normal">469</span>
|
||
<span class="normal">470</span>
|
||
<span class="normal">471</span>
|
||
<span class="normal">472</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">set_user_phone</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">phone</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">User</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Set or clear phone for user by Telegram user id.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> telegram_user_id: Telegram user id.</span>
|
||
<span class="sd"> phone: Phone string or None to clear.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Updated User or None if not found.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">User</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">telegram_user_id</span> <span class="o">==</span> <span class="n">telegram_user_id</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="kc">None</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">phone</span> <span class="o">=</span> <span class="n">phone</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.update_user_display_name" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">update_user_display_name</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">,</span> <span class="n">full_name</span><span class="p">,</span> <span class="n">first_name</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">last_name</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Update display name and set name_manually_edited=True.</p>
|
||
<p>Use from API or admin when name is changed manually; subsequent
|
||
get_or_create_user will not overwrite these fields.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>telegram_user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram user id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>full_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>New full name.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>first_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>New first name (optional).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>None</code>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>last_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>New last name (optional).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>None</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="User (duty_teller.db.models.User)" href="#duty_teller.db.models.User">User</a> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Updated User or None if not found.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">104</span>
|
||
<span class="normal">105</span>
|
||
<span class="normal">106</span>
|
||
<span class="normal">107</span>
|
||
<span class="normal">108</span>
|
||
<span class="normal">109</span>
|
||
<span class="normal">110</span>
|
||
<span class="normal">111</span>
|
||
<span class="normal">112</span>
|
||
<span class="normal">113</span>
|
||
<span class="normal">114</span>
|
||
<span class="normal">115</span>
|
||
<span class="normal">116</span>
|
||
<span class="normal">117</span>
|
||
<span class="normal">118</span>
|
||
<span class="normal">119</span>
|
||
<span class="normal">120</span>
|
||
<span class="normal">121</span>
|
||
<span class="normal">122</span>
|
||
<span class="normal">123</span>
|
||
<span class="normal">124</span>
|
||
<span class="normal">125</span>
|
||
<span class="normal">126</span>
|
||
<span class="normal">127</span>
|
||
<span class="normal">128</span>
|
||
<span class="normal">129</span>
|
||
<span class="normal">130</span>
|
||
<span class="normal">131</span>
|
||
<span class="normal">132</span>
|
||
<span class="normal">133</span>
|
||
<span class="normal">134</span>
|
||
<span class="normal">135</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">update_user_display_name</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">telegram_user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="n">full_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">first_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">last_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">User</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Update display name and set name_manually_edited=True.</span>
|
||
|
||
<span class="sd"> Use from API or admin when name is changed manually; subsequent</span>
|
||
<span class="sd"> get_or_create_user will not overwrite these fields.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> telegram_user_id: Telegram user id.</span>
|
||
<span class="sd"> full_name: New full name.</span>
|
||
<span class="sd"> first_name: New first name (optional).</span>
|
||
<span class="sd"> last_name: New last name (optional).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Updated User or None if not found.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">User</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">telegram_user_id</span> <span class="o">==</span> <span class="n">telegram_user_id</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="kc">None</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">full_name</span> <span class="o">=</span> <span class="n">full_name</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">first_name</span> <span class="o">=</span> <span class="n">first_name</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">last_name</span> <span class="o">=</span> <span class="n">last_name</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">name_manually_edited</span> <span class="o">=</span> <span class="kc">True</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.db.models" class="doc doc-heading">
|
||
<code>duty_teller.db.models</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>SQLAlchemy ORM models for users and duties.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.models.Base" class="doc doc-heading">
|
||
<code>Base</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><span title="sqlalchemy.orm.DeclarativeBase">DeclarativeBase</span></code></p>
|
||
|
||
|
||
|
||
<p>Declarative base for all models.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/models.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 7</span>
|
||
<span class="normal"> 8</span>
|
||
<span class="normal"> 9</span>
|
||
<span class="normal">10</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">Base</span><span class="p">(</span><span class="n">DeclarativeBase</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Declarative base for all models."""</span>
|
||
|
||
<span class="k">pass</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.models.CalendarSubscriptionToken" class="doc doc-heading">
|
||
<code>CalendarSubscriptionToken</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="Base (duty_teller.db.models.Base)" href="#duty_teller.db.models.Base">Base</a></code></p>
|
||
|
||
|
||
|
||
<p>One active calendar subscription token per user; token_hash is unique.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/models.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span>
|
||
<span class="normal">38</span>
|
||
<span class="normal">39</span>
|
||
<span class="normal">40</span>
|
||
<span class="normal">41</span>
|
||
<span class="normal">42</span>
|
||
<span class="normal">43</span>
|
||
<span class="normal">44</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">CalendarSubscriptionToken</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""One active calendar subscription token per user; token_hash is unique."""</span>
|
||
|
||
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">"calendar_subscription_tokens"</span>
|
||
|
||
<span class="nb">id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">autoincrement</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">user_id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span>
|
||
<span class="n">Integer</span><span class="p">,</span> <span class="n">ForeignKey</span><span class="p">(</span><span class="s2">"users.id"</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span>
|
||
<span class="p">)</span>
|
||
<span class="n">token_hash</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">created_at</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.models.Duty" class="doc doc-heading">
|
||
<code>Duty</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="Base (duty_teller.db.models.Base)" href="#duty_teller.db.models.Base">Base</a></code></p>
|
||
|
||
|
||
|
||
<p>Single duty/unavailable/vacation slot (UTC start_at/end_at, event_type).</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/models.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span>
|
||
<span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">Duty</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Single duty/unavailable/vacation slot (UTC start_at/end_at, event_type)."""</span>
|
||
|
||
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">"duties"</span>
|
||
|
||
<span class="nb">id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">autoincrement</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">user_id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span>
|
||
<span class="n">Integer</span><span class="p">,</span> <span class="n">ForeignKey</span><span class="p">(</span><span class="s2">"users.id"</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span>
|
||
<span class="p">)</span>
|
||
<span class="c1"># UTC, ISO 8601 with Z suffix (e.g. 2025-01-15T09:00:00Z)</span>
|
||
<span class="n">start_at</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="n">end_at</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="c1"># duty | unavailable | vacation</span>
|
||
<span class="n">event_type</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">server_default</span><span class="o">=</span><span class="s2">"duty"</span><span class="p">)</span>
|
||
|
||
<span class="n">user</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="s2">"User"</span><span class="p">]</span> <span class="o">=</span> <span class="n">relationship</span><span class="p">(</span><span class="s2">"User"</span><span class="p">,</span> <span class="n">back_populates</span><span class="o">=</span><span class="s2">"duties"</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.models.GroupDutyPin" class="doc doc-heading">
|
||
<code>GroupDutyPin</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="Base (duty_teller.db.models.Base)" href="#duty_teller.db.models.Base">Base</a></code></p>
|
||
|
||
|
||
|
||
<p>Stores which message to update in each group for the pinned duty notice.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/models.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">65</span>
|
||
<span class="normal">66</span>
|
||
<span class="normal">67</span>
|
||
<span class="normal">68</span>
|
||
<span class="normal">69</span>
|
||
<span class="normal">70</span>
|
||
<span class="normal">71</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">GroupDutyPin</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Stores which message to update in each group for the pinned duty notice."""</span>
|
||
|
||
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">"group_duty_pins"</span>
|
||
|
||
<span class="n">chat_id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">BigInteger</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">message_id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.models.User" class="doc doc-heading">
|
||
<code>User</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="Base (duty_teller.db.models.Base)" href="#duty_teller.db.models.Base">Base</a></code></p>
|
||
|
||
|
||
|
||
<p>Telegram user and display name; may have telegram_user_id=None for import-only users.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/models.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">13</span>
|
||
<span class="normal">14</span>
|
||
<span class="normal">15</span>
|
||
<span class="normal">16</span>
|
||
<span class="normal">17</span>
|
||
<span class="normal">18</span>
|
||
<span class="normal">19</span>
|
||
<span class="normal">20</span>
|
||
<span class="normal">21</span>
|
||
<span class="normal">22</span>
|
||
<span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span>
|
||
<span class="normal">31</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">User</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Telegram user and display name; may have telegram_user_id=None for import-only users."""</span>
|
||
|
||
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">"users"</span>
|
||
|
||
<span class="nb">id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">autoincrement</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">telegram_user_id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span>
|
||
<span class="n">BigInteger</span><span class="p">,</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">True</span>
|
||
<span class="p">)</span>
|
||
<span class="n">full_name</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="n">username</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">first_name</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">last_name</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">phone</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">name_manually_edited</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span>
|
||
<span class="n">Boolean</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">server_default</span><span class="o">=</span><span class="s2">"0"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">False</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="n">duties</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">list</span><span class="p">[</span><span class="s2">"Duty"</span><span class="p">]]</span> <span class="o">=</span> <span class="n">relationship</span><span class="p">(</span><span class="s2">"Duty"</span><span class="p">,</span> <span class="n">back_populates</span><span class="o">=</span><span class="s2">"user"</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.db.schemas" class="doc doc-heading">
|
||
<code>duty_teller.db.schemas</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Pydantic schemas for API request/response and validation.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.schemas.CalendarEvent" class="doc doc-heading">
|
||
<code>CalendarEvent</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><span title="pydantic.BaseModel">BaseModel</span></code></p>
|
||
|
||
|
||
|
||
<p>External calendar event (e.g. holiday) for a single day.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">66</span>
|
||
<span class="normal">67</span>
|
||
<span class="normal">68</span>
|
||
<span class="normal">69</span>
|
||
<span class="normal">70</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">CalendarEvent</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""External calendar event (e.g. holiday) for a single day."""</span>
|
||
|
||
<span class="n">date</span><span class="p">:</span> <span class="nb">str</span> <span class="c1"># YYYY-MM-DD</span>
|
||
<span class="n">summary</span><span class="p">:</span> <span class="nb">str</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.schemas.DutyBase" class="doc doc-heading">
|
||
<code>DutyBase</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><span title="pydantic.BaseModel">BaseModel</span></code></p>
|
||
|
||
|
||
|
||
<p>Duty fields: user_id, start_at, end_at (UTC ISO 8601 with Z).</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">32</span>
|
||
<span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">DutyBase</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Duty fields: user_id, start_at, end_at (UTC ISO 8601 with Z)."""</span>
|
||
|
||
<span class="n">user_id</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">start_at</span><span class="p">:</span> <span class="nb">str</span> <span class="c1"># UTC, ISO 8601 with Z</span>
|
||
<span class="n">end_at</span><span class="p">:</span> <span class="nb">str</span> <span class="c1"># UTC, ISO 8601 with Z</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.schemas.DutyCreate" class="doc doc-heading">
|
||
<code>DutyCreate</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="DutyBase (duty_teller.db.schemas.DutyBase)" href="#duty_teller.db.schemas.DutyBase">DutyBase</a></code></p>
|
||
|
||
|
||
|
||
<p>Duty creation payload.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">40</span>
|
||
<span class="normal">41</span>
|
||
<span class="normal">42</span>
|
||
<span class="normal">43</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">DutyCreate</span><span class="p">(</span><span class="n">DutyBase</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Duty creation payload."""</span>
|
||
|
||
<span class="k">pass</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.schemas.DutyInDb" class="doc doc-heading">
|
||
<code>DutyInDb</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="DutyBase (duty_teller.db.schemas.DutyBase)" href="#duty_teller.db.schemas.DutyBase">DutyBase</a></code></p>
|
||
|
||
|
||
|
||
<p>Duty as stored in DB (includes id).</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">46</span>
|
||
<span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">DutyInDb</span><span class="p">(</span><span class="n">DutyBase</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Duty as stored in DB (includes id)."""</span>
|
||
|
||
<span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>
|
||
|
||
<span class="n">model_config</span> <span class="o">=</span> <span class="n">ConfigDict</span><span class="p">(</span><span class="n">from_attributes</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.schemas.DutyWithUser" class="doc doc-heading">
|
||
<code>DutyWithUser</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="DutyInDb (duty_teller.db.schemas.DutyInDb)" href="#duty_teller.db.schemas.DutyInDb">DutyInDb</a></code></p>
|
||
|
||
|
||
|
||
<p>Duty with full_name and event_type for calendar display.</p>
|
||
<p>event_type: only these values are returned; unknown DB values are mapped to "duty" in the API.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span>
|
||
<span class="normal">63</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">DutyWithUser</span><span class="p">(</span><span class="n">DutyInDb</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Duty with full_name and event_type for calendar display.</span>
|
||
|
||
<span class="sd"> event_type: only these values are returned; unknown DB values are mapped to "duty" in the API.</span>
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="n">full_name</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">event_type</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"duty"</span><span class="p">,</span> <span class="s2">"unavailable"</span><span class="p">,</span> <span class="s2">"vacation"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"duty"</span>
|
||
|
||
<span class="n">model_config</span> <span class="o">=</span> <span class="n">ConfigDict</span><span class="p">(</span><span class="n">from_attributes</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.schemas.UserBase" class="doc doc-heading">
|
||
<code>UserBase</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><span title="pydantic.BaseModel">BaseModel</span></code></p>
|
||
|
||
|
||
|
||
<p>Base user fields (full_name, username, first/last name).</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 8</span>
|
||
<span class="normal"> 9</span>
|
||
<span class="normal">10</span>
|
||
<span class="normal">11</span>
|
||
<span class="normal">12</span>
|
||
<span class="normal">13</span>
|
||
<span class="normal">14</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">UserBase</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Base user fields (full_name, username, first/last name)."""</span>
|
||
|
||
<span class="n">full_name</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">first_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">last_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.schemas.UserCreate" class="doc doc-heading">
|
||
<code>UserCreate</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="UserBase (duty_teller.db.schemas.UserBase)" href="#duty_teller.db.schemas.UserBase">UserBase</a></code></p>
|
||
|
||
|
||
|
||
<p>User creation payload including Telegram user id.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">17</span>
|
||
<span class="normal">18</span>
|
||
<span class="normal">19</span>
|
||
<span class="normal">20</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">UserCreate</span><span class="p">(</span><span class="n">UserBase</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""User creation payload including Telegram user id."""</span>
|
||
|
||
<span class="n">telegram_user_id</span><span class="p">:</span> <span class="nb">int</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.db.schemas.UserInDb" class="doc doc-heading">
|
||
<code>UserInDb</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><a class="autorefs autorefs-internal" title="UserBase (duty_teller.db.schemas.UserBase)" href="#duty_teller.db.schemas.UserBase">UserBase</a></code></p>
|
||
|
||
|
||
|
||
<p>User as stored in DB (includes id and telegram_user_id).</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/schemas.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">UserInDb</span><span class="p">(</span><span class="n">UserBase</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""User as stored in DB (includes id and telegram_user_id)."""</span>
|
||
|
||
<span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">telegram_user_id</span><span class="p">:</span> <span class="nb">int</span>
|
||
|
||
<span class="n">model_config</span> <span class="o">=</span> <span class="n">ConfigDict</span><span class="p">(</span><span class="n">from_attributes</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.db.session" class="doc doc-heading">
|
||
<code>duty_teller.db.session</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>SQLAlchemy engine and session factory.</p>
|
||
<p>Engine and session factory are cached globally per process. Only one DATABASE_URL
|
||
is effectively used for the process lifetime. Using a different URL later (e.g. in
|
||
tests with in-memory SQLite) would still use the first engine. To use a different
|
||
URL in tests, set env (e.g. DATABASE_URL) before the first import of this module, or
|
||
clear _engine and _SessionLocal in test fixtures. Prefer session_scope() for all
|
||
callers so sessions are always closed and rolled back on error.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.session.get_engine" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_engine</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return cached SQLAlchemy engine for the given URL (one per process).</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/session.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">42</span>
|
||
<span class="normal">43</span>
|
||
<span class="normal">44</span>
|
||
<span class="normal">45</span>
|
||
<span class="normal">46</span>
|
||
<span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_engine</span><span class="p">(</span><span class="n">database_url</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Return cached SQLAlchemy engine for the given URL (one per process)."""</span>
|
||
<span class="k">global</span> <span class="n">_engine</span>
|
||
<span class="k">if</span> <span class="n">_engine</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">_engine</span> <span class="o">=</span> <span class="n">create_engine</span><span class="p">(</span>
|
||
<span class="n">database_url</span><span class="p">,</span>
|
||
<span class="n">connect_args</span><span class="o">=</span><span class="p">{</span><span class="s2">"check_same_thread"</span><span class="p">:</span> <span class="kc">False</span><span class="p">}</span>
|
||
<span class="k">if</span> <span class="s2">"sqlite"</span> <span class="ow">in</span> <span class="n">database_url</span>
|
||
<span class="k">else</span> <span class="p">{},</span>
|
||
<span class="n">echo</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">_engine</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.session.get_session" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_session</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Create a new session from the factory for the given URL.</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/session.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">65</span>
|
||
<span class="normal">66</span>
|
||
<span class="normal">67</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_session</span><span class="p">(</span><span class="n">database_url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">Session</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Create a new session from the factory for the given URL."""</span>
|
||
<span class="k">return</span> <span class="n">get_session_factory</span><span class="p">(</span><span class="n">database_url</span><span class="p">)()</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.session.get_session_factory" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_session_factory</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return cached session factory for the given URL (one per process).</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/session.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_session_factory</span><span class="p">(</span><span class="n">database_url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">sessionmaker</span><span class="p">[</span><span class="n">Session</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Return cached session factory for the given URL (one per process)."""</span>
|
||
<span class="k">global</span> <span class="n">_SessionLocal</span>
|
||
<span class="k">if</span> <span class="n">_SessionLocal</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">engine</span> <span class="o">=</span> <span class="n">get_engine</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span>
|
||
<span class="n">_SessionLocal</span> <span class="o">=</span> <span class="n">sessionmaker</span><span class="p">(</span><span class="n">autocommit</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">autoflush</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">_SessionLocal</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.session.session_scope" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">session_scope</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Context manager that yields a session; rolls back on exception, closes on exit.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>database_url</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>SQLAlchemy database URL.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Yields:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Session instance. Caller must not use it after exit.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/session.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">22</span>
|
||
<span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span>
|
||
<span class="normal">31</span>
|
||
<span class="normal">32</span>
|
||
<span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span>
|
||
<span class="normal">38</span>
|
||
<span class="normal">39</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@contextmanager</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">session_scope</span><span class="p">(</span><span class="n">database_url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">Generator</span><span class="p">[</span><span class="n">Session</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Context manager that yields a session; rolls back on exception, closes on exit.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> database_url: SQLAlchemy database URL.</span>
|
||
|
||
<span class="sd"> Yields:</span>
|
||
<span class="sd"> Session instance. Caller must not use it after exit.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">session</span> <span class="o">=</span> <span class="n">get_session</span><span class="p">(</span><span class="n">database_url</span><span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">yield</span> <span class="n">session</span>
|
||
<span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">rollback</span><span class="p">()</span>
|
||
<span class="k">raise</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.db.repository" class="doc doc-heading">
|
||
<code>duty_teller.db.repository</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Repository: get_or_create_user, get_duties, insert_duty, get_current_duty, group_duty_pins.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.create_calendar_token" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">create_calendar_token</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">user_id</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Create a new calendar subscription token for the user.</p>
|
||
<p>Any existing tokens for this user are removed. The raw token is returned
|
||
only once (not stored in plain text).</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Raw token string (e.g. for URL /api/calendar/ical/{token}.ics).</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">232</span>
|
||
<span class="normal">233</span>
|
||
<span class="normal">234</span>
|
||
<span class="normal">235</span>
|
||
<span class="normal">236</span>
|
||
<span class="normal">237</span>
|
||
<span class="normal">238</span>
|
||
<span class="normal">239</span>
|
||
<span class="normal">240</span>
|
||
<span class="normal">241</span>
|
||
<span class="normal">242</span>
|
||
<span class="normal">243</span>
|
||
<span class="normal">244</span>
|
||
<span class="normal">245</span>
|
||
<span class="normal">246</span>
|
||
<span class="normal">247</span>
|
||
<span class="normal">248</span>
|
||
<span class="normal">249</span>
|
||
<span class="normal">250</span>
|
||
<span class="normal">251</span>
|
||
<span class="normal">252</span>
|
||
<span class="normal">253</span>
|
||
<span class="normal">254</span>
|
||
<span class="normal">255</span>
|
||
<span class="normal">256</span>
|
||
<span class="normal">257</span>
|
||
<span class="normal">258</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">create_calendar_token</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Create a new calendar subscription token for the user.</span>
|
||
|
||
<span class="sd"> Any existing tokens for this user are removed. The raw token is returned</span>
|
||
<span class="sd"> only once (not stored in plain text).</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> user_id: User id.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Raw token string (e.g. for URL /api/calendar/ical/{token}.ics).</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">CalendarSubscriptionToken</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span>
|
||
<span class="n">CalendarSubscriptionToken</span><span class="o">.</span><span class="n">user_id</span> <span class="o">==</span> <span class="n">user_id</span>
|
||
<span class="p">)</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">synchronize_session</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="n">raw_token</span> <span class="o">=</span> <span class="n">secrets</span><span class="o">.</span><span class="n">token_urlsafe</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span>
|
||
<span class="n">token_hash_val</span> <span class="o">=</span> <span class="n">_token_hash</span><span class="p">(</span><span class="n">raw_token</span><span class="p">)</span>
|
||
<span class="n">now_iso</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">timezone</span><span class="o">.</span><span class="n">utc</span><span class="p">)</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-</span><span class="si">%d</span><span class="s2">T%H:%M:%SZ"</span><span class="p">)</span>
|
||
<span class="n">record</span> <span class="o">=</span> <span class="n">CalendarSubscriptionToken</span><span class="p">(</span>
|
||
<span class="n">user_id</span><span class="o">=</span><span class="n">user_id</span><span class="p">,</span>
|
||
<span class="n">token_hash</span><span class="o">=</span><span class="n">token_hash_val</span><span class="p">,</span>
|
||
<span class="n">created_at</span><span class="o">=</span><span class="n">now_iso</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="k">return</span> <span class="n">raw_token</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.delete_duties_in_range" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">delete_duties_in_range</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">user_id</span><span class="p">,</span> <span class="n">from_date</span><span class="p">,</span> <span class="n">to_date</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Delete all duties of the user that overlap the given date range.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>from_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Start date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>to_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>End date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Number of duties deleted.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">138</span>
|
||
<span class="normal">139</span>
|
||
<span class="normal">140</span>
|
||
<span class="normal">141</span>
|
||
<span class="normal">142</span>
|
||
<span class="normal">143</span>
|
||
<span class="normal">144</span>
|
||
<span class="normal">145</span>
|
||
<span class="normal">146</span>
|
||
<span class="normal">147</span>
|
||
<span class="normal">148</span>
|
||
<span class="normal">149</span>
|
||
<span class="normal">150</span>
|
||
<span class="normal">151</span>
|
||
<span class="normal">152</span>
|
||
<span class="normal">153</span>
|
||
<span class="normal">154</span>
|
||
<span class="normal">155</span>
|
||
<span class="normal">156</span>
|
||
<span class="normal">157</span>
|
||
<span class="normal">158</span>
|
||
<span class="normal">159</span>
|
||
<span class="normal">160</span>
|
||
<span class="normal">161</span>
|
||
<span class="normal">162</span>
|
||
<span class="normal">163</span>
|
||
<span class="normal">164</span>
|
||
<span class="normal">165</span>
|
||
<span class="normal">166</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">delete_duties_in_range</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="n">from_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">to_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Delete all duties of the user that overlap the given date range.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> user_id: User id.</span>
|
||
<span class="sd"> from_date: Start date YYYY-MM-DD.</span>
|
||
<span class="sd"> to_date: End date YYYY-MM-DD.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Number of duties deleted.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">to_next</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">datetime</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">to_date</span> <span class="o">+</span> <span class="s2">"T00:00:00"</span><span class="p">)</span> <span class="o">+</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
|
||
<span class="p">)</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-</span><span class="si">%d</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="n">q</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Duty</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">user_id</span> <span class="o">==</span> <span class="n">user_id</span><span class="p">,</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">start_at</span> <span class="o"><</span> <span class="n">to_next</span><span class="p">,</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">end_at</span> <span class="o">>=</span> <span class="n">from_date</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">count</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">count</span><span class="p">()</span>
|
||
<span class="n">q</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">synchronize_session</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="k">return</span> <span class="n">count</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.delete_group_duty_pin" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">delete_group_duty_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Remove the pinned duty message record for the chat (e.g. when bot leaves group).</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>chat_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram chat id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">427</span>
|
||
<span class="normal">428</span>
|
||
<span class="normal">429</span>
|
||
<span class="normal">430</span>
|
||
<span class="normal">431</span>
|
||
<span class="normal">432</span>
|
||
<span class="normal">433</span>
|
||
<span class="normal">434</span>
|
||
<span class="normal">435</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">delete_group_duty_pin</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Remove the pinned duty message record for the chat (e.g. when bot leaves group).</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> chat_id: Telegram chat id.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">GroupDutyPin</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">GroupDutyPin</span><span class="o">.</span><span class="n">chat_id</span> <span class="o">==</span> <span class="n">chat_id</span><span class="p">)</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.get_all_group_duty_pin_chat_ids" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_all_group_duty_pin_chat_ids</span><span class="p">(</span><span class="n">session</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return all chat_ids that have a pinned duty message.</p>
|
||
<p>Used to restore update jobs on bot startup.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="list">list</span>[<span title="int">int</span>]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>List of chat ids.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">438</span>
|
||
<span class="normal">439</span>
|
||
<span class="normal">440</span>
|
||
<span class="normal">441</span>
|
||
<span class="normal">442</span>
|
||
<span class="normal">443</span>
|
||
<span class="normal">444</span>
|
||
<span class="normal">445</span>
|
||
<span class="normal">446</span>
|
||
<span class="normal">447</span>
|
||
<span class="normal">448</span>
|
||
<span class="normal">449</span>
|
||
<span class="normal">450</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_all_group_duty_pin_chat_ids</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Return all chat_ids that have a pinned duty message.</span>
|
||
|
||
<span class="sd"> Used to restore update jobs on bot startup.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> List of chat ids.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">rows</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">GroupDutyPin</span><span class="o">.</span><span class="n">chat_id</span><span class="p">)</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
|
||
<span class="k">return</span> <span class="p">[</span><span class="n">r</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">rows</span><span class="p">]</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.get_current_duty" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_current_duty</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">at_utc</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return the duty and user active at the given UTC time (event_type='duty').</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>at_utc</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="datetime.datetime">datetime</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Point in time (timezone-aware or naive UTC).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="tuple">tuple</span>[<a class="autorefs autorefs-internal" title="Duty (duty_teller.db.models.Duty)" href="#duty_teller.db.models.Duty">Duty</a>, <a class="autorefs autorefs-internal" title="User (duty_teller.db.models.User)" href="#duty_teller.db.models.User">User</a>] | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>(Duty, User) or None if no duty at that time.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">319</span>
|
||
<span class="normal">320</span>
|
||
<span class="normal">321</span>
|
||
<span class="normal">322</span>
|
||
<span class="normal">323</span>
|
||
<span class="normal">324</span>
|
||
<span class="normal">325</span>
|
||
<span class="normal">326</span>
|
||
<span class="normal">327</span>
|
||
<span class="normal">328</span>
|
||
<span class="normal">329</span>
|
||
<span class="normal">330</span>
|
||
<span class="normal">331</span>
|
||
<span class="normal">332</span>
|
||
<span class="normal">333</span>
|
||
<span class="normal">334</span>
|
||
<span class="normal">335</span>
|
||
<span class="normal">336</span>
|
||
<span class="normal">337</span>
|
||
<span class="normal">338</span>
|
||
<span class="normal">339</span>
|
||
<span class="normal">340</span>
|
||
<span class="normal">341</span>
|
||
<span class="normal">342</span>
|
||
<span class="normal">343</span>
|
||
<span class="normal">344</span>
|
||
<span class="normal">345</span>
|
||
<span class="normal">346</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_current_duty</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">at_utc</span><span class="p">:</span> <span class="n">datetime</span><span class="p">)</span> <span class="o">-></span> <span class="nb">tuple</span><span class="p">[</span><span class="n">Duty</span><span class="p">,</span> <span class="n">User</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Return the duty and user active at the given UTC time (event_type='duty').</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> at_utc: Point in time (timezone-aware or naive UTC).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> (Duty, User) or None if no duty at that time.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">datetime</span><span class="w"> </span><span class="kn">import</span> <span class="n">timezone</span>
|
||
|
||
<span class="k">if</span> <span class="n">at_utc</span><span class="o">.</span><span class="n">tzinfo</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">at_utc</span> <span class="o">=</span> <span class="n">at_utc</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">timezone</span><span class="o">.</span><span class="n">utc</span><span class="p">)</span>
|
||
<span class="n">now_iso</span> <span class="o">=</span> <span class="n">at_utc</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-</span><span class="si">%d</span><span class="s2">T%H:%M:%S"</span><span class="p">)</span> <span class="o">+</span> <span class="s2">"Z"</span>
|
||
<span class="n">row</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Duty</span><span class="p">,</span> <span class="n">User</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">User</span><span class="p">,</span> <span class="n">Duty</span><span class="o">.</span><span class="n">user_id</span> <span class="o">==</span> <span class="n">User</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">filter</span><span class="p">(</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">event_type</span> <span class="o">==</span> <span class="s2">"duty"</span><span class="p">,</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">start_at</span> <span class="o"><=</span> <span class="n">now_iso</span><span class="p">,</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">end_at</span> <span class="o">></span> <span class="n">now_iso</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
<span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">row</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="kc">None</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.get_duties" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_duties</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">from_date</span><span class="p">,</span> <span class="n">to_date</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return duties overlapping the given date range with user full_name.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>from_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Start date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>to_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>End date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="list">list</span>[<span title="tuple">tuple</span>[<a class="autorefs autorefs-internal" title="Duty (duty_teller.db.models.Duty)" href="#duty_teller.db.models.Duty">Duty</a>, <span title="str">str</span>]]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>List of (Duty, full_name) tuples.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">169</span>
|
||
<span class="normal">170</span>
|
||
<span class="normal">171</span>
|
||
<span class="normal">172</span>
|
||
<span class="normal">173</span>
|
||
<span class="normal">174</span>
|
||
<span class="normal">175</span>
|
||
<span class="normal">176</span>
|
||
<span class="normal">177</span>
|
||
<span class="normal">178</span>
|
||
<span class="normal">179</span>
|
||
<span class="normal">180</span>
|
||
<span class="normal">181</span>
|
||
<span class="normal">182</span>
|
||
<span class="normal">183</span>
|
||
<span class="normal">184</span>
|
||
<span class="normal">185</span>
|
||
<span class="normal">186</span>
|
||
<span class="normal">187</span>
|
||
<span class="normal">188</span>
|
||
<span class="normal">189</span>
|
||
<span class="normal">190</span>
|
||
<span class="normal">191</span>
|
||
<span class="normal">192</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_duties</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">from_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">to_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">tuple</span><span class="p">[</span><span class="n">Duty</span><span class="p">,</span> <span class="nb">str</span><span class="p">]]:</span>
|
||
<span class="w"> </span><span class="sd">"""Return duties overlapping the given date range with user full_name.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> from_date: Start date YYYY-MM-DD.</span>
|
||
<span class="sd"> to_date: End date YYYY-MM-DD.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> List of (Duty, full_name) tuples.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">to_date_next</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">datetime</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">to_date</span> <span class="o">+</span> <span class="s2">"T00:00:00"</span><span class="p">)</span> <span class="o">+</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
|
||
<span class="p">)</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-</span><span class="si">%d</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="n">q</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Duty</span><span class="p">,</span> <span class="n">User</span><span class="o">.</span><span class="n">full_name</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">User</span><span class="p">,</span> <span class="n">Duty</span><span class="o">.</span><span class="n">user_id</span> <span class="o">==</span> <span class="n">User</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">Duty</span><span class="o">.</span><span class="n">start_at</span> <span class="o"><</span> <span class="n">to_date_next</span><span class="p">,</span> <span class="n">Duty</span><span class="o">.</span><span class="n">end_at</span> <span class="o">>=</span> <span class="n">from_date</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="n">q</span><span class="o">.</span><span class="n">all</span><span class="p">())</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.get_duties_for_user" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_duties_for_user</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">user_id</span><span class="p">,</span> <span class="n">from_date</span><span class="p">,</span> <span class="n">to_date</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return duties for one user overlapping the date range.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>from_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Start date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>to_date</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>End date YYYY-MM-DD.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="list">list</span>[<span title="tuple">tuple</span>[<a class="autorefs autorefs-internal" title="Duty (duty_teller.db.models.Duty)" href="#duty_teller.db.models.Duty">Duty</a>, <span title="str">str</span>]]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>List of (Duty, full_name) tuples.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">195</span>
|
||
<span class="normal">196</span>
|
||
<span class="normal">197</span>
|
||
<span class="normal">198</span>
|
||
<span class="normal">199</span>
|
||
<span class="normal">200</span>
|
||
<span class="normal">201</span>
|
||
<span class="normal">202</span>
|
||
<span class="normal">203</span>
|
||
<span class="normal">204</span>
|
||
<span class="normal">205</span>
|
||
<span class="normal">206</span>
|
||
<span class="normal">207</span>
|
||
<span class="normal">208</span>
|
||
<span class="normal">209</span>
|
||
<span class="normal">210</span>
|
||
<span class="normal">211</span>
|
||
<span class="normal">212</span>
|
||
<span class="normal">213</span>
|
||
<span class="normal">214</span>
|
||
<span class="normal">215</span>
|
||
<span class="normal">216</span>
|
||
<span class="normal">217</span>
|
||
<span class="normal">218</span>
|
||
<span class="normal">219</span>
|
||
<span class="normal">220</span>
|
||
<span class="normal">221</span>
|
||
<span class="normal">222</span>
|
||
<span class="normal">223</span>
|
||
<span class="normal">224</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_duties_for_user</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="n">from_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">to_date</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">tuple</span><span class="p">[</span><span class="n">Duty</span><span class="p">,</span> <span class="nb">str</span><span class="p">]]:</span>
|
||
<span class="w"> </span><span class="sd">"""Return duties for one user overlapping the date range.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> user_id: User id.</span>
|
||
<span class="sd"> from_date: Start date YYYY-MM-DD.</span>
|
||
<span class="sd"> to_date: End date YYYY-MM-DD.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> List of (Duty, full_name) tuples.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">to_date_next</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">datetime</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">to_date</span> <span class="o">+</span> <span class="s2">"T00:00:00"</span><span class="p">)</span> <span class="o">+</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
|
||
<span class="p">)</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-</span><span class="si">%d</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="n">q</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Duty</span><span class="p">,</span> <span class="n">User</span><span class="o">.</span><span class="n">full_name</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">User</span><span class="p">,</span> <span class="n">Duty</span><span class="o">.</span><span class="n">user_id</span> <span class="o">==</span> <span class="n">User</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">filter</span><span class="p">(</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">user_id</span> <span class="o">==</span> <span class="n">user_id</span><span class="p">,</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">start_at</span> <span class="o"><</span> <span class="n">to_date_next</span><span class="p">,</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">end_at</span> <span class="o">>=</span> <span class="n">from_date</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="n">q</span><span class="o">.</span><span class="n">all</span><span class="p">())</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.get_group_duty_pin" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_group_duty_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Get the pinned duty message record for a chat.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>chat_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram chat id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="GroupDutyPin (duty_teller.db.models.GroupDutyPin)" href="#duty_teller.db.models.GroupDutyPin">GroupDutyPin</a> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>GroupDutyPin or None.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">390</span>
|
||
<span class="normal">391</span>
|
||
<span class="normal">392</span>
|
||
<span class="normal">393</span>
|
||
<span class="normal">394</span>
|
||
<span class="normal">395</span>
|
||
<span class="normal">396</span>
|
||
<span class="normal">397</span>
|
||
<span class="normal">398</span>
|
||
<span class="normal">399</span>
|
||
<span class="normal">400</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_group_duty_pin</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">GroupDutyPin</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Get the pinned duty message record for a chat.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> chat_id: Telegram chat id.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> GroupDutyPin or None.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">return</span> <span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">GroupDutyPin</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">GroupDutyPin</span><span class="o">.</span><span class="n">chat_id</span> <span class="o">==</span> <span class="n">chat_id</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.get_next_shift_end" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_next_shift_end</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">after_utc</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return the end_at of the current or next duty (event_type='duty').</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>after_utc</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="datetime.datetime">datetime</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Point in time (timezone-aware or naive UTC).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="datetime.datetime">datetime</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>End datetime (naive UTC) or None if no current or future duty.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">349</span>
|
||
<span class="normal">350</span>
|
||
<span class="normal">351</span>
|
||
<span class="normal">352</span>
|
||
<span class="normal">353</span>
|
||
<span class="normal">354</span>
|
||
<span class="normal">355</span>
|
||
<span class="normal">356</span>
|
||
<span class="normal">357</span>
|
||
<span class="normal">358</span>
|
||
<span class="normal">359</span>
|
||
<span class="normal">360</span>
|
||
<span class="normal">361</span>
|
||
<span class="normal">362</span>
|
||
<span class="normal">363</span>
|
||
<span class="normal">364</span>
|
||
<span class="normal">365</span>
|
||
<span class="normal">366</span>
|
||
<span class="normal">367</span>
|
||
<span class="normal">368</span>
|
||
<span class="normal">369</span>
|
||
<span class="normal">370</span>
|
||
<span class="normal">371</span>
|
||
<span class="normal">372</span>
|
||
<span class="normal">373</span>
|
||
<span class="normal">374</span>
|
||
<span class="normal">375</span>
|
||
<span class="normal">376</span>
|
||
<span class="normal">377</span>
|
||
<span class="normal">378</span>
|
||
<span class="normal">379</span>
|
||
<span class="normal">380</span>
|
||
<span class="normal">381</span>
|
||
<span class="normal">382</span>
|
||
<span class="normal">383</span>
|
||
<span class="normal">384</span>
|
||
<span class="normal">385</span>
|
||
<span class="normal">386</span>
|
||
<span class="normal">387</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_next_shift_end</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">after_utc</span><span class="p">:</span> <span class="n">datetime</span><span class="p">)</span> <span class="o">-></span> <span class="n">datetime</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Return the end_at of the current or next duty (event_type='duty').</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> after_utc: Point in time (timezone-aware or naive UTC).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> End datetime (naive UTC) or None if no current or future duty.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">datetime</span><span class="w"> </span><span class="kn">import</span> <span class="n">timezone</span>
|
||
|
||
<span class="k">if</span> <span class="n">after_utc</span><span class="o">.</span><span class="n">tzinfo</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">after_utc</span> <span class="o">=</span> <span class="n">after_utc</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">timezone</span><span class="o">.</span><span class="n">utc</span><span class="p">)</span>
|
||
<span class="n">after_iso</span> <span class="o">=</span> <span class="n">after_utc</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-</span><span class="si">%d</span><span class="s2">T%H:%M:%S"</span><span class="p">)</span> <span class="o">+</span> <span class="s2">"Z"</span>
|
||
<span class="n">current</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Duty</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">filter</span><span class="p">(</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">event_type</span> <span class="o">==</span> <span class="s2">"duty"</span><span class="p">,</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">start_at</span> <span class="o"><=</span> <span class="n">after_iso</span><span class="p">,</span>
|
||
<span class="n">Duty</span><span class="o">.</span><span class="n">end_at</span> <span class="o">></span> <span class="n">after_iso</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
<span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">current</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">datetime</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">current</span><span class="o">.</span><span class="n">end_at</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"Z"</span><span class="p">,</span> <span class="s2">"+00:00"</span><span class="p">))</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span>
|
||
<span class="n">tzinfo</span><span class="o">=</span><span class="kc">None</span>
|
||
<span class="p">)</span>
|
||
<span class="n">next_duty</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Duty</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">Duty</span><span class="o">.</span><span class="n">event_type</span> <span class="o">==</span> <span class="s2">"duty"</span><span class="p">,</span> <span class="n">Duty</span><span class="o">.</span><span class="n">start_at</span> <span class="o">></span> <span class="n">after_iso</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="n">Duty</span><span class="o">.</span><span class="n">start_at</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
<span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">next_duty</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">datetime</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">next_duty</span><span class="o">.</span><span class="n">end_at</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"Z"</span><span class="p">,</span> <span class="s2">"+00:00"</span><span class="p">))</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span>
|
||
<span class="n">tzinfo</span><span class="o">=</span><span class="kc">None</span>
|
||
<span class="p">)</span>
|
||
<span class="k">return</span> <span class="kc">None</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.get_or_create_user" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_or_create_user</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">,</span> <span class="n">full_name</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">first_name</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">last_name</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Get or create user by Telegram user ID.</p>
|
||
<p>On create, name fields come from Telegram. On update: username is always
|
||
synced; full_name, first_name, last_name are updated only if
|
||
name_manually_edited is False (otherwise existing display name is kept).</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>telegram_user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram user id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>full_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Display full name.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>username</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram username (optional).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>None</code>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>first_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram first name (optional).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>None</code>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>last_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram last name (optional).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>None</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="User (duty_teller.db.models.User)" href="#duty_teller.db.models.User">User</a></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User instance (created or updated).</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span>
|
||
<span class="normal">31</span>
|
||
<span class="normal">32</span>
|
||
<span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span>
|
||
<span class="normal">38</span>
|
||
<span class="normal">39</span>
|
||
<span class="normal">40</span>
|
||
<span class="normal">41</span>
|
||
<span class="normal">42</span>
|
||
<span class="normal">43</span>
|
||
<span class="normal">44</span>
|
||
<span class="normal">45</span>
|
||
<span class="normal">46</span>
|
||
<span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span>
|
||
<span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span>
|
||
<span class="normal">63</span>
|
||
<span class="normal">64</span>
|
||
<span class="normal">65</span>
|
||
<span class="normal">66</span>
|
||
<span class="normal">67</span>
|
||
<span class="normal">68</span>
|
||
<span class="normal">69</span>
|
||
<span class="normal">70</span>
|
||
<span class="normal">71</span>
|
||
<span class="normal">72</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_or_create_user</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">telegram_user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="n">full_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">username</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">first_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">last_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">User</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Get or create user by Telegram user ID.</span>
|
||
|
||
<span class="sd"> On create, name fields come from Telegram. On update: username is always</span>
|
||
<span class="sd"> synced; full_name, first_name, last_name are updated only if</span>
|
||
<span class="sd"> name_manually_edited is False (otherwise existing display name is kept).</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> telegram_user_id: Telegram user id.</span>
|
||
<span class="sd"> full_name: Display full name.</span>
|
||
<span class="sd"> username: Telegram username (optional).</span>
|
||
<span class="sd"> first_name: Telegram first name (optional).</span>
|
||
<span class="sd"> last_name: Telegram last name (optional).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> User instance (created or updated).</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">get_user_by_telegram_id</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">user</span><span class="p">:</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">username</span> <span class="o">=</span> <span class="n">username</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="o">.</span><span class="n">name_manually_edited</span><span class="p">:</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">full_name</span> <span class="o">=</span> <span class="n">full_name</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">first_name</span> <span class="o">=</span> <span class="n">first_name</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">last_name</span> <span class="o">=</span> <span class="n">last_name</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span>
|
||
<span class="n">telegram_user_id</span><span class="o">=</span><span class="n">telegram_user_id</span><span class="p">,</span>
|
||
<span class="n">full_name</span><span class="o">=</span><span class="n">full_name</span><span class="p">,</span>
|
||
<span class="n">username</span><span class="o">=</span><span class="n">username</span><span class="p">,</span>
|
||
<span class="n">first_name</span><span class="o">=</span><span class="n">first_name</span><span class="p">,</span>
|
||
<span class="n">last_name</span><span class="o">=</span><span class="n">last_name</span><span class="p">,</span>
|
||
<span class="n">name_manually_edited</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.get_or_create_user_by_full_name" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_or_create_user_by_full_name</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">full_name</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Find user by exact full_name or create one (for duty-schedule import).</p>
|
||
<p>New users have telegram_user_id=None and name_manually_edited=True.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>full_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Exact full name to match or set.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="User (duty_teller.db.models.User)" href="#duty_teller.db.models.User">User</a></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User instance (existing or newly created).</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 75</span>
|
||
<span class="normal"> 76</span>
|
||
<span class="normal"> 77</span>
|
||
<span class="normal"> 78</span>
|
||
<span class="normal"> 79</span>
|
||
<span class="normal"> 80</span>
|
||
<span class="normal"> 81</span>
|
||
<span class="normal"> 82</span>
|
||
<span class="normal"> 83</span>
|
||
<span class="normal"> 84</span>
|
||
<span class="normal"> 85</span>
|
||
<span class="normal"> 86</span>
|
||
<span class="normal"> 87</span>
|
||
<span class="normal"> 88</span>
|
||
<span class="normal"> 89</span>
|
||
<span class="normal"> 90</span>
|
||
<span class="normal"> 91</span>
|
||
<span class="normal"> 92</span>
|
||
<span class="normal"> 93</span>
|
||
<span class="normal"> 94</span>
|
||
<span class="normal"> 95</span>
|
||
<span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span>
|
||
<span class="normal">101</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_or_create_user_by_full_name</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">full_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">User</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Find user by exact full_name or create one (for duty-schedule import).</span>
|
||
|
||
<span class="sd"> New users have telegram_user_id=None and name_manually_edited=True.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> full_name: Exact full name to match or set.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> User instance (existing or newly created).</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">User</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">full_name</span> <span class="o">==</span> <span class="n">full_name</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="n">user</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span>
|
||
<span class="n">telegram_user_id</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">full_name</span><span class="o">=</span><span class="n">full_name</span><span class="p">,</span>
|
||
<span class="n">username</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">first_name</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">last_name</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">name_manually_edited</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.get_user_by_calendar_token" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_user_by_calendar_token</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">token</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Find user by calendar subscription token.</p>
|
||
<p>Uses constant-time comparison to avoid timing leaks.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>token</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Raw token from URL.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="User (duty_teller.db.models.User)" href="#duty_teller.db.models.User">User</a> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User or None if token is invalid or not found.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">261</span>
|
||
<span class="normal">262</span>
|
||
<span class="normal">263</span>
|
||
<span class="normal">264</span>
|
||
<span class="normal">265</span>
|
||
<span class="normal">266</span>
|
||
<span class="normal">267</span>
|
||
<span class="normal">268</span>
|
||
<span class="normal">269</span>
|
||
<span class="normal">270</span>
|
||
<span class="normal">271</span>
|
||
<span class="normal">272</span>
|
||
<span class="normal">273</span>
|
||
<span class="normal">274</span>
|
||
<span class="normal">275</span>
|
||
<span class="normal">276</span>
|
||
<span class="normal">277</span>
|
||
<span class="normal">278</span>
|
||
<span class="normal">279</span>
|
||
<span class="normal">280</span>
|
||
<span class="normal">281</span>
|
||
<span class="normal">282</span>
|
||
<span class="normal">283</span>
|
||
<span class="normal">284</span>
|
||
<span class="normal">285</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_user_by_calendar_token</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">token</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">User</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Find user by calendar subscription token.</span>
|
||
|
||
<span class="sd"> Uses constant-time comparison to avoid timing leaks.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> token: Raw token from URL.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> User or None if token is invalid or not found.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">token_hash_val</span> <span class="o">=</span> <span class="n">_token_hash</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
|
||
<span class="n">row</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">CalendarSubscriptionToken</span><span class="p">,</span> <span class="n">User</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">User</span><span class="p">,</span> <span class="n">CalendarSubscriptionToken</span><span class="o">.</span><span class="n">user_id</span> <span class="o">==</span> <span class="n">User</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">CalendarSubscriptionToken</span><span class="o">.</span><span class="n">token_hash</span> <span class="o">==</span> <span class="n">token_hash_val</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
<span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">row</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="kc">None</span>
|
||
<span class="c1"># Constant-time compare to avoid timing leaks (token_hash is already hashed).</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">hmac</span><span class="o">.</span><span class="n">compare_digest</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">token_hash</span><span class="p">,</span> <span class="n">token_hash_val</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="kc">None</span>
|
||
<span class="k">return</span> <span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.get_user_by_telegram_id" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_user_by_telegram_id</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Find user by Telegram user ID.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>telegram_user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram user id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="User (duty_teller.db.models.User)" href="#duty_teller.db.models.User">User</a> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User or None if not found. Does not create a user.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">13</span>
|
||
<span class="normal">14</span>
|
||
<span class="normal">15</span>
|
||
<span class="normal">16</span>
|
||
<span class="normal">17</span>
|
||
<span class="normal">18</span>
|
||
<span class="normal">19</span>
|
||
<span class="normal">20</span>
|
||
<span class="normal">21</span>
|
||
<span class="normal">22</span>
|
||
<span class="normal">23</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_user_by_telegram_id</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">User</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Find user by Telegram user ID.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> telegram_user_id: Telegram user id.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> User or None if not found. Does not create a user.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">return</span> <span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">User</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">telegram_user_id</span> <span class="o">==</span> <span class="n">telegram_user_id</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.insert_duty" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">insert_duty</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">user_id</span><span class="p">,</span> <span class="n">start_at</span><span class="p">,</span> <span class="n">end_at</span><span class="p">,</span> <span class="n">event_type</span><span class="o">=</span><span class="s1">'duty'</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Create a duty record.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>start_at</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Start time UTC, ISO 8601 with Z (e.g. 2025-01-15T09:00:00Z).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>end_at</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>End time UTC, ISO 8601 with Z.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>event_type</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>One of "duty", "unavailable", "vacation". Default "duty".</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>'duty'</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="Duty (duty_teller.db.models.Duty)" href="#duty_teller.db.models.Duty">Duty</a></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Created Duty instance.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">288</span>
|
||
<span class="normal">289</span>
|
||
<span class="normal">290</span>
|
||
<span class="normal">291</span>
|
||
<span class="normal">292</span>
|
||
<span class="normal">293</span>
|
||
<span class="normal">294</span>
|
||
<span class="normal">295</span>
|
||
<span class="normal">296</span>
|
||
<span class="normal">297</span>
|
||
<span class="normal">298</span>
|
||
<span class="normal">299</span>
|
||
<span class="normal">300</span>
|
||
<span class="normal">301</span>
|
||
<span class="normal">302</span>
|
||
<span class="normal">303</span>
|
||
<span class="normal">304</span>
|
||
<span class="normal">305</span>
|
||
<span class="normal">306</span>
|
||
<span class="normal">307</span>
|
||
<span class="normal">308</span>
|
||
<span class="normal">309</span>
|
||
<span class="normal">310</span>
|
||
<span class="normal">311</span>
|
||
<span class="normal">312</span>
|
||
<span class="normal">313</span>
|
||
<span class="normal">314</span>
|
||
<span class="normal">315</span>
|
||
<span class="normal">316</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">insert_duty</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="n">start_at</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">end_at</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">event_type</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"duty"</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">Duty</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Create a duty record.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> user_id: User id.</span>
|
||
<span class="sd"> start_at: Start time UTC, ISO 8601 with Z (e.g. 2025-01-15T09:00:00Z).</span>
|
||
<span class="sd"> end_at: End time UTC, ISO 8601 with Z.</span>
|
||
<span class="sd"> event_type: One of "duty", "unavailable", "vacation". Default "duty".</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Created Duty instance.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">duty</span> <span class="o">=</span> <span class="n">Duty</span><span class="p">(</span>
|
||
<span class="n">user_id</span><span class="o">=</span><span class="n">user_id</span><span class="p">,</span>
|
||
<span class="n">start_at</span><span class="o">=</span><span class="n">start_at</span><span class="p">,</span>
|
||
<span class="n">end_at</span><span class="o">=</span><span class="n">end_at</span><span class="p">,</span>
|
||
<span class="n">event_type</span><span class="o">=</span><span class="n">event_type</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">duty</span><span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">duty</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">duty</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.save_group_duty_pin" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">save_group_duty_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">message_id</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Save or update the pinned duty message for a chat.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>chat_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram chat id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>message_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Message id to pin/update.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="GroupDutyPin (duty_teller.db.models.GroupDutyPin)" href="#duty_teller.db.models.GroupDutyPin">GroupDutyPin</a></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>GroupDutyPin instance (created or updated).</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">403</span>
|
||
<span class="normal">404</span>
|
||
<span class="normal">405</span>
|
||
<span class="normal">406</span>
|
||
<span class="normal">407</span>
|
||
<span class="normal">408</span>
|
||
<span class="normal">409</span>
|
||
<span class="normal">410</span>
|
||
<span class="normal">411</span>
|
||
<span class="normal">412</span>
|
||
<span class="normal">413</span>
|
||
<span class="normal">414</span>
|
||
<span class="normal">415</span>
|
||
<span class="normal">416</span>
|
||
<span class="normal">417</span>
|
||
<span class="normal">418</span>
|
||
<span class="normal">419</span>
|
||
<span class="normal">420</span>
|
||
<span class="normal">421</span>
|
||
<span class="normal">422</span>
|
||
<span class="normal">423</span>
|
||
<span class="normal">424</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">save_group_duty_pin</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">message_id</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">GroupDutyPin</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Save or update the pinned duty message for a chat.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> chat_id: Telegram chat id.</span>
|
||
<span class="sd"> message_id: Message id to pin/update.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> GroupDutyPin instance (created or updated).</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">pin</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">GroupDutyPin</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">GroupDutyPin</span><span class="o">.</span><span class="n">chat_id</span> <span class="o">==</span> <span class="n">chat_id</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="n">pin</span><span class="p">:</span>
|
||
<span class="n">pin</span><span class="o">.</span><span class="n">message_id</span> <span class="o">=</span> <span class="n">message_id</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">pin</span> <span class="o">=</span> <span class="n">GroupDutyPin</span><span class="p">(</span><span class="n">chat_id</span><span class="o">=</span><span class="n">chat_id</span><span class="p">,</span> <span class="n">message_id</span><span class="o">=</span><span class="n">message_id</span><span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">pin</span><span class="p">)</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">pin</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">pin</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.set_user_phone" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">set_user_phone</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">,</span> <span class="n">phone</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Set or clear phone for user by Telegram user id.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>telegram_user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram user id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>phone</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Phone string or None to clear.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="User (duty_teller.db.models.User)" href="#duty_teller.db.models.User">User</a> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Updated User or None if not found.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">453</span>
|
||
<span class="normal">454</span>
|
||
<span class="normal">455</span>
|
||
<span class="normal">456</span>
|
||
<span class="normal">457</span>
|
||
<span class="normal">458</span>
|
||
<span class="normal">459</span>
|
||
<span class="normal">460</span>
|
||
<span class="normal">461</span>
|
||
<span class="normal">462</span>
|
||
<span class="normal">463</span>
|
||
<span class="normal">464</span>
|
||
<span class="normal">465</span>
|
||
<span class="normal">466</span>
|
||
<span class="normal">467</span>
|
||
<span class="normal">468</span>
|
||
<span class="normal">469</span>
|
||
<span class="normal">470</span>
|
||
<span class="normal">471</span>
|
||
<span class="normal">472</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">set_user_phone</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">phone</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">User</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Set or clear phone for user by Telegram user id.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> telegram_user_id: Telegram user id.</span>
|
||
<span class="sd"> phone: Phone string or None to clear.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Updated User or None if not found.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">User</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">telegram_user_id</span> <span class="o">==</span> <span class="n">telegram_user_id</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="kc">None</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">phone</span> <span class="o">=</span> <span class="n">phone</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.db.repository.update_user_display_name" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">update_user_display_name</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">,</span> <span class="n">full_name</span><span class="p">,</span> <span class="n">first_name</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">last_name</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Update display name and set name_manually_edited=True.</p>
|
||
<p>Use from API or admin when name is changed manually; subsequent
|
||
get_or_create_user will not overwrite these fields.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>telegram_user_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram user id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>full_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>New full name.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>first_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>New first name (optional).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>None</code>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>last_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>New last name (optional).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>None</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="User (duty_teller.db.models.User)" href="#duty_teller.db.models.User">User</a> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Updated User or None if not found.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/db/repository.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">104</span>
|
||
<span class="normal">105</span>
|
||
<span class="normal">106</span>
|
||
<span class="normal">107</span>
|
||
<span class="normal">108</span>
|
||
<span class="normal">109</span>
|
||
<span class="normal">110</span>
|
||
<span class="normal">111</span>
|
||
<span class="normal">112</span>
|
||
<span class="normal">113</span>
|
||
<span class="normal">114</span>
|
||
<span class="normal">115</span>
|
||
<span class="normal">116</span>
|
||
<span class="normal">117</span>
|
||
<span class="normal">118</span>
|
||
<span class="normal">119</span>
|
||
<span class="normal">120</span>
|
||
<span class="normal">121</span>
|
||
<span class="normal">122</span>
|
||
<span class="normal">123</span>
|
||
<span class="normal">124</span>
|
||
<span class="normal">125</span>
|
||
<span class="normal">126</span>
|
||
<span class="normal">127</span>
|
||
<span class="normal">128</span>
|
||
<span class="normal">129</span>
|
||
<span class="normal">130</span>
|
||
<span class="normal">131</span>
|
||
<span class="normal">132</span>
|
||
<span class="normal">133</span>
|
||
<span class="normal">134</span>
|
||
<span class="normal">135</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">update_user_display_name</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">telegram_user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="n">full_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="n">first_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">last_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">User</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Update display name and set name_manually_edited=True.</span>
|
||
|
||
<span class="sd"> Use from API or admin when name is changed manually; subsequent</span>
|
||
<span class="sd"> get_or_create_user will not overwrite these fields.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> telegram_user_id: Telegram user id.</span>
|
||
<span class="sd"> full_name: New full name.</span>
|
||
<span class="sd"> first_name: New first name (optional).</span>
|
||
<span class="sd"> last_name: New last name (optional).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Updated User or None if not found.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">User</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">telegram_user_id</span> <span class="o">==</span> <span class="n">telegram_user_id</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="kc">None</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">full_name</span> <span class="o">=</span> <span class="n">full_name</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">first_name</span> <span class="o">=</span> <span class="n">first_name</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">last_name</span> <span class="o">=</span> <span class="n">last_name</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">name_manually_edited</span> <span class="o">=</span> <span class="kc">True</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
<span class="n">session</span><span class="o">.</span><span class="n">refresh</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div><h2 id="services">Services</h2>
|
||
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.services" class="doc doc-heading">
|
||
<code>duty_teller.services</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Service layer: business logic and orchestration.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.delete_pin" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">delete_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Remove the pinned message record for the chat (e.g. when bot leaves).</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>chat_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram chat id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">107</span>
|
||
<span class="normal">108</span>
|
||
<span class="normal">109</span>
|
||
<span class="normal">110</span>
|
||
<span class="normal">111</span>
|
||
<span class="normal">112</span>
|
||
<span class="normal">113</span>
|
||
<span class="normal">114</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">delete_pin</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Remove the pinned message record for the chat (e.g. when bot leaves).</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> chat_id: Telegram chat id.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">delete_group_duty_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.format_duty_message" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">format_duty_message</span><span class="p">(</span><span class="n">duty</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">tz_name</span><span class="p">,</span> <span class="n">lang</span><span class="o">=</span><span class="s1">'en'</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Build the text for the pinned duty message.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>duty</code>
|
||
</td>
|
||
<td>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Duty instance or None.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>user</code>
|
||
</td>
|
||
<td>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User instance or None.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>tz_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Timezone name for display (e.g. Europe/Moscow).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>lang</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Language code for i18n ('ru' or 'en').</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>'en'</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Formatted message string; "No duty" if duty or user is None.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">19</span>
|
||
<span class="normal">20</span>
|
||
<span class="normal">21</span>
|
||
<span class="normal">22</span>
|
||
<span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span>
|
||
<span class="normal">31</span>
|
||
<span class="normal">32</span>
|
||
<span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span>
|
||
<span class="normal">38</span>
|
||
<span class="normal">39</span>
|
||
<span class="normal">40</span>
|
||
<span class="normal">41</span>
|
||
<span class="normal">42</span>
|
||
<span class="normal">43</span>
|
||
<span class="normal">44</span>
|
||
<span class="normal">45</span>
|
||
<span class="normal">46</span>
|
||
<span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span>
|
||
<span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">format_duty_message</span><span class="p">(</span><span class="n">duty</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">tz_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">lang</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"en"</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Build the text for the pinned duty message.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> duty: Duty instance or None.</span>
|
||
<span class="sd"> user: User instance or None.</span>
|
||
<span class="sd"> tz_name: Timezone name for display (e.g. Europe/Moscow).</span>
|
||
<span class="sd"> lang: Language code for i18n ('ru' or 'en').</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Formatted message string; "No duty" if duty or user is None.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">if</span> <span class="n">duty</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="n">user</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"duty.no_duty"</span><span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">tz</span> <span class="o">=</span> <span class="n">ZoneInfo</span><span class="p">(</span><span class="n">tz_name</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
|
||
<span class="n">tz</span> <span class="o">=</span> <span class="n">ZoneInfo</span><span class="p">(</span><span class="s2">"Europe/Moscow"</span><span class="p">)</span>
|
||
<span class="n">tz_name</span> <span class="o">=</span> <span class="s2">"Europe/Moscow"</span>
|
||
<span class="n">start_dt</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">duty</span><span class="o">.</span><span class="n">start_at</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"Z"</span><span class="p">,</span> <span class="s2">"+00:00"</span><span class="p">))</span>
|
||
<span class="n">end_dt</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">duty</span><span class="o">.</span><span class="n">end_at</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"Z"</span><span class="p">,</span> <span class="s2">"+00:00"</span><span class="p">))</span>
|
||
<span class="n">start_local</span> <span class="o">=</span> <span class="n">start_dt</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="p">)</span>
|
||
<span class="n">end_local</span> <span class="o">=</span> <span class="n">end_dt</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="p">)</span>
|
||
<span class="n">offset_sec</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">start_local</span><span class="o">.</span><span class="n">utcoffset</span><span class="p">()</span><span class="o">.</span><span class="n">total_seconds</span><span class="p">()</span> <span class="k">if</span> <span class="n">start_local</span><span class="o">.</span><span class="n">utcoffset</span><span class="p">()</span> <span class="k">else</span> <span class="mi">0</span>
|
||
<span class="p">)</span>
|
||
<span class="n">sign</span> <span class="o">=</span> <span class="s2">"+"</span> <span class="k">if</span> <span class="n">offset_sec</span> <span class="o">>=</span> <span class="mi">0</span> <span class="k">else</span> <span class="s2">"-"</span>
|
||
<span class="n">h</span><span class="p">,</span> <span class="n">r</span> <span class="o">=</span> <span class="nb">divmod</span><span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">offset_sec</span><span class="p">)),</span> <span class="mi">3600</span><span class="p">)</span>
|
||
<span class="n">m</span> <span class="o">=</span> <span class="n">r</span> <span class="o">//</span> <span class="mi">60</span>
|
||
<span class="n">tz_hint</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"UTC</span><span class="si">{</span><span class="n">sign</span><span class="si">}{</span><span class="n">h</span><span class="si">:</span><span class="s2">d</span><span class="si">}</span><span class="s2">:</span><span class="si">{</span><span class="n">m</span><span class="si">:</span><span class="s2">02d</span><span class="si">}</span><span class="s2">, </span><span class="si">{</span><span class="n">tz_name</span><span class="si">}</span><span class="s2">"</span>
|
||
<span class="n">time_range</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">start_local</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">.%m.%Y %H:%M'</span><span class="p">)</span><span class="si">}</span><span class="s2"> — "</span>
|
||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">end_local</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">.%m.%Y %H:%M'</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">tz_hint</span><span class="si">}</span><span class="s2">)"</span>
|
||
<span class="p">)</span>
|
||
<span class="n">label</span> <span class="o">=</span> <span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"duty.label"</span><span class="p">)</span>
|
||
<span class="n">lines</span> <span class="o">=</span> <span class="p">[</span>
|
||
<span class="sa">f</span><span class="s2">"🕐 </span><span class="si">{</span><span class="n">label</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">time_range</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span>
|
||
<span class="sa">f</span><span class="s2">"👤 </span><span class="si">{</span><span class="n">user</span><span class="o">.</span><span class="n">full_name</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span>
|
||
<span class="p">]</span>
|
||
<span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">phone</span><span class="p">:</span>
|
||
<span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"📞 </span><span class="si">{</span><span class="n">user</span><span class="o">.</span><span class="n">phone</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">username</span><span class="p">:</span>
|
||
<span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"@</span><span class="si">{</span><span class="n">user</span><span class="o">.</span><span class="n">username</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">lines</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.get_all_pin_chat_ids" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_all_pin_chat_ids</span><span class="p">(</span><span class="n">session</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return all chat_ids that have a pinned duty message.</p>
|
||
<p>Used to restore update jobs on bot startup.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="list">list</span>[<span title="int">int</span>]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>List of chat ids.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">131</span>
|
||
<span class="normal">132</span>
|
||
<span class="normal">133</span>
|
||
<span class="normal">134</span>
|
||
<span class="normal">135</span>
|
||
<span class="normal">136</span>
|
||
<span class="normal">137</span>
|
||
<span class="normal">138</span>
|
||
<span class="normal">139</span>
|
||
<span class="normal">140</span>
|
||
<span class="normal">141</span>
|
||
<span class="normal">142</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_all_pin_chat_ids</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Return all chat_ids that have a pinned duty message.</span>
|
||
|
||
<span class="sd"> Used to restore update jobs on bot startup.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> List of chat ids.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">return</span> <span class="n">get_all_group_duty_pin_chat_ids</span><span class="p">(</span><span class="n">session</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.get_duty_message_text" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_duty_message_text</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">tz_name</span><span class="p">,</span> <span class="n">lang</span><span class="o">=</span><span class="s1">'en'</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Get current duty from DB and return formatted message text.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>tz_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Timezone name for display.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>lang</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Language code for i18n.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>'en'</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Formatted duty message or "No duty" if none.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">65</span>
|
||
<span class="normal">66</span>
|
||
<span class="normal">67</span>
|
||
<span class="normal">68</span>
|
||
<span class="normal">69</span>
|
||
<span class="normal">70</span>
|
||
<span class="normal">71</span>
|
||
<span class="normal">72</span>
|
||
<span class="normal">73</span>
|
||
<span class="normal">74</span>
|
||
<span class="normal">75</span>
|
||
<span class="normal">76</span>
|
||
<span class="normal">77</span>
|
||
<span class="normal">78</span>
|
||
<span class="normal">79</span>
|
||
<span class="normal">80</span>
|
||
<span class="normal">81</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_duty_message_text</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">tz_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">lang</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"en"</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Get current duty from DB and return formatted message text.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> tz_name: Timezone name for display.</span>
|
||
<span class="sd"> lang: Language code for i18n.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Formatted duty message or "No duty" if none.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">now</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">timezone</span><span class="o">.</span><span class="n">utc</span><span class="p">)</span>
|
||
<span class="n">result</span> <span class="o">=</span> <span class="n">get_current_duty</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">now</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">result</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"duty.no_duty"</span><span class="p">)</span>
|
||
<span class="n">duty</span><span class="p">,</span> <span class="n">user</span> <span class="o">=</span> <span class="n">result</span>
|
||
<span class="k">return</span> <span class="n">format_duty_message</span><span class="p">(</span><span class="n">duty</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">tz_name</span><span class="p">,</span> <span class="n">lang</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.get_message_id" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_message_id</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return message_id for the pinned duty message in this chat.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>chat_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram chat id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="int">int</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Message id or None if no pin record.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">117</span>
|
||
<span class="normal">118</span>
|
||
<span class="normal">119</span>
|
||
<span class="normal">120</span>
|
||
<span class="normal">121</span>
|
||
<span class="normal">122</span>
|
||
<span class="normal">123</span>
|
||
<span class="normal">124</span>
|
||
<span class="normal">125</span>
|
||
<span class="normal">126</span>
|
||
<span class="normal">127</span>
|
||
<span class="normal">128</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_message_id</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Return message_id for the pinned duty message in this chat.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> chat_id: Telegram chat id.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Message id or None if no pin record.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">pin</span> <span class="o">=</span> <span class="n">get_group_duty_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">pin</span><span class="o">.</span><span class="n">message_id</span> <span class="k">if</span> <span class="n">pin</span> <span class="k">else</span> <span class="kc">None</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.get_next_shift_end_utc" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_next_shift_end_utc</span><span class="p">(</span><span class="n">session</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return next shift end as naive UTC datetime for job scheduling.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="datetime.datetime">datetime</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Next shift end (naive UTC) or None.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">84</span>
|
||
<span class="normal">85</span>
|
||
<span class="normal">86</span>
|
||
<span class="normal">87</span>
|
||
<span class="normal">88</span>
|
||
<span class="normal">89</span>
|
||
<span class="normal">90</span>
|
||
<span class="normal">91</span>
|
||
<span class="normal">92</span>
|
||
<span class="normal">93</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_next_shift_end_utc</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">)</span> <span class="o">-></span> <span class="n">datetime</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Return next shift end as naive UTC datetime for job scheduling.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Next shift end (naive UTC) or None.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">return</span> <span class="n">get_next_shift_end</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">timezone</span><span class="o">.</span><span class="n">utc</span><span class="p">))</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.run_import" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">run_import</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">result</span><span class="p">,</span> <span class="n">hour_utc</span><span class="p">,</span> <span class="n">minute_utc</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Run duty-schedule import: delete range per user, insert duty/unavailable/vacation.</p>
|
||
<p>For each entry: get_or_create_user_by_full_name, delete_duties_in_range for
|
||
the result date range, then insert duties (handover time in UTC), unavailable
|
||
(all-day), and vacation (consecutive ranges).</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>result</code>
|
||
</td>
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="DutyScheduleResult
|
||
|
||
|
||
|
||
dataclass
|
||
(duty_teller.importers.duty_schedule.DutyScheduleResult)" href="#duty_teller.importers.duty_schedule.DutyScheduleResult">DutyScheduleResult</a></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Parsed duty schedule (start_date, end_date, entries).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>hour_utc</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Handover hour in UTC (0-23).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>minute_utc</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Handover minute in UTC (0-59).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="tuple">tuple</span>[<span title="int">int</span>, <span title="int">int</span>, <span title="int">int</span>, <span title="int">int</span>]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Tuple (num_users, num_duty, num_unavailable, num_vacation).</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/import_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span>
|
||
<span class="normal">38</span>
|
||
<span class="normal">39</span>
|
||
<span class="normal">40</span>
|
||
<span class="normal">41</span>
|
||
<span class="normal">42</span>
|
||
<span class="normal">43</span>
|
||
<span class="normal">44</span>
|
||
<span class="normal">45</span>
|
||
<span class="normal">46</span>
|
||
<span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span>
|
||
<span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span>
|
||
<span class="normal">63</span>
|
||
<span class="normal">64</span>
|
||
<span class="normal">65</span>
|
||
<span class="normal">66</span>
|
||
<span class="normal">67</span>
|
||
<span class="normal">68</span>
|
||
<span class="normal">69</span>
|
||
<span class="normal">70</span>
|
||
<span class="normal">71</span>
|
||
<span class="normal">72</span>
|
||
<span class="normal">73</span>
|
||
<span class="normal">74</span>
|
||
<span class="normal">75</span>
|
||
<span class="normal">76</span>
|
||
<span class="normal">77</span>
|
||
<span class="normal">78</span>
|
||
<span class="normal">79</span>
|
||
<span class="normal">80</span>
|
||
<span class="normal">81</span>
|
||
<span class="normal">82</span>
|
||
<span class="normal">83</span>
|
||
<span class="normal">84</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">run_import</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">result</span><span class="p">:</span> <span class="n">DutyScheduleResult</span><span class="p">,</span>
|
||
<span class="n">hour_utc</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="n">minute_utc</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Run duty-schedule import: delete range per user, insert duty/unavailable/vacation.</span>
|
||
|
||
<span class="sd"> For each entry: get_or_create_user_by_full_name, delete_duties_in_range for</span>
|
||
<span class="sd"> the result date range, then insert duties (handover time in UTC), unavailable</span>
|
||
<span class="sd"> (all-day), and vacation (consecutive ranges).</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> result: Parsed duty schedule (start_date, end_date, entries).</span>
|
||
<span class="sd"> hour_utc: Handover hour in UTC (0-23).</span>
|
||
<span class="sd"> minute_utc: Handover minute in UTC (0-59).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Tuple (num_users, num_duty, num_unavailable, num_vacation).</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">from_date_str</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="n">start_date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
|
||
<span class="n">to_date_str</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="n">end_date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
|
||
<span class="n">num_duty</span> <span class="o">=</span> <span class="n">num_unavailable</span> <span class="o">=</span> <span class="n">num_vacation</span> <span class="o">=</span> <span class="mi">0</span>
|
||
<span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">result</span><span class="o">.</span><span class="n">entries</span><span class="p">:</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">get_or_create_user_by_full_name</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">entry</span><span class="o">.</span><span class="n">full_name</span><span class="p">)</span>
|
||
<span class="n">delete_duties_in_range</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="n">from_date_str</span><span class="p">,</span> <span class="n">to_date_str</span><span class="p">)</span>
|
||
<span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">entry</span><span class="o">.</span><span class="n">duty_dates</span><span class="p">:</span>
|
||
<span class="n">start_at</span> <span class="o">=</span> <span class="n">duty_to_iso</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">hour_utc</span><span class="p">,</span> <span class="n">minute_utc</span><span class="p">)</span>
|
||
<span class="n">d_next</span> <span class="o">=</span> <span class="n">d</span> <span class="o">+</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
|
||
<span class="n">end_at</span> <span class="o">=</span> <span class="n">duty_to_iso</span><span class="p">(</span><span class="n">d_next</span><span class="p">,</span> <span class="n">hour_utc</span><span class="p">,</span> <span class="n">minute_utc</span><span class="p">)</span>
|
||
<span class="n">insert_duty</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="n">start_at</span><span class="p">,</span> <span class="n">end_at</span><span class="p">,</span> <span class="n">event_type</span><span class="o">=</span><span class="s2">"duty"</span><span class="p">)</span>
|
||
<span class="n">num_duty</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
<span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">entry</span><span class="o">.</span><span class="n">unavailable_dates</span><span class="p">:</span>
|
||
<span class="n">insert_duty</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">,</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">,</span>
|
||
<span class="n">day_start_iso</span><span class="p">(</span><span class="n">d</span><span class="p">),</span>
|
||
<span class="n">day_end_iso</span><span class="p">(</span><span class="n">d</span><span class="p">),</span>
|
||
<span class="n">event_type</span><span class="o">=</span><span class="s2">"unavailable"</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">num_unavailable</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
<span class="k">for</span> <span class="n">start_d</span><span class="p">,</span> <span class="n">end_d</span> <span class="ow">in</span> <span class="n">_consecutive_date_ranges</span><span class="p">(</span><span class="n">entry</span><span class="o">.</span><span class="n">vacation_dates</span><span class="p">):</span>
|
||
<span class="n">insert_duty</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">,</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">,</span>
|
||
<span class="n">day_start_iso</span><span class="p">(</span><span class="n">start_d</span><span class="p">),</span>
|
||
<span class="n">day_end_iso</span><span class="p">(</span><span class="n">end_d</span><span class="p">),</span>
|
||
<span class="n">event_type</span><span class="o">=</span><span class="s2">"vacation"</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">num_vacation</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">result</span><span class="o">.</span><span class="n">entries</span><span class="p">),</span> <span class="n">num_duty</span><span class="p">,</span> <span class="n">num_unavailable</span><span class="p">,</span> <span class="n">num_vacation</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.save_pin" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">save_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">message_id</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Save or update the pinned duty message record for a chat.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>chat_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram chat id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>message_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Message id to store.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span>
|
||
<span class="normal">101</span>
|
||
<span class="normal">102</span>
|
||
<span class="normal">103</span>
|
||
<span class="normal">104</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">save_pin</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">message_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Save or update the pinned duty message record for a chat.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> chat_id: Telegram chat id.</span>
|
||
<span class="sd"> message_id: Message id to store.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">save_group_duty_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">message_id</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.services.import_service" class="doc doc-heading">
|
||
<code>duty_teller.services.import_service</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Import duty schedule: delete range, insert duties/unavailable/vacation. Accepts session.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.import_service.run_import" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">run_import</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">result</span><span class="p">,</span> <span class="n">hour_utc</span><span class="p">,</span> <span class="n">minute_utc</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Run duty-schedule import: delete range per user, insert duty/unavailable/vacation.</p>
|
||
<p>For each entry: get_or_create_user_by_full_name, delete_duties_in_range for
|
||
the result date range, then insert duties (handover time in UTC), unavailable
|
||
(all-day), and vacation (consecutive ranges).</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>result</code>
|
||
</td>
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="DutyScheduleResult
|
||
|
||
|
||
|
||
dataclass
|
||
(duty_teller.importers.duty_schedule.DutyScheduleResult)" href="#duty_teller.importers.duty_schedule.DutyScheduleResult">DutyScheduleResult</a></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Parsed duty schedule (start_date, end_date, entries).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>hour_utc</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Handover hour in UTC (0-23).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>minute_utc</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Handover minute in UTC (0-59).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="tuple">tuple</span>[<span title="int">int</span>, <span title="int">int</span>, <span title="int">int</span>, <span title="int">int</span>]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Tuple (num_users, num_duty, num_unavailable, num_vacation).</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/import_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span>
|
||
<span class="normal">38</span>
|
||
<span class="normal">39</span>
|
||
<span class="normal">40</span>
|
||
<span class="normal">41</span>
|
||
<span class="normal">42</span>
|
||
<span class="normal">43</span>
|
||
<span class="normal">44</span>
|
||
<span class="normal">45</span>
|
||
<span class="normal">46</span>
|
||
<span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span>
|
||
<span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span>
|
||
<span class="normal">63</span>
|
||
<span class="normal">64</span>
|
||
<span class="normal">65</span>
|
||
<span class="normal">66</span>
|
||
<span class="normal">67</span>
|
||
<span class="normal">68</span>
|
||
<span class="normal">69</span>
|
||
<span class="normal">70</span>
|
||
<span class="normal">71</span>
|
||
<span class="normal">72</span>
|
||
<span class="normal">73</span>
|
||
<span class="normal">74</span>
|
||
<span class="normal">75</span>
|
||
<span class="normal">76</span>
|
||
<span class="normal">77</span>
|
||
<span class="normal">78</span>
|
||
<span class="normal">79</span>
|
||
<span class="normal">80</span>
|
||
<span class="normal">81</span>
|
||
<span class="normal">82</span>
|
||
<span class="normal">83</span>
|
||
<span class="normal">84</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">run_import</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span>
|
||
<span class="n">result</span><span class="p">:</span> <span class="n">DutyScheduleResult</span><span class="p">,</span>
|
||
<span class="n">hour_utc</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="n">minute_utc</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Run duty-schedule import: delete range per user, insert duty/unavailable/vacation.</span>
|
||
|
||
<span class="sd"> For each entry: get_or_create_user_by_full_name, delete_duties_in_range for</span>
|
||
<span class="sd"> the result date range, then insert duties (handover time in UTC), unavailable</span>
|
||
<span class="sd"> (all-day), and vacation (consecutive ranges).</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> result: Parsed duty schedule (start_date, end_date, entries).</span>
|
||
<span class="sd"> hour_utc: Handover hour in UTC (0-23).</span>
|
||
<span class="sd"> minute_utc: Handover minute in UTC (0-59).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Tuple (num_users, num_duty, num_unavailable, num_vacation).</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">from_date_str</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="n">start_date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
|
||
<span class="n">to_date_str</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="n">end_date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
|
||
<span class="n">num_duty</span> <span class="o">=</span> <span class="n">num_unavailable</span> <span class="o">=</span> <span class="n">num_vacation</span> <span class="o">=</span> <span class="mi">0</span>
|
||
<span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">result</span><span class="o">.</span><span class="n">entries</span><span class="p">:</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">get_or_create_user_by_full_name</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">entry</span><span class="o">.</span><span class="n">full_name</span><span class="p">)</span>
|
||
<span class="n">delete_duties_in_range</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="n">from_date_str</span><span class="p">,</span> <span class="n">to_date_str</span><span class="p">)</span>
|
||
<span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">entry</span><span class="o">.</span><span class="n">duty_dates</span><span class="p">:</span>
|
||
<span class="n">start_at</span> <span class="o">=</span> <span class="n">duty_to_iso</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">hour_utc</span><span class="p">,</span> <span class="n">minute_utc</span><span class="p">)</span>
|
||
<span class="n">d_next</span> <span class="o">=</span> <span class="n">d</span> <span class="o">+</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
|
||
<span class="n">end_at</span> <span class="o">=</span> <span class="n">duty_to_iso</span><span class="p">(</span><span class="n">d_next</span><span class="p">,</span> <span class="n">hour_utc</span><span class="p">,</span> <span class="n">minute_utc</span><span class="p">)</span>
|
||
<span class="n">insert_duty</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="n">start_at</span><span class="p">,</span> <span class="n">end_at</span><span class="p">,</span> <span class="n">event_type</span><span class="o">=</span><span class="s2">"duty"</span><span class="p">)</span>
|
||
<span class="n">num_duty</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
<span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">entry</span><span class="o">.</span><span class="n">unavailable_dates</span><span class="p">:</span>
|
||
<span class="n">insert_duty</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">,</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">,</span>
|
||
<span class="n">day_start_iso</span><span class="p">(</span><span class="n">d</span><span class="p">),</span>
|
||
<span class="n">day_end_iso</span><span class="p">(</span><span class="n">d</span><span class="p">),</span>
|
||
<span class="n">event_type</span><span class="o">=</span><span class="s2">"unavailable"</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">num_unavailable</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
<span class="k">for</span> <span class="n">start_d</span><span class="p">,</span> <span class="n">end_d</span> <span class="ow">in</span> <span class="n">_consecutive_date_ranges</span><span class="p">(</span><span class="n">entry</span><span class="o">.</span><span class="n">vacation_dates</span><span class="p">):</span>
|
||
<span class="n">insert_duty</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">,</span>
|
||
<span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">,</span>
|
||
<span class="n">day_start_iso</span><span class="p">(</span><span class="n">start_d</span><span class="p">),</span>
|
||
<span class="n">day_end_iso</span><span class="p">(</span><span class="n">end_d</span><span class="p">),</span>
|
||
<span class="n">event_type</span><span class="o">=</span><span class="s2">"vacation"</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">num_vacation</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">result</span><span class="o">.</span><span class="n">entries</span><span class="p">),</span> <span class="n">num_duty</span><span class="p">,</span> <span class="n">num_unavailable</span><span class="p">,</span> <span class="n">num_vacation</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.services.group_duty_pin_service" class="doc doc-heading">
|
||
<code>duty_teller.services.group_duty_pin_service</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Group duty pin: current duty message text, next shift end, pin CRUD. All accept session.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.group_duty_pin_service.delete_pin" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">delete_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Remove the pinned message record for the chat (e.g. when bot leaves).</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>chat_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram chat id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">107</span>
|
||
<span class="normal">108</span>
|
||
<span class="normal">109</span>
|
||
<span class="normal">110</span>
|
||
<span class="normal">111</span>
|
||
<span class="normal">112</span>
|
||
<span class="normal">113</span>
|
||
<span class="normal">114</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">delete_pin</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Remove the pinned message record for the chat (e.g. when bot leaves).</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> chat_id: Telegram chat id.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">delete_group_duty_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.group_duty_pin_service.format_duty_message" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">format_duty_message</span><span class="p">(</span><span class="n">duty</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">tz_name</span><span class="p">,</span> <span class="n">lang</span><span class="o">=</span><span class="s1">'en'</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Build the text for the pinned duty message.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>duty</code>
|
||
</td>
|
||
<td>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Duty instance or None.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>user</code>
|
||
</td>
|
||
<td>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>User instance or None.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>tz_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Timezone name for display (e.g. Europe/Moscow).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>lang</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Language code for i18n ('ru' or 'en').</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>'en'</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Formatted message string; "No duty" if duty or user is None.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">19</span>
|
||
<span class="normal">20</span>
|
||
<span class="normal">21</span>
|
||
<span class="normal">22</span>
|
||
<span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span>
|
||
<span class="normal">31</span>
|
||
<span class="normal">32</span>
|
||
<span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span>
|
||
<span class="normal">38</span>
|
||
<span class="normal">39</span>
|
||
<span class="normal">40</span>
|
||
<span class="normal">41</span>
|
||
<span class="normal">42</span>
|
||
<span class="normal">43</span>
|
||
<span class="normal">44</span>
|
||
<span class="normal">45</span>
|
||
<span class="normal">46</span>
|
||
<span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span>
|
||
<span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">format_duty_message</span><span class="p">(</span><span class="n">duty</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">tz_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">lang</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"en"</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Build the text for the pinned duty message.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> duty: Duty instance or None.</span>
|
||
<span class="sd"> user: User instance or None.</span>
|
||
<span class="sd"> tz_name: Timezone name for display (e.g. Europe/Moscow).</span>
|
||
<span class="sd"> lang: Language code for i18n ('ru' or 'en').</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Formatted message string; "No duty" if duty or user is None.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">if</span> <span class="n">duty</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="n">user</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"duty.no_duty"</span><span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">tz</span> <span class="o">=</span> <span class="n">ZoneInfo</span><span class="p">(</span><span class="n">tz_name</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
|
||
<span class="n">tz</span> <span class="o">=</span> <span class="n">ZoneInfo</span><span class="p">(</span><span class="s2">"Europe/Moscow"</span><span class="p">)</span>
|
||
<span class="n">tz_name</span> <span class="o">=</span> <span class="s2">"Europe/Moscow"</span>
|
||
<span class="n">start_dt</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">duty</span><span class="o">.</span><span class="n">start_at</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"Z"</span><span class="p">,</span> <span class="s2">"+00:00"</span><span class="p">))</span>
|
||
<span class="n">end_dt</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">duty</span><span class="o">.</span><span class="n">end_at</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"Z"</span><span class="p">,</span> <span class="s2">"+00:00"</span><span class="p">))</span>
|
||
<span class="n">start_local</span> <span class="o">=</span> <span class="n">start_dt</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="p">)</span>
|
||
<span class="n">end_local</span> <span class="o">=</span> <span class="n">end_dt</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="p">)</span>
|
||
<span class="n">offset_sec</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">start_local</span><span class="o">.</span><span class="n">utcoffset</span><span class="p">()</span><span class="o">.</span><span class="n">total_seconds</span><span class="p">()</span> <span class="k">if</span> <span class="n">start_local</span><span class="o">.</span><span class="n">utcoffset</span><span class="p">()</span> <span class="k">else</span> <span class="mi">0</span>
|
||
<span class="p">)</span>
|
||
<span class="n">sign</span> <span class="o">=</span> <span class="s2">"+"</span> <span class="k">if</span> <span class="n">offset_sec</span> <span class="o">>=</span> <span class="mi">0</span> <span class="k">else</span> <span class="s2">"-"</span>
|
||
<span class="n">h</span><span class="p">,</span> <span class="n">r</span> <span class="o">=</span> <span class="nb">divmod</span><span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">offset_sec</span><span class="p">)),</span> <span class="mi">3600</span><span class="p">)</span>
|
||
<span class="n">m</span> <span class="o">=</span> <span class="n">r</span> <span class="o">//</span> <span class="mi">60</span>
|
||
<span class="n">tz_hint</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"UTC</span><span class="si">{</span><span class="n">sign</span><span class="si">}{</span><span class="n">h</span><span class="si">:</span><span class="s2">d</span><span class="si">}</span><span class="s2">:</span><span class="si">{</span><span class="n">m</span><span class="si">:</span><span class="s2">02d</span><span class="si">}</span><span class="s2">, </span><span class="si">{</span><span class="n">tz_name</span><span class="si">}</span><span class="s2">"</span>
|
||
<span class="n">time_range</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">start_local</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">.%m.%Y %H:%M'</span><span class="p">)</span><span class="si">}</span><span class="s2"> — "</span>
|
||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">end_local</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%d</span><span class="s1">.%m.%Y %H:%M'</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">tz_hint</span><span class="si">}</span><span class="s2">)"</span>
|
||
<span class="p">)</span>
|
||
<span class="n">label</span> <span class="o">=</span> <span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"duty.label"</span><span class="p">)</span>
|
||
<span class="n">lines</span> <span class="o">=</span> <span class="p">[</span>
|
||
<span class="sa">f</span><span class="s2">"🕐 </span><span class="si">{</span><span class="n">label</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">time_range</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span>
|
||
<span class="sa">f</span><span class="s2">"👤 </span><span class="si">{</span><span class="n">user</span><span class="o">.</span><span class="n">full_name</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span>
|
||
<span class="p">]</span>
|
||
<span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">phone</span><span class="p">:</span>
|
||
<span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"📞 </span><span class="si">{</span><span class="n">user</span><span class="o">.</span><span class="n">phone</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">username</span><span class="p">:</span>
|
||
<span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"@</span><span class="si">{</span><span class="n">user</span><span class="o">.</span><span class="n">username</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">lines</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.group_duty_pin_service.get_all_pin_chat_ids" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_all_pin_chat_ids</span><span class="p">(</span><span class="n">session</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return all chat_ids that have a pinned duty message.</p>
|
||
<p>Used to restore update jobs on bot startup.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="list">list</span>[<span title="int">int</span>]</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>List of chat ids.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">131</span>
|
||
<span class="normal">132</span>
|
||
<span class="normal">133</span>
|
||
<span class="normal">134</span>
|
||
<span class="normal">135</span>
|
||
<span class="normal">136</span>
|
||
<span class="normal">137</span>
|
||
<span class="normal">138</span>
|
||
<span class="normal">139</span>
|
||
<span class="normal">140</span>
|
||
<span class="normal">141</span>
|
||
<span class="normal">142</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_all_pin_chat_ids</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
|
||
<span class="w"> </span><span class="sd">"""Return all chat_ids that have a pinned duty message.</span>
|
||
|
||
<span class="sd"> Used to restore update jobs on bot startup.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> List of chat ids.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">return</span> <span class="n">get_all_group_duty_pin_chat_ids</span><span class="p">(</span><span class="n">session</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.group_duty_pin_service.get_duty_message_text" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_duty_message_text</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">tz_name</span><span class="p">,</span> <span class="n">lang</span><span class="o">=</span><span class="s1">'en'</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Get current duty from DB and return formatted message text.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>tz_name</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Timezone name for display.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>lang</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Language code for i18n.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<code>'en'</code>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="str">str</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Formatted duty message or "No duty" if none.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">65</span>
|
||
<span class="normal">66</span>
|
||
<span class="normal">67</span>
|
||
<span class="normal">68</span>
|
||
<span class="normal">69</span>
|
||
<span class="normal">70</span>
|
||
<span class="normal">71</span>
|
||
<span class="normal">72</span>
|
||
<span class="normal">73</span>
|
||
<span class="normal">74</span>
|
||
<span class="normal">75</span>
|
||
<span class="normal">76</span>
|
||
<span class="normal">77</span>
|
||
<span class="normal">78</span>
|
||
<span class="normal">79</span>
|
||
<span class="normal">80</span>
|
||
<span class="normal">81</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_duty_message_text</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">tz_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">lang</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"en"</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Get current duty from DB and return formatted message text.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> tz_name: Timezone name for display.</span>
|
||
<span class="sd"> lang: Language code for i18n.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Formatted duty message or "No duty" if none.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">now</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">timezone</span><span class="o">.</span><span class="n">utc</span><span class="p">)</span>
|
||
<span class="n">result</span> <span class="o">=</span> <span class="n">get_current_duty</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">now</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">result</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"duty.no_duty"</span><span class="p">)</span>
|
||
<span class="n">duty</span><span class="p">,</span> <span class="n">user</span> <span class="o">=</span> <span class="n">result</span>
|
||
<span class="k">return</span> <span class="n">format_duty_message</span><span class="p">(</span><span class="n">duty</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">tz_name</span><span class="p">,</span> <span class="n">lang</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.group_duty_pin_service.get_message_id" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_message_id</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return message_id for the pinned duty message in this chat.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>chat_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram chat id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="int">int</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Message id or None if no pin record.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">117</span>
|
||
<span class="normal">118</span>
|
||
<span class="normal">119</span>
|
||
<span class="normal">120</span>
|
||
<span class="normal">121</span>
|
||
<span class="normal">122</span>
|
||
<span class="normal">123</span>
|
||
<span class="normal">124</span>
|
||
<span class="normal">125</span>
|
||
<span class="normal">126</span>
|
||
<span class="normal">127</span>
|
||
<span class="normal">128</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_message_id</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Return message_id for the pinned duty message in this chat.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> chat_id: Telegram chat id.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Message id or None if no pin record.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">pin</span> <span class="o">=</span> <span class="n">get_group_duty_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">pin</span><span class="o">.</span><span class="n">message_id</span> <span class="k">if</span> <span class="n">pin</span> <span class="k">else</span> <span class="kc">None</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.group_duty_pin_service.get_next_shift_end_utc" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">get_next_shift_end_utc</span><span class="p">(</span><span class="n">session</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Return next shift end as naive UTC datetime for job scheduling.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><span title="datetime.datetime">datetime</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Next shift end (naive UTC) or None.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">84</span>
|
||
<span class="normal">85</span>
|
||
<span class="normal">86</span>
|
||
<span class="normal">87</span>
|
||
<span class="normal">88</span>
|
||
<span class="normal">89</span>
|
||
<span class="normal">90</span>
|
||
<span class="normal">91</span>
|
||
<span class="normal">92</span>
|
||
<span class="normal">93</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">get_next_shift_end_utc</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">)</span> <span class="o">-></span> <span class="n">datetime</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Return next shift end as naive UTC datetime for job scheduling.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> Next shift end (naive UTC) or None.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">return</span> <span class="n">get_next_shift_end</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">timezone</span><span class="o">.</span><span class="n">utc</span><span class="p">))</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.services.group_duty_pin_service.save_pin" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">save_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">message_id</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Save or update the pinned duty message record for a chat.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>session</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="sqlalchemy.orm.Session">Session</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DB session.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>chat_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Telegram chat id.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>message_id</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="int">int</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Message id to store.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/services/group_duty_pin_service.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span>
|
||
<span class="normal">101</span>
|
||
<span class="normal">102</span>
|
||
<span class="normal">103</span>
|
||
<span class="normal">104</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">save_pin</span><span class="p">(</span><span class="n">session</span><span class="p">:</span> <span class="n">Session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">message_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Save or update the pinned duty message record for a chat.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> session: DB session.</span>
|
||
<span class="sd"> chat_id: Telegram chat id.</span>
|
||
<span class="sd"> message_id: Message id to store.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">save_group_duty_pin</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">message_id</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div><h2 id="handlers">Handlers</h2>
|
||
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.handlers" class="doc doc-heading">
|
||
<code>duty_teller.handlers</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Expose a single register_handlers(app) that registers all handlers.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.register_handlers" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">register_handlers</span><span class="p">(</span><span class="n">app</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Register all Telegram handlers (commands, import, group pin, error handler) on the application.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>app</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="telegram.ext.Application">Application</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>python-telegram-bot Application instance.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/__init__.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 8</span>
|
||
<span class="normal"> 9</span>
|
||
<span class="normal">10</span>
|
||
<span class="normal">11</span>
|
||
<span class="normal">12</span>
|
||
<span class="normal">13</span>
|
||
<span class="normal">14</span>
|
||
<span class="normal">15</span>
|
||
<span class="normal">16</span>
|
||
<span class="normal">17</span>
|
||
<span class="normal">18</span>
|
||
<span class="normal">19</span>
|
||
<span class="normal">20</span>
|
||
<span class="normal">21</span>
|
||
<span class="normal">22</span>
|
||
<span class="normal">23</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">register_handlers</span><span class="p">(</span><span class="n">app</span><span class="p">:</span> <span class="n">Application</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Register all Telegram handlers (commands, import, group pin, error handler) on the application.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> app: python-telegram-bot Application instance.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">app</span><span class="o">.</span><span class="n">add_handler</span><span class="p">(</span><span class="n">commands</span><span class="o">.</span><span class="n">start_handler</span><span class="p">)</span>
|
||
<span class="n">app</span><span class="o">.</span><span class="n">add_handler</span><span class="p">(</span><span class="n">commands</span><span class="o">.</span><span class="n">help_handler</span><span class="p">)</span>
|
||
<span class="n">app</span><span class="o">.</span><span class="n">add_handler</span><span class="p">(</span><span class="n">commands</span><span class="o">.</span><span class="n">set_phone_handler</span><span class="p">)</span>
|
||
<span class="n">app</span><span class="o">.</span><span class="n">add_handler</span><span class="p">(</span><span class="n">commands</span><span class="o">.</span><span class="n">calendar_link_handler</span><span class="p">)</span>
|
||
<span class="n">app</span><span class="o">.</span><span class="n">add_handler</span><span class="p">(</span><span class="n">import_duty_schedule</span><span class="o">.</span><span class="n">import_duty_schedule_handler</span><span class="p">)</span>
|
||
<span class="n">app</span><span class="o">.</span><span class="n">add_handler</span><span class="p">(</span><span class="n">import_duty_schedule</span><span class="o">.</span><span class="n">handover_time_handler</span><span class="p">)</span>
|
||
<span class="n">app</span><span class="o">.</span><span class="n">add_handler</span><span class="p">(</span><span class="n">import_duty_schedule</span><span class="o">.</span><span class="n">duty_schedule_document_handler</span><span class="p">)</span>
|
||
<span class="n">app</span><span class="o">.</span><span class="n">add_handler</span><span class="p">(</span><span class="n">group_duty_pin</span><span class="o">.</span><span class="n">group_duty_pin_handler</span><span class="p">)</span>
|
||
<span class="n">app</span><span class="o">.</span><span class="n">add_handler</span><span class="p">(</span><span class="n">group_duty_pin</span><span class="o">.</span><span class="n">pin_duty_handler</span><span class="p">)</span>
|
||
<span class="n">app</span><span class="o">.</span><span class="n">add_error_handler</span><span class="p">(</span><span class="n">errors</span><span class="o">.</span><span class="n">error_handler</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.handlers.commands" class="doc doc-heading">
|
||
<code>duty_teller.handlers.commands</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Command handlers: /start, /help; /start registers user.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.commands.calendar_link" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">calendar_link</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-async"><code>async</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Handle /calendar_link: send personal ICS URL (private chat only; user must be in allowlist).</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/commands.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 91</span>
|
||
<span class="normal"> 92</span>
|
||
<span class="normal"> 93</span>
|
||
<span class="normal"> 94</span>
|
||
<span class="normal"> 95</span>
|
||
<span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span>
|
||
<span class="normal">101</span>
|
||
<span class="normal">102</span>
|
||
<span class="normal">103</span>
|
||
<span class="normal">104</span>
|
||
<span class="normal">105</span>
|
||
<span class="normal">106</span>
|
||
<span class="normal">107</span>
|
||
<span class="normal">108</span>
|
||
<span class="normal">109</span>
|
||
<span class="normal">110</span>
|
||
<span class="normal">111</span>
|
||
<span class="normal">112</span>
|
||
<span class="normal">113</span>
|
||
<span class="normal">114</span>
|
||
<span class="normal">115</span>
|
||
<span class="normal">116</span>
|
||
<span class="normal">117</span>
|
||
<span class="normal">118</span>
|
||
<span class="normal">119</span>
|
||
<span class="normal">120</span>
|
||
<span class="normal">121</span>
|
||
<span class="normal">122</span>
|
||
<span class="normal">123</span>
|
||
<span class="normal">124</span>
|
||
<span class="normal">125</span>
|
||
<span class="normal">126</span>
|
||
<span class="normal">127</span>
|
||
<span class="normal">128</span>
|
||
<span class="normal">129</span>
|
||
<span class="normal">130</span>
|
||
<span class="normal">131</span>
|
||
<span class="normal">132</span>
|
||
<span class="normal">133</span>
|
||
<span class="normal">134</span>
|
||
<span class="normal">135</span>
|
||
<span class="normal">136</span>
|
||
<span class="normal">137</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">calendar_link</span><span class="p">(</span><span class="n">update</span><span class="p">:</span> <span class="n">Update</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="o">.</span><span class="n">DEFAULT_TYPE</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Handle /calendar_link: send personal ICS URL (private chat only; user must be in allowlist)."""</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">get_lang</span><span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_chat</span> <span class="ow">and</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_chat</span><span class="o">.</span><span class="n">type</span> <span class="o">!=</span> <span class="s2">"private"</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"calendar_link.private_only"</span><span class="p">))</span>
|
||
<span class="k">return</span>
|
||
<span class="n">telegram_user_id</span> <span class="o">=</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">id</span>
|
||
<span class="n">username</span> <span class="o">=</span> <span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">username</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="n">full_name</span> <span class="o">=</span> <span class="n">build_full_name</span><span class="p">(</span>
|
||
<span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">first_name</span><span class="p">,</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">last_name</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">do_calendar_link</span><span class="p">()</span> <span class="o">-></span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">,</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">]:</span>
|
||
<span class="k">with</span> <span class="n">session_scope</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">DATABASE_URL</span><span class="p">)</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">get_or_create_user</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">,</span>
|
||
<span class="n">telegram_user_id</span><span class="o">=</span><span class="n">telegram_user_id</span><span class="p">,</span>
|
||
<span class="n">full_name</span><span class="o">=</span><span class="n">full_name</span><span class="p">,</span>
|
||
<span class="n">username</span><span class="o">=</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">username</span><span class="p">,</span>
|
||
<span class="n">first_name</span><span class="o">=</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">first_name</span><span class="p">,</span>
|
||
<span class="n">last_name</span><span class="o">=</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">last_name</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">config</span><span class="o">.</span><span class="n">can_access_miniapp</span><span class="p">(</span>
|
||
<span class="n">username</span>
|
||
<span class="p">)</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">config</span><span class="o">.</span><span class="n">can_access_miniapp_by_phone</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">phone</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="s2">"denied"</span><span class="p">)</span>
|
||
<span class="n">token</span> <span class="o">=</span> <span class="n">create_calendar_token</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
|
||
<span class="n">base</span> <span class="o">=</span> <span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">MINI_APP_BASE_URL</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">rstrip</span><span class="p">(</span><span class="s2">"/"</span><span class="p">)</span>
|
||
<span class="n">url</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">base</span><span class="si">}</span><span class="s2">/api/calendar/ical/</span><span class="si">{</span><span class="n">token</span><span class="si">}</span><span class="s2">.ics"</span> <span class="k">if</span> <span class="n">base</span> <span class="k">else</span> <span class="kc">None</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
|
||
<span class="n">result_url</span><span class="p">,</span> <span class="n">error</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_running_loop</span><span class="p">()</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span>
|
||
<span class="kc">None</span><span class="p">,</span> <span class="n">do_calendar_link</span>
|
||
<span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">error</span> <span class="o">==</span> <span class="s2">"denied"</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"calendar_link.access_denied"</span><span class="p">))</span>
|
||
<span class="k">return</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">result_url</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"calendar_link.error"</span><span class="p">))</span>
|
||
<span class="k">return</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span>
|
||
<span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"calendar_link.success"</span><span class="p">,</span> <span class="n">url</span><span class="o">=</span><span class="n">result_url</span><span class="p">)</span>
|
||
<span class="o">+</span> <span class="s2">"</span><span class="se">\n\n</span><span class="s2">"</span>
|
||
<span class="o">+</span> <span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"calendar_link.help_hint"</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.commands.help_cmd" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">help_cmd</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-async"><code>async</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Handle /help: send list of commands (admins see import_duty_schedule).</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/commands.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">140</span>
|
||
<span class="normal">141</span>
|
||
<span class="normal">142</span>
|
||
<span class="normal">143</span>
|
||
<span class="normal">144</span>
|
||
<span class="normal">145</span>
|
||
<span class="normal">146</span>
|
||
<span class="normal">147</span>
|
||
<span class="normal">148</span>
|
||
<span class="normal">149</span>
|
||
<span class="normal">150</span>
|
||
<span class="normal">151</span>
|
||
<span class="normal">152</span>
|
||
<span class="normal">153</span>
|
||
<span class="normal">154</span>
|
||
<span class="normal">155</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">help_cmd</span><span class="p">(</span><span class="n">update</span><span class="p">:</span> <span class="n">Update</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="o">.</span><span class="n">DEFAULT_TYPE</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Handle /help: send list of commands (admins see import_duty_schedule)."""</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">get_lang</span><span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">)</span>
|
||
<span class="n">lines</span> <span class="o">=</span> <span class="p">[</span>
|
||
<span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"help.title"</span><span class="p">),</span>
|
||
<span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"help.start"</span><span class="p">),</span>
|
||
<span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"help.help"</span><span class="p">),</span>
|
||
<span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"help.set_phone"</span><span class="p">),</span>
|
||
<span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"help.calendar_link"</span><span class="p">),</span>
|
||
<span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"help.pin_duty"</span><span class="p">),</span>
|
||
<span class="p">]</span>
|
||
<span class="k">if</span> <span class="n">config</span><span class="o">.</span><span class="n">is_admin</span><span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">username</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">):</span>
|
||
<span class="n">lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"help.import_schedule"</span><span class="p">))</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">lines</span><span class="p">))</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.commands.set_phone" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">set_phone</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-async"><code>async</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Handle /set_phone [number]: set or clear phone (private chat only).</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/commands.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span>
|
||
<span class="normal">54</span>
|
||
<span class="normal">55</span>
|
||
<span class="normal">56</span>
|
||
<span class="normal">57</span>
|
||
<span class="normal">58</span>
|
||
<span class="normal">59</span>
|
||
<span class="normal">60</span>
|
||
<span class="normal">61</span>
|
||
<span class="normal">62</span>
|
||
<span class="normal">63</span>
|
||
<span class="normal">64</span>
|
||
<span class="normal">65</span>
|
||
<span class="normal">66</span>
|
||
<span class="normal">67</span>
|
||
<span class="normal">68</span>
|
||
<span class="normal">69</span>
|
||
<span class="normal">70</span>
|
||
<span class="normal">71</span>
|
||
<span class="normal">72</span>
|
||
<span class="normal">73</span>
|
||
<span class="normal">74</span>
|
||
<span class="normal">75</span>
|
||
<span class="normal">76</span>
|
||
<span class="normal">77</span>
|
||
<span class="normal">78</span>
|
||
<span class="normal">79</span>
|
||
<span class="normal">80</span>
|
||
<span class="normal">81</span>
|
||
<span class="normal">82</span>
|
||
<span class="normal">83</span>
|
||
<span class="normal">84</span>
|
||
<span class="normal">85</span>
|
||
<span class="normal">86</span>
|
||
<span class="normal">87</span>
|
||
<span class="normal">88</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">set_phone</span><span class="p">(</span><span class="n">update</span><span class="p">:</span> <span class="n">Update</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="o">.</span><span class="n">DEFAULT_TYPE</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Handle /set_phone [number]: set or clear phone (private chat only)."""</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">get_lang</span><span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_chat</span> <span class="ow">and</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_chat</span><span class="o">.</span><span class="n">type</span> <span class="o">!=</span> <span class="s2">"private"</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"set_phone.private_only"</span><span class="p">))</span>
|
||
<span class="k">return</span>
|
||
<span class="n">args</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="n">args</span> <span class="ow">or</span> <span class="p">[]</span>
|
||
<span class="n">phone</span> <span class="o">=</span> <span class="s2">" "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">args</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">if</span> <span class="n">args</span> <span class="k">else</span> <span class="kc">None</span>
|
||
<span class="n">telegram_user_id</span> <span class="o">=</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">id</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">do_set_phone</span><span class="p">()</span> <span class="o">-></span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">with</span> <span class="n">session_scope</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">DATABASE_URL</span><span class="p">)</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||
<span class="n">full_name</span> <span class="o">=</span> <span class="n">build_full_name</span><span class="p">(</span>
|
||
<span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">first_name</span><span class="p">,</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">last_name</span>
|
||
<span class="p">)</span>
|
||
<span class="n">get_or_create_user</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">,</span>
|
||
<span class="n">telegram_user_id</span><span class="o">=</span><span class="n">telegram_user_id</span><span class="p">,</span>
|
||
<span class="n">full_name</span><span class="o">=</span><span class="n">full_name</span><span class="p">,</span>
|
||
<span class="n">username</span><span class="o">=</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">username</span><span class="p">,</span>
|
||
<span class="n">first_name</span><span class="o">=</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">first_name</span><span class="p">,</span>
|
||
<span class="n">last_name</span><span class="o">=</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">last_name</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">set_user_phone</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">telegram_user_id</span><span class="p">,</span> <span class="n">phone</span> <span class="ow">or</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">user</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="s2">"error"</span>
|
||
<span class="k">if</span> <span class="n">phone</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="s2">"saved"</span>
|
||
<span class="k">return</span> <span class="s2">"cleared"</span>
|
||
|
||
<span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_running_loop</span><span class="p">()</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">do_set_phone</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">result</span> <span class="o">==</span> <span class="s2">"error"</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"set_phone.error"</span><span class="p">))</span>
|
||
<span class="k">elif</span> <span class="n">result</span> <span class="o">==</span> <span class="s2">"saved"</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"set_phone.saved"</span><span class="p">,</span> <span class="n">phone</span><span class="o">=</span><span class="n">phone</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">))</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"set_phone.cleared"</span><span class="p">))</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.commands.start" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">start</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-async"><code>async</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Handle /start: register user in DB and send greeting.</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/commands.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">19</span>
|
||
<span class="normal">20</span>
|
||
<span class="normal">21</span>
|
||
<span class="normal">22</span>
|
||
<span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span>
|
||
<span class="normal">31</span>
|
||
<span class="normal">32</span>
|
||
<span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span>
|
||
<span class="normal">38</span>
|
||
<span class="normal">39</span>
|
||
<span class="normal">40</span>
|
||
<span class="normal">41</span>
|
||
<span class="normal">42</span>
|
||
<span class="normal">43</span>
|
||
<span class="normal">44</span>
|
||
<span class="normal">45</span>
|
||
<span class="normal">46</span>
|
||
<span class="normal">47</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">start</span><span class="p">(</span><span class="n">update</span><span class="p">:</span> <span class="n">Update</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="o">.</span><span class="n">DEFAULT_TYPE</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Handle /start: register user in DB and send greeting."""</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="n">full_name</span> <span class="o">=</span> <span class="n">build_full_name</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">first_name</span><span class="p">,</span> <span class="n">user</span><span class="o">.</span><span class="n">last_name</span><span class="p">)</span>
|
||
<span class="n">telegram_user_id</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
|
||
<span class="n">username</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">username</span>
|
||
<span class="n">first_name</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">first_name</span>
|
||
<span class="n">last_name</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">last_name</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">do_get_or_create</span><span class="p">()</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">with</span> <span class="n">session_scope</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">DATABASE_URL</span><span class="p">)</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||
<span class="n">get_or_create_user</span><span class="p">(</span>
|
||
<span class="n">session</span><span class="p">,</span>
|
||
<span class="n">telegram_user_id</span><span class="o">=</span><span class="n">telegram_user_id</span><span class="p">,</span>
|
||
<span class="n">full_name</span><span class="o">=</span><span class="n">full_name</span><span class="p">,</span>
|
||
<span class="n">username</span><span class="o">=</span><span class="n">username</span><span class="p">,</span>
|
||
<span class="n">first_name</span><span class="o">=</span><span class="n">first_name</span><span class="p">,</span>
|
||
<span class="n">last_name</span><span class="o">=</span><span class="n">last_name</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_running_loop</span><span class="p">()</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">do_get_or_create</span><span class="p">)</span>
|
||
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">get_lang</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"start.greeting"</span><span class="p">)</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.handlers.import_duty_schedule" class="doc doc-heading">
|
||
<code>duty_teller.handlers.import_duty_schedule</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Import duty-schedule: /import_duty_schedule (admin only). Two steps: handover time -> JSON file.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.import_duty_schedule.handle_duty_schedule_document" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">handle_duty_schedule_document</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-async"><code>async</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Handle uploaded JSON file: parse duty-schedule and run import.</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/import_duty_schedule.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 56</span>
|
||
<span class="normal"> 57</span>
|
||
<span class="normal"> 58</span>
|
||
<span class="normal"> 59</span>
|
||
<span class="normal"> 60</span>
|
||
<span class="normal"> 61</span>
|
||
<span class="normal"> 62</span>
|
||
<span class="normal"> 63</span>
|
||
<span class="normal"> 64</span>
|
||
<span class="normal"> 65</span>
|
||
<span class="normal"> 66</span>
|
||
<span class="normal"> 67</span>
|
||
<span class="normal"> 68</span>
|
||
<span class="normal"> 69</span>
|
||
<span class="normal"> 70</span>
|
||
<span class="normal"> 71</span>
|
||
<span class="normal"> 72</span>
|
||
<span class="normal"> 73</span>
|
||
<span class="normal"> 74</span>
|
||
<span class="normal"> 75</span>
|
||
<span class="normal"> 76</span>
|
||
<span class="normal"> 77</span>
|
||
<span class="normal"> 78</span>
|
||
<span class="normal"> 79</span>
|
||
<span class="normal"> 80</span>
|
||
<span class="normal"> 81</span>
|
||
<span class="normal"> 82</span>
|
||
<span class="normal"> 83</span>
|
||
<span class="normal"> 84</span>
|
||
<span class="normal"> 85</span>
|
||
<span class="normal"> 86</span>
|
||
<span class="normal"> 87</span>
|
||
<span class="normal"> 88</span>
|
||
<span class="normal"> 89</span>
|
||
<span class="normal"> 90</span>
|
||
<span class="normal"> 91</span>
|
||
<span class="normal"> 92</span>
|
||
<span class="normal"> 93</span>
|
||
<span class="normal"> 94</span>
|
||
<span class="normal"> 95</span>
|
||
<span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span>
|
||
<span class="normal">101</span>
|
||
<span class="normal">102</span>
|
||
<span class="normal">103</span>
|
||
<span class="normal">104</span>
|
||
<span class="normal">105</span>
|
||
<span class="normal">106</span>
|
||
<span class="normal">107</span>
|
||
<span class="normal">108</span>
|
||
<span class="normal">109</span>
|
||
<span class="normal">110</span>
|
||
<span class="normal">111</span>
|
||
<span class="normal">112</span>
|
||
<span class="normal">113</span>
|
||
<span class="normal">114</span>
|
||
<span class="normal">115</span>
|
||
<span class="normal">116</span>
|
||
<span class="normal">117</span>
|
||
<span class="normal">118</span>
|
||
<span class="normal">119</span>
|
||
<span class="normal">120</span>
|
||
<span class="normal">121</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">handle_duty_schedule_document</span><span class="p">(</span>
|
||
<span class="n">update</span><span class="p">:</span> <span class="n">Update</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="o">.</span><span class="n">DEFAULT_TYPE</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Handle uploaded JSON file: parse duty-schedule and run import."""</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">document</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">context</span><span class="o">.</span><span class="n">user_data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"awaiting_duty_schedule_file"</span><span class="p">):</span>
|
||
<span class="k">return</span>
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">get_lang</span><span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">)</span>
|
||
<span class="n">handover</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="n">user_data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"handover_utc_time"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">handover</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">config</span><span class="o">.</span><span class="n">is_admin</span><span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">username</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">):</span>
|
||
<span class="k">return</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">document</span><span class="o">.</span><span class="n">file_name</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s2">".json"</span><span class="p">):</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"import.need_json"</span><span class="p">))</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="n">hour_utc</span><span class="p">,</span> <span class="n">minute_utc</span> <span class="o">=</span> <span class="n">handover</span>
|
||
<span class="n">file_id</span> <span class="o">=</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">document</span><span class="o">.</span><span class="n">file_id</span>
|
||
|
||
<span class="n">file</span> <span class="o">=</span> <span class="k">await</span> <span class="n">context</span><span class="o">.</span><span class="n">bot</span><span class="o">.</span><span class="n">get_file</span><span class="p">(</span><span class="n">file_id</span><span class="p">)</span>
|
||
<span class="n">raw</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">(</span><span class="k">await</span> <span class="n">file</span><span class="o">.</span><span class="n">download_as_bytearray</span><span class="p">())</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">result</span> <span class="o">=</span> <span class="n">parse_duty_schedule</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="n">DutyScheduleParseError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||
<span class="n">context</span><span class="o">.</span><span class="n">user_data</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">"awaiting_duty_schedule_file"</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="n">context</span><span class="o">.</span><span class="n">user_data</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">"handover_utc_time"</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"import.parse_error"</span><span class="p">,</span> <span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">run_import_with_scope</span><span class="p">():</span>
|
||
<span class="k">with</span> <span class="n">session_scope</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">DATABASE_URL</span><span class="p">)</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">run_import</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">result</span><span class="p">,</span> <span class="n">hour_utc</span><span class="p">,</span> <span class="n">minute_utc</span><span class="p">)</span>
|
||
|
||
<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_running_loop</span><span class="p">()</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">num_users</span><span class="p">,</span> <span class="n">num_duty</span><span class="p">,</span> <span class="n">num_unavailable</span><span class="p">,</span> <span class="n">num_vacation</span> <span class="o">=</span> <span class="k">await</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span>
|
||
<span class="kc">None</span><span class="p">,</span> <span class="n">run_import_with_scope</span>
|
||
<span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"import.import_error"</span><span class="p">,</span> <span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">total</span> <span class="o">=</span> <span class="n">num_duty</span> <span class="o">+</span> <span class="n">num_unavailable</span> <span class="o">+</span> <span class="n">num_vacation</span>
|
||
<span class="n">unavailable_suffix</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"import.done_unavailable"</span><span class="p">,</span> <span class="n">count</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">num_unavailable</span><span class="p">))</span>
|
||
<span class="k">if</span> <span class="n">num_unavailable</span>
|
||
<span class="k">else</span> <span class="s2">""</span>
|
||
<span class="p">)</span>
|
||
<span class="n">vacation_suffix</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"import.done_vacation"</span><span class="p">,</span> <span class="n">count</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">num_vacation</span><span class="p">))</span>
|
||
<span class="k">if</span> <span class="n">num_vacation</span>
|
||
<span class="k">else</span> <span class="s2">""</span>
|
||
<span class="p">)</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span>
|
||
<span class="n">t</span><span class="p">(</span>
|
||
<span class="n">lang</span><span class="p">,</span>
|
||
<span class="s2">"import.done"</span><span class="p">,</span>
|
||
<span class="n">users</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">num_users</span><span class="p">),</span>
|
||
<span class="n">duties</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">num_duty</span><span class="p">),</span>
|
||
<span class="n">unavailable</span><span class="o">=</span><span class="n">unavailable_suffix</span><span class="p">,</span>
|
||
<span class="n">vacation</span><span class="o">=</span><span class="n">vacation_suffix</span><span class="p">,</span>
|
||
<span class="n">total</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">total</span><span class="p">),</span>
|
||
<span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="n">context</span><span class="o">.</span><span class="n">user_data</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">"awaiting_duty_schedule_file"</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="n">context</span><span class="o">.</span><span class="n">user_data</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">"handover_utc_time"</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.import_duty_schedule.handle_handover_time_text" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">handle_handover_time_text</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-async"><code>async</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Handle text message when awaiting handover time (e.g. 09:00 Europe/Moscow).</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/import_duty_schedule.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span>
|
||
<span class="normal">36</span>
|
||
<span class="normal">37</span>
|
||
<span class="normal">38</span>
|
||
<span class="normal">39</span>
|
||
<span class="normal">40</span>
|
||
<span class="normal">41</span>
|
||
<span class="normal">42</span>
|
||
<span class="normal">43</span>
|
||
<span class="normal">44</span>
|
||
<span class="normal">45</span>
|
||
<span class="normal">46</span>
|
||
<span class="normal">47</span>
|
||
<span class="normal">48</span>
|
||
<span class="normal">49</span>
|
||
<span class="normal">50</span>
|
||
<span class="normal">51</span>
|
||
<span class="normal">52</span>
|
||
<span class="normal">53</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">handle_handover_time_text</span><span class="p">(</span>
|
||
<span class="n">update</span><span class="p">:</span> <span class="n">Update</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="o">.</span><span class="n">DEFAULT_TYPE</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Handle text message when awaiting handover time (e.g. 09:00 Europe/Moscow)."""</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">text</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">context</span><span class="o">.</span><span class="n">user_data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"awaiting_handover_time"</span><span class="p">):</span>
|
||
<span class="k">return</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">config</span><span class="o">.</span><span class="n">is_admin</span><span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">username</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">):</span>
|
||
<span class="k">return</span>
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">get_lang</span><span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">)</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="n">parsed</span> <span class="o">=</span> <span class="n">parse_handover_time</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">parsed</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"import.parse_time_error"</span><span class="p">))</span>
|
||
<span class="k">return</span>
|
||
<span class="n">hour_utc</span><span class="p">,</span> <span class="n">minute_utc</span> <span class="o">=</span> <span class="n">parsed</span>
|
||
<span class="n">context</span><span class="o">.</span><span class="n">user_data</span><span class="p">[</span><span class="s2">"handover_utc_time"</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">hour_utc</span><span class="p">,</span> <span class="n">minute_utc</span><span class="p">)</span>
|
||
<span class="n">context</span><span class="o">.</span><span class="n">user_data</span><span class="p">[</span><span class="s2">"awaiting_handover_time"</span><span class="p">]</span> <span class="o">=</span> <span class="kc">False</span>
|
||
<span class="n">context</span><span class="o">.</span><span class="n">user_data</span><span class="p">[</span><span class="s2">"awaiting_duty_schedule_file"</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"import.send_json"</span><span class="p">))</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.import_duty_schedule.import_duty_schedule_cmd" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">import_duty_schedule_cmd</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-async"><code>async</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Handle /import_duty_schedule: start two-step import (admin only); asks for handover time.</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/import_duty_schedule.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">19</span>
|
||
<span class="normal">20</span>
|
||
<span class="normal">21</span>
|
||
<span class="normal">22</span>
|
||
<span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span>
|
||
<span class="normal">30</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">import_duty_schedule_cmd</span><span class="p">(</span>
|
||
<span class="n">update</span><span class="p">:</span> <span class="n">Update</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="o">.</span><span class="n">DEFAULT_TYPE</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Handle /import_duty_schedule: start two-step import (admin only); asks for handover time."""</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">get_lang</span><span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">config</span><span class="o">.</span><span class="n">is_admin</span><span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="o">.</span><span class="n">username</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">):</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"import.admin_only"</span><span class="p">))</span>
|
||
<span class="k">return</span>
|
||
<span class="n">context</span><span class="o">.</span><span class="n">user_data</span><span class="p">[</span><span class="s2">"awaiting_handover_time"</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"import.handover_format"</span><span class="p">))</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.handlers.group_duty_pin" class="doc doc-heading">
|
||
<code>duty_teller.handlers.group_duty_pin</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Pinned duty message in groups: handle bot add/remove, schedule updates at shift end.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.group_duty_pin.my_chat_member_handler" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">my_chat_member_handler</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-async"><code>async</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Handle bot added to or removed from group: send/pin duty message or delete pin record.</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/group_duty_pin.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">118</span>
|
||
<span class="normal">119</span>
|
||
<span class="normal">120</span>
|
||
<span class="normal">121</span>
|
||
<span class="normal">122</span>
|
||
<span class="normal">123</span>
|
||
<span class="normal">124</span>
|
||
<span class="normal">125</span>
|
||
<span class="normal">126</span>
|
||
<span class="normal">127</span>
|
||
<span class="normal">128</span>
|
||
<span class="normal">129</span>
|
||
<span class="normal">130</span>
|
||
<span class="normal">131</span>
|
||
<span class="normal">132</span>
|
||
<span class="normal">133</span>
|
||
<span class="normal">134</span>
|
||
<span class="normal">135</span>
|
||
<span class="normal">136</span>
|
||
<span class="normal">137</span>
|
||
<span class="normal">138</span>
|
||
<span class="normal">139</span>
|
||
<span class="normal">140</span>
|
||
<span class="normal">141</span>
|
||
<span class="normal">142</span>
|
||
<span class="normal">143</span>
|
||
<span class="normal">144</span>
|
||
<span class="normal">145</span>
|
||
<span class="normal">146</span>
|
||
<span class="normal">147</span>
|
||
<span class="normal">148</span>
|
||
<span class="normal">149</span>
|
||
<span class="normal">150</span>
|
||
<span class="normal">151</span>
|
||
<span class="normal">152</span>
|
||
<span class="normal">153</span>
|
||
<span class="normal">154</span>
|
||
<span class="normal">155</span>
|
||
<span class="normal">156</span>
|
||
<span class="normal">157</span>
|
||
<span class="normal">158</span>
|
||
<span class="normal">159</span>
|
||
<span class="normal">160</span>
|
||
<span class="normal">161</span>
|
||
<span class="normal">162</span>
|
||
<span class="normal">163</span>
|
||
<span class="normal">164</span>
|
||
<span class="normal">165</span>
|
||
<span class="normal">166</span>
|
||
<span class="normal">167</span>
|
||
<span class="normal">168</span>
|
||
<span class="normal">169</span>
|
||
<span class="normal">170</span>
|
||
<span class="normal">171</span>
|
||
<span class="normal">172</span>
|
||
<span class="normal">173</span>
|
||
<span class="normal">174</span>
|
||
<span class="normal">175</span>
|
||
<span class="normal">176</span>
|
||
<span class="normal">177</span>
|
||
<span class="normal">178</span>
|
||
<span class="normal">179</span>
|
||
<span class="normal">180</span>
|
||
<span class="normal">181</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">my_chat_member_handler</span><span class="p">(</span>
|
||
<span class="n">update</span><span class="p">:</span> <span class="n">Update</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="o">.</span><span class="n">DEFAULT_TYPE</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Handle bot added to or removed from group: send/pin duty message or delete pin record."""</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">my_chat_member</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="n">old</span> <span class="o">=</span> <span class="n">update</span><span class="o">.</span><span class="n">my_chat_member</span><span class="o">.</span><span class="n">old_chat_member</span>
|
||
<span class="n">new</span> <span class="o">=</span> <span class="n">update</span><span class="o">.</span><span class="n">my_chat_member</span><span class="o">.</span><span class="n">new_chat_member</span>
|
||
<span class="n">chat</span> <span class="o">=</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_chat</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">chat</span> <span class="ow">or</span> <span class="n">chat</span><span class="o">.</span><span class="n">type</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">"group"</span><span class="p">,</span> <span class="s2">"supergroup"</span><span class="p">):</span>
|
||
<span class="k">return</span>
|
||
<span class="k">if</span> <span class="n">new</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">id</span> <span class="o">!=</span> <span class="n">context</span><span class="o">.</span><span class="n">bot</span><span class="o">.</span><span class="n">id</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="n">chat_id</span> <span class="o">=</span> <span class="n">chat</span><span class="o">.</span><span class="n">id</span>
|
||
|
||
<span class="k">if</span> <span class="n">new</span><span class="o">.</span><span class="n">status</span> <span class="ow">in</span> <span class="p">(</span>
|
||
<span class="n">ChatMemberStatus</span><span class="o">.</span><span class="n">MEMBER</span><span class="p">,</span>
|
||
<span class="n">ChatMemberStatus</span><span class="o">.</span><span class="n">ADMINISTRATOR</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="ow">and</span> <span class="n">old</span><span class="o">.</span><span class="n">status</span> <span class="ow">in</span> <span class="p">(</span>
|
||
<span class="n">ChatMemberStatus</span><span class="o">.</span><span class="n">LEFT</span><span class="p">,</span>
|
||
<span class="n">ChatMemberStatus</span><span class="o">.</span><span class="n">BANNED</span><span class="p">,</span>
|
||
<span class="p">):</span>
|
||
<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_running_loop</span><span class="p">()</span>
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">get_lang</span><span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">)</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="k">await</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span>
|
||
<span class="kc">None</span><span class="p">,</span> <span class="k">lambda</span><span class="p">:</span> <span class="n">_get_duty_message_text_sync</span><span class="p">(</span><span class="n">lang</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">msg</span> <span class="o">=</span> <span class="k">await</span> <span class="n">context</span><span class="o">.</span><span class="n">bot</span><span class="o">.</span><span class="n">send_message</span><span class="p">(</span><span class="n">chat_id</span><span class="o">=</span><span class="n">chat_id</span><span class="p">,</span> <span class="n">text</span><span class="o">=</span><span class="n">text</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="p">(</span><span class="n">BadRequest</span><span class="p">,</span> <span class="n">Forbidden</span><span class="p">)</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||
<span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s2">"Failed to send duty message in chat_id=</span><span class="si">%s</span><span class="s2">: </span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
<span class="n">pinned</span> <span class="o">=</span> <span class="kc">False</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">context</span><span class="o">.</span><span class="n">bot</span><span class="o">.</span><span class="n">pin_chat_message</span><span class="p">(</span>
|
||
<span class="n">chat_id</span><span class="o">=</span><span class="n">chat_id</span><span class="p">,</span>
|
||
<span class="n">message_id</span><span class="o">=</span><span class="n">msg</span><span class="o">.</span><span class="n">message_id</span><span class="p">,</span>
|
||
<span class="n">disable_notification</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="n">pinned</span> <span class="o">=</span> <span class="kc">True</span>
|
||
<span class="k">except</span> <span class="p">(</span><span class="n">BadRequest</span><span class="p">,</span> <span class="n">Forbidden</span><span class="p">)</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||
<span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s2">"Failed to pin message in chat_id=</span><span class="si">%s</span><span class="s2">: </span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span>
|
||
<span class="k">await</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">_sync_save_pin</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">msg</span><span class="o">.</span><span class="n">message_id</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">pinned</span><span class="p">:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">context</span><span class="o">.</span><span class="n">bot</span><span class="o">.</span><span class="n">send_message</span><span class="p">(</span>
|
||
<span class="n">chat_id</span><span class="o">=</span><span class="n">chat_id</span><span class="p">,</span>
|
||
<span class="n">text</span><span class="o">=</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"pin_duty.could_not_pin_make_admin"</span><span class="p">),</span>
|
||
<span class="p">)</span>
|
||
<span class="k">except</span> <span class="p">(</span><span class="n">BadRequest</span><span class="p">,</span> <span class="n">Forbidden</span><span class="p">):</span>
|
||
<span class="k">pass</span>
|
||
<span class="n">next_end</span> <span class="o">=</span> <span class="k">await</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">_get_next_shift_end_sync</span><span class="p">)</span>
|
||
<span class="k">await</span> <span class="n">_schedule_next_update</span><span class="p">(</span><span class="n">context</span><span class="o">.</span><span class="n">application</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">next_end</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="k">if</span> <span class="n">new</span><span class="o">.</span><span class="n">status</span> <span class="ow">in</span> <span class="p">(</span><span class="n">ChatMemberStatus</span><span class="o">.</span><span class="n">LEFT</span><span class="p">,</span> <span class="n">ChatMemberStatus</span><span class="o">.</span><span class="n">BANNED</span><span class="p">):</span>
|
||
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_running_loop</span><span class="p">()</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span>
|
||
<span class="kc">None</span><span class="p">,</span> <span class="n">_sync_delete_pin</span><span class="p">,</span> <span class="n">chat_id</span>
|
||
<span class="p">)</span>
|
||
<span class="n">name</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">JOB_NAME_PREFIX</span><span class="si">}{</span><span class="n">chat_id</span><span class="si">}</span><span class="s2">"</span>
|
||
<span class="k">if</span> <span class="n">context</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">job_queue</span><span class="p">:</span>
|
||
<span class="k">for</span> <span class="n">job</span> <span class="ow">in</span> <span class="n">context</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">job_queue</span><span class="o">.</span><span class="n">get_jobs_by_name</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
|
||
<span class="n">job</span><span class="o">.</span><span class="n">schedule_removal</span><span class="p">()</span>
|
||
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">"Bot left chat_id=</span><span class="si">%s</span><span class="s2">, removed pin record and jobs"</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.group_duty_pin.pin_duty_cmd" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">pin_duty_cmd</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-async"><code>async</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Handle /pin_duty: pin the current duty message in the group (reply to bot's message).</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/group_duty_pin.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">199</span>
|
||
<span class="normal">200</span>
|
||
<span class="normal">201</span>
|
||
<span class="normal">202</span>
|
||
<span class="normal">203</span>
|
||
<span class="normal">204</span>
|
||
<span class="normal">205</span>
|
||
<span class="normal">206</span>
|
||
<span class="normal">207</span>
|
||
<span class="normal">208</span>
|
||
<span class="normal">209</span>
|
||
<span class="normal">210</span>
|
||
<span class="normal">211</span>
|
||
<span class="normal">212</span>
|
||
<span class="normal">213</span>
|
||
<span class="normal">214</span>
|
||
<span class="normal">215</span>
|
||
<span class="normal">216</span>
|
||
<span class="normal">217</span>
|
||
<span class="normal">218</span>
|
||
<span class="normal">219</span>
|
||
<span class="normal">220</span>
|
||
<span class="normal">221</span>
|
||
<span class="normal">222</span>
|
||
<span class="normal">223</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">pin_duty_cmd</span><span class="p">(</span><span class="n">update</span><span class="p">:</span> <span class="n">Update</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="o">.</span><span class="n">DEFAULT_TYPE</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Handle /pin_duty: pin the current duty message in the group (reply to bot's message)."""</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_chat</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="n">chat</span> <span class="o">=</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_chat</span>
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">get_lang</span><span class="p">(</span><span class="n">update</span><span class="o">.</span><span class="n">effective_user</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">chat</span><span class="o">.</span><span class="n">type</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">"group"</span><span class="p">,</span> <span class="s2">"supergroup"</span><span class="p">):</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"pin_duty.group_only"</span><span class="p">))</span>
|
||
<span class="k">return</span>
|
||
<span class="n">chat_id</span> <span class="o">=</span> <span class="n">chat</span><span class="o">.</span><span class="n">id</span>
|
||
<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_running_loop</span><span class="p">()</span>
|
||
<span class="n">message_id</span> <span class="o">=</span> <span class="k">await</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">_sync_get_message_id</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">message_id</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"pin_duty.no_message"</span><span class="p">))</span>
|
||
<span class="k">return</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">context</span><span class="o">.</span><span class="n">bot</span><span class="o">.</span><span class="n">pin_chat_message</span><span class="p">(</span>
|
||
<span class="n">chat_id</span><span class="o">=</span><span class="n">chat_id</span><span class="p">,</span>
|
||
<span class="n">message_id</span><span class="o">=</span><span class="n">message_id</span><span class="p">,</span>
|
||
<span class="n">disable_notification</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"pin_duty.pinned"</span><span class="p">))</span>
|
||
<span class="k">except</span> <span class="p">(</span><span class="n">BadRequest</span><span class="p">,</span> <span class="n">Forbidden</span><span class="p">)</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||
<span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s2">"pin_duty failed chat_id=</span><span class="si">%s</span><span class="s2">: </span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"pin_duty.failed"</span><span class="p">))</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.group_duty_pin.restore_group_pin_jobs" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">restore_group_pin_jobs</span><span class="p">(</span><span class="n">application</span><span class="p">)</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-async"><code>async</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Restore scheduled pin-update jobs for all chats that have a pinned message (on startup).</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/group_duty_pin.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">189</span>
|
||
<span class="normal">190</span>
|
||
<span class="normal">191</span>
|
||
<span class="normal">192</span>
|
||
<span class="normal">193</span>
|
||
<span class="normal">194</span>
|
||
<span class="normal">195</span>
|
||
<span class="normal">196</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">restore_group_pin_jobs</span><span class="p">(</span><span class="n">application</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Restore scheduled pin-update jobs for all chats that have a pinned message (on startup)."""</span>
|
||
<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_running_loop</span><span class="p">()</span>
|
||
<span class="n">chat_ids</span> <span class="o">=</span> <span class="k">await</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">_get_all_pin_chat_ids_sync</span><span class="p">)</span>
|
||
<span class="k">for</span> <span class="n">chat_id</span> <span class="ow">in</span> <span class="n">chat_ids</span><span class="p">:</span>
|
||
<span class="n">next_end</span> <span class="o">=</span> <span class="k">await</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">_get_next_shift_end_sync</span><span class="p">)</span>
|
||
<span class="k">await</span> <span class="n">_schedule_next_update</span><span class="p">(</span><span class="n">application</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">next_end</span><span class="p">)</span>
|
||
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">"Restored </span><span class="si">%s</span><span class="s2"> group pin jobs"</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">chat_ids</span><span class="p">))</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.group_duty_pin.update_group_pin" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">update_group_pin</span><span class="p">(</span><span class="n">context</span><span class="p">)</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-async"><code>async</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Job callback: refresh pinned duty message and schedule next update at shift end.</p>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/group_duty_pin.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 93</span>
|
||
<span class="normal"> 94</span>
|
||
<span class="normal"> 95</span>
|
||
<span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span>
|
||
<span class="normal">101</span>
|
||
<span class="normal">102</span>
|
||
<span class="normal">103</span>
|
||
<span class="normal">104</span>
|
||
<span class="normal">105</span>
|
||
<span class="normal">106</span>
|
||
<span class="normal">107</span>
|
||
<span class="normal">108</span>
|
||
<span class="normal">109</span>
|
||
<span class="normal">110</span>
|
||
<span class="normal">111</span>
|
||
<span class="normal">112</span>
|
||
<span class="normal">113</span>
|
||
<span class="normal">114</span>
|
||
<span class="normal">115</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_group_pin</span><span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="o">.</span><span class="n">DEFAULT_TYPE</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Job callback: refresh pinned duty message and schedule next update at shift end."""</span>
|
||
<span class="n">chat_id</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"chat_id"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">chat_id</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_running_loop</span><span class="p">()</span>
|
||
<span class="n">message_id</span> <span class="o">=</span> <span class="k">await</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">_sync_get_message_id</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">message_id</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">"No pin record for chat_id=</span><span class="si">%s</span><span class="s2">, skipping update"</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="k">await</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span>
|
||
<span class="kc">None</span><span class="p">,</span> <span class="k">lambda</span><span class="p">:</span> <span class="n">_get_duty_message_text_sync</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">DEFAULT_LANGUAGE</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">await</span> <span class="n">context</span><span class="o">.</span><span class="n">bot</span><span class="o">.</span><span class="n">edit_message_text</span><span class="p">(</span>
|
||
<span class="n">chat_id</span><span class="o">=</span><span class="n">chat_id</span><span class="p">,</span>
|
||
<span class="n">message_id</span><span class="o">=</span><span class="n">message_id</span><span class="p">,</span>
|
||
<span class="n">text</span><span class="o">=</span><span class="n">text</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="k">except</span> <span class="p">(</span><span class="n">BadRequest</span><span class="p">,</span> <span class="n">Forbidden</span><span class="p">)</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||
<span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s2">"Failed to edit pinned message chat_id=</span><span class="si">%s</span><span class="s2">: </span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span>
|
||
<span class="n">next_end</span> <span class="o">=</span> <span class="k">await</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_in_executor</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">_get_next_shift_end_sync</span><span class="p">)</span>
|
||
<span class="k">await</span> <span class="n">_schedule_next_update</span><span class="p">(</span><span class="n">context</span><span class="o">.</span><span class="n">application</span><span class="p">,</span> <span class="n">chat_id</span><span class="p">,</span> <span class="n">next_end</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.handlers.errors" class="doc doc-heading">
|
||
<code>duty_teller.handlers.errors</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Global error handler: log exception and notify user.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.handlers.errors.error_handler" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">error_handler</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span></code>
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-async"><code>async</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Global error handler: log exception and reply with generic message if possible.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>update</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="telegram.Update">Update</span> | None</code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Update that caused the error (may be None).</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>context</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="telegram.ext.ContextTypes.DEFAULT_TYPE">DEFAULT_TYPE</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>Callback context.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/handlers/errors.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">14</span>
|
||
<span class="normal">15</span>
|
||
<span class="normal">16</span>
|
||
<span class="normal">17</span>
|
||
<span class="normal">18</span>
|
||
<span class="normal">19</span>
|
||
<span class="normal">20</span>
|
||
<span class="normal">21</span>
|
||
<span class="normal">22</span>
|
||
<span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">error_handler</span><span class="p">(</span>
|
||
<span class="n">update</span><span class="p">:</span> <span class="n">Update</span> <span class="o">|</span> <span class="kc">None</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">ContextTypes</span><span class="o">.</span><span class="n">DEFAULT_TYPE</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Global error handler: log exception and reply with generic message if possible.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> update: Update that caused the error (may be None).</span>
|
||
<span class="sd"> context: Callback context.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">logger</span><span class="o">.</span><span class="n">exception</span><span class="p">(</span><span class="s2">"Exception while handling an update"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="n">Update</span><span class="p">)</span> <span class="ow">and</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_message</span><span class="p">:</span>
|
||
<span class="n">user</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="s2">"effective_user"</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="n">lang</span> <span class="o">=</span> <span class="n">get_lang</span><span class="p">(</span><span class="n">user</span><span class="p">)</span> <span class="k">if</span> <span class="n">user</span> <span class="k">else</span> <span class="n">config</span><span class="o">.</span><span class="n">DEFAULT_LANGUAGE</span>
|
||
<span class="k">await</span> <span class="n">update</span><span class="o">.</span><span class="n">effective_message</span><span class="o">.</span><span class="n">reply_text</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="s2">"errors.generic"</span><span class="p">))</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div><h2 id="importers">Importers</h2>
|
||
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.importers" class="doc doc-heading">
|
||
<code>duty_teller.importers</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Importers for duty data (e.g. duty-schedule JSON).</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-module">
|
||
|
||
|
||
|
||
<h2 id="duty_teller.importers.duty_schedule" class="doc doc-heading">
|
||
<code>duty_teller.importers.duty_schedule</code>
|
||
|
||
|
||
</h2>
|
||
|
||
<div class="doc doc-contents first">
|
||
|
||
<p>Parser for duty-schedule JSON format. No DB access.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.importers.duty_schedule.DutyScheduleEntry" class="doc doc-heading">
|
||
<code>DutyScheduleEntry</code>
|
||
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-dataclass"><code>dataclass</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
|
||
|
||
<p>One person's schedule: full_name and three lists of dates by event type.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/importers/duty_schedule.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">13</span>
|
||
<span class="normal">14</span>
|
||
<span class="normal">15</span>
|
||
<span class="normal">16</span>
|
||
<span class="normal">17</span>
|
||
<span class="normal">18</span>
|
||
<span class="normal">19</span>
|
||
<span class="normal">20</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">DutyScheduleEntry</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""One person's schedule: full_name and three lists of dates by event type."""</span>
|
||
|
||
<span class="n">full_name</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">duty_dates</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">date</span><span class="p">]</span>
|
||
<span class="n">unavailable_dates</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">date</span><span class="p">]</span>
|
||
<span class="n">vacation_dates</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">date</span><span class="p">]</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.importers.duty_schedule.DutyScheduleParseError" class="doc doc-heading">
|
||
<code>DutyScheduleParseError</code>
|
||
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
<p class="doc doc-class-bases">
|
||
Bases: <code><span title="Exception">Exception</span></code></p>
|
||
|
||
|
||
|
||
<p>Invalid or missing fields in duty-schedule JSON.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/importers/duty_schedule.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">32</span>
|
||
<span class="normal">33</span>
|
||
<span class="normal">34</span>
|
||
<span class="normal">35</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">DutyScheduleParseError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Invalid or missing fields in duty-schedule JSON."""</span>
|
||
|
||
<span class="k">pass</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="doc doc-object doc-class">
|
||
|
||
|
||
|
||
<h3 id="duty_teller.importers.duty_schedule.DutyScheduleResult" class="doc doc-heading">
|
||
<code>DutyScheduleResult</code>
|
||
|
||
|
||
<span class="doc doc-labels">
|
||
<small class="doc doc-label doc-label-dataclass"><code>dataclass</code></small>
|
||
</span>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
|
||
|
||
<p>Parsed duty schedule: start_date, end_date, and per-person entries.</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/importers/duty_schedule.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal">23</span>
|
||
<span class="normal">24</span>
|
||
<span class="normal">25</span>
|
||
<span class="normal">26</span>
|
||
<span class="normal">27</span>
|
||
<span class="normal">28</span>
|
||
<span class="normal">29</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="nd">@dataclass</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">DutyScheduleResult</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Parsed duty schedule: start_date, end_date, and per-person entries."""</span>
|
||
|
||
<span class="n">start_date</span><span class="p">:</span> <span class="n">date</span>
|
||
<span class="n">end_date</span><span class="p">:</span> <span class="n">date</span>
|
||
<span class="n">entries</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">DutyScheduleEntry</span><span class="p">]</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
|
||
|
||
|
||
<div class="doc doc-children">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
<div class="doc doc-object doc-function">
|
||
|
||
|
||
<h3 id="duty_teller.importers.duty_schedule.parse_duty_schedule" class="doc doc-heading">
|
||
<code class="highlight language-python"><span class="n">parse_duty_schedule</span><span class="p">(</span><span class="n">raw_bytes</span><span class="p">)</span></code>
|
||
|
||
</h3>
|
||
|
||
|
||
<div class="doc doc-contents ">
|
||
|
||
<p>Parse duty-schedule JSON into DutyScheduleResult.</p>
|
||
<p>Expects meta.start_date (YYYY-MM-DD) and schedule (array). For each schedule
|
||
item: name (required), duty string with ';' separator; index i = start_date + i days.
|
||
Cell values: в/В/б/Б => duty, Н => unavailable, О => vacation; rest ignored.</p>
|
||
|
||
|
||
<p><span class="doc-section-title">Parameters:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code>raw_bytes</code>
|
||
</td>
|
||
<td>
|
||
<code><span title="bytes">bytes</span></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>UTF-8 encoded JSON bytes.</p>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<em>required</em>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Returns:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="DutyScheduleResult
|
||
|
||
|
||
|
||
dataclass
|
||
(duty_teller.importers.duty_schedule.DutyScheduleResult)" href="#duty_teller.importers.duty_schedule.DutyScheduleResult">DutyScheduleResult</a></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>DutyScheduleResult with start_date, end_date, and entries (per-person dates).</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<p><span class="doc-section-title">Raises:</span></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="doc-section-item">
|
||
<td>
|
||
<code><a class="autorefs autorefs-internal" title="DutyScheduleParseError (duty_teller.importers.duty_schedule.DutyScheduleParseError)" href="#duty_teller.importers.duty_schedule.DutyScheduleParseError">DutyScheduleParseError</a></code>
|
||
</td>
|
||
<td>
|
||
<div class="doc-md-description">
|
||
<p>On invalid JSON, missing/invalid meta or schedule,
|
||
or invalid item fields.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
|
||
<details class="mkdocstrings-source">
|
||
<summary>Source code in <code>duty_teller/importers/duty_schedule.py</code></summary>
|
||
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"> 38</span>
|
||
<span class="normal"> 39</span>
|
||
<span class="normal"> 40</span>
|
||
<span class="normal"> 41</span>
|
||
<span class="normal"> 42</span>
|
||
<span class="normal"> 43</span>
|
||
<span class="normal"> 44</span>
|
||
<span class="normal"> 45</span>
|
||
<span class="normal"> 46</span>
|
||
<span class="normal"> 47</span>
|
||
<span class="normal"> 48</span>
|
||
<span class="normal"> 49</span>
|
||
<span class="normal"> 50</span>
|
||
<span class="normal"> 51</span>
|
||
<span class="normal"> 52</span>
|
||
<span class="normal"> 53</span>
|
||
<span class="normal"> 54</span>
|
||
<span class="normal"> 55</span>
|
||
<span class="normal"> 56</span>
|
||
<span class="normal"> 57</span>
|
||
<span class="normal"> 58</span>
|
||
<span class="normal"> 59</span>
|
||
<span class="normal"> 60</span>
|
||
<span class="normal"> 61</span>
|
||
<span class="normal"> 62</span>
|
||
<span class="normal"> 63</span>
|
||
<span class="normal"> 64</span>
|
||
<span class="normal"> 65</span>
|
||
<span class="normal"> 66</span>
|
||
<span class="normal"> 67</span>
|
||
<span class="normal"> 68</span>
|
||
<span class="normal"> 69</span>
|
||
<span class="normal"> 70</span>
|
||
<span class="normal"> 71</span>
|
||
<span class="normal"> 72</span>
|
||
<span class="normal"> 73</span>
|
||
<span class="normal"> 74</span>
|
||
<span class="normal"> 75</span>
|
||
<span class="normal"> 76</span>
|
||
<span class="normal"> 77</span>
|
||
<span class="normal"> 78</span>
|
||
<span class="normal"> 79</span>
|
||
<span class="normal"> 80</span>
|
||
<span class="normal"> 81</span>
|
||
<span class="normal"> 82</span>
|
||
<span class="normal"> 83</span>
|
||
<span class="normal"> 84</span>
|
||
<span class="normal"> 85</span>
|
||
<span class="normal"> 86</span>
|
||
<span class="normal"> 87</span>
|
||
<span class="normal"> 88</span>
|
||
<span class="normal"> 89</span>
|
||
<span class="normal"> 90</span>
|
||
<span class="normal"> 91</span>
|
||
<span class="normal"> 92</span>
|
||
<span class="normal"> 93</span>
|
||
<span class="normal"> 94</span>
|
||
<span class="normal"> 95</span>
|
||
<span class="normal"> 96</span>
|
||
<span class="normal"> 97</span>
|
||
<span class="normal"> 98</span>
|
||
<span class="normal"> 99</span>
|
||
<span class="normal">100</span>
|
||
<span class="normal">101</span>
|
||
<span class="normal">102</span>
|
||
<span class="normal">103</span>
|
||
<span class="normal">104</span>
|
||
<span class="normal">105</span>
|
||
<span class="normal">106</span>
|
||
<span class="normal">107</span>
|
||
<span class="normal">108</span>
|
||
<span class="normal">109</span>
|
||
<span class="normal">110</span>
|
||
<span class="normal">111</span>
|
||
<span class="normal">112</span>
|
||
<span class="normal">113</span>
|
||
<span class="normal">114</span>
|
||
<span class="normal">115</span>
|
||
<span class="normal">116</span>
|
||
<span class="normal">117</span>
|
||
<span class="normal">118</span>
|
||
<span class="normal">119</span>
|
||
<span class="normal">120</span>
|
||
<span class="normal">121</span>
|
||
<span class="normal">122</span>
|
||
<span class="normal">123</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">parse_duty_schedule</span><span class="p">(</span><span class="n">raw_bytes</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">)</span> <span class="o">-></span> <span class="n">DutyScheduleResult</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Parse duty-schedule JSON into DutyScheduleResult.</span>
|
||
|
||
<span class="sd"> Expects meta.start_date (YYYY-MM-DD) and schedule (array). For each schedule</span>
|
||
<span class="sd"> item: name (required), duty string with ';' separator; index i = start_date + i days.</span>
|
||
<span class="sd"> Cell values: в/В/б/Б => duty, Н => unavailable, О => vacation; rest ignored.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> raw_bytes: UTF-8 encoded JSON bytes.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> DutyScheduleResult with start_date, end_date, and entries (per-person dates).</span>
|
||
|
||
<span class="sd"> Raises:</span>
|
||
<span class="sd"> DutyScheduleParseError: On invalid JSON, missing/invalid meta or schedule,</span>
|
||
<span class="sd"> or invalid item fields.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">raw_bytes</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">))</span>
|
||
<span class="k">except</span> <span class="p">(</span><span class="n">json</span><span class="o">.</span><span class="n">JSONDecodeError</span><span class="p">,</span> <span class="ne">UnicodeDecodeError</span><span class="p">)</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="n">DutyScheduleParseError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Invalid JSON or encoding: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">e</span>
|
||
|
||
<span class="n">meta</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"meta"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">meta</span> <span class="ow">or</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">meta</span><span class="p">,</span> <span class="nb">dict</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="n">DutyScheduleParseError</span><span class="p">(</span><span class="s2">"Missing or invalid 'meta'"</span><span class="p">)</span>
|
||
|
||
<span class="n">start_str</span> <span class="o">=</span> <span class="n">meta</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"start_date"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">start_str</span> <span class="ow">or</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">start_str</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="n">DutyScheduleParseError</span><span class="p">(</span><span class="s2">"Missing or invalid meta.start_date"</span><span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">start_date</span> <span class="o">=</span> <span class="n">date</span><span class="o">.</span><span class="n">fromisoformat</span><span class="p">(</span><span class="n">start_str</span><span class="o">.</span><span class="n">strip</span><span class="p">())</span>
|
||
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="n">DutyScheduleParseError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Invalid meta.start_date: </span><span class="si">{</span><span class="n">start_str</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="kn">from</span><span class="w"> </span><span class="nn">e</span>
|
||
|
||
<span class="n">schedule</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"schedule"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">schedule</span><span class="p">,</span> <span class="nb">list</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="n">DutyScheduleParseError</span><span class="p">(</span><span class="s2">"Missing or invalid 'schedule' (must be array)"</span><span class="p">)</span>
|
||
|
||
<span class="n">max_days</span> <span class="o">=</span> <span class="mi">0</span>
|
||
<span class="n">entries</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">DutyScheduleEntry</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
||
|
||
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">schedule</span><span class="p">:</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">row</span><span class="p">,</span> <span class="nb">dict</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="n">DutyScheduleParseError</span><span class="p">(</span><span class="s2">"schedule item must be an object"</span><span class="p">)</span>
|
||
<span class="n">name</span> <span class="o">=</span> <span class="n">row</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"name"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">name</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="n">DutyScheduleParseError</span><span class="p">(</span><span class="s2">"schedule item must have 'name' (string)"</span><span class="p">)</span>
|
||
<span class="n">full_name</span> <span class="o">=</span> <span class="n">name</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">full_name</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="n">DutyScheduleParseError</span><span class="p">(</span><span class="s2">"schedule item 'name' cannot be empty"</span><span class="p">)</span>
|
||
|
||
<span class="n">duty_str</span> <span class="o">=</span> <span class="n">row</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"duty"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">duty_str</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">duty_str</span> <span class="o">=</span> <span class="s2">""</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">duty_str</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="n">DutyScheduleParseError</span><span class="p">(</span><span class="s2">"schedule item 'duty' must be string"</span><span class="p">)</span>
|
||
|
||
<span class="n">cells</span> <span class="o">=</span> <span class="p">[</span><span class="n">c</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">duty_str</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">";"</span><span class="p">)]</span>
|
||
<span class="n">max_days</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">max_days</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">cells</span><span class="p">))</span>
|
||
|
||
<span class="n">duty_dates</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">date</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="n">unavailable_dates</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">date</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="n">vacation_dates</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">date</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">cell</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">cells</span><span class="p">):</span>
|
||
<span class="n">d</span> <span class="o">=</span> <span class="n">start_date</span> <span class="o">+</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="n">i</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">cell</span> <span class="ow">in</span> <span class="n">DUTY_MARKERS</span><span class="p">:</span>
|
||
<span class="n">duty_dates</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
|
||
<span class="k">elif</span> <span class="n">cell</span> <span class="o">==</span> <span class="n">UNAVAILABLE_MARKER</span><span class="p">:</span>
|
||
<span class="n">unavailable_dates</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
|
||
<span class="k">elif</span> <span class="n">cell</span> <span class="o">==</span> <span class="n">VACATION_MARKER</span><span class="p">:</span>
|
||
<span class="n">vacation_dates</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
|
||
<span class="n">entries</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||
<span class="n">DutyScheduleEntry</span><span class="p">(</span>
|
||
<span class="n">full_name</span><span class="o">=</span><span class="n">full_name</span><span class="p">,</span>
|
||
<span class="n">duty_dates</span><span class="o">=</span><span class="n">duty_dates</span><span class="p">,</span>
|
||
<span class="n">unavailable_dates</span><span class="o">=</span><span class="n">unavailable_dates</span><span class="p">,</span>
|
||
<span class="n">vacation_dates</span><span class="o">=</span><span class="n">vacation_dates</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="n">max_days</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
|
||
<span class="n">end_date</span> <span class="o">=</span> <span class="n">start_date</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">end_date</span> <span class="o">=</span> <span class="n">start_date</span> <span class="o">+</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="n">max_days</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
|
||
|
||
<span class="k">return</span> <span class="n">DutyScheduleResult</span><span class="p">(</span><span class="n">start_date</span><span class="o">=</span><span class="n">start_date</span><span class="p">,</span> <span class="n">end_date</span><span class="o">=</span><span class="n">end_date</span><span class="p">,</span> <span class="n">entries</span><span class="o">=</span><span class="n">entries</span><span class="p">)</span>
|
||
</code></pre></div></td></tr></table></div>
|
||
</details>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</article>
|
||
</div>
|
||
|
||
|
||
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
|
||
</div>
|
||
|
||
</main>
|
||
|
||
<footer class="md-footer">
|
||
|
||
<div class="md-footer-meta md-typeset">
|
||
<div class="md-footer-meta__inner md-grid">
|
||
<div class="md-copyright">
|
||
|
||
|
||
Made with
|
||
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
||
Material for MkDocs
|
||
</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
</div>
|
||
<div class="md-dialog" data-md-component="dialog">
|
||
<div class="md-dialog__inner md-typeset"></div>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
<script id="__config" type="application/json">{"annotate": null, "base": "..", "features": [], "search": "../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
|
||
|
||
|
||
<script src="../assets/javascripts/bundle.79ae519e.min.js"></script>
|
||
|
||
|
||
</body>
|
||
</html> |