Source code for tsmtool.tarsnap

# Tarsnap - tarsnap website interface

import datetime
from pathlib import Path

import requests
from bs4 import BeautifulSoup, element

URL = "https://www.tarsnap.com"


[docs]class Tarsnap: def __init__(self, config_file, account=None, email=None, password=None): self.url = URL self.account = account or "undefined" self.config = {} if config_file and config_file.exists(): for line in Path(config_file).open("r").readlines(): _account, _email, _password = line.split() self.config[_account] = dict(email=_email, password=_password) if not account: account = _account _config = self.config.get(account, {}) self.email = email or _config.get("email") self.password = password or _config.get("password") self.session = requests.Session() def _post(self, route, data): return self.session.post(self.url + "/" + route, data) def _get(self, route): return self.session.get(self.url + "/" + route) def _round(self, value): return float("%.2f" % value) def _query(self): response = self._post( "manage.cgi", {"address": self.email, "password": self.password} ) # for div in [soup.find('div')]: # print('div: %s ' % repr(div.text)) balance = None account = None verbose_soup = None soup = BeautifulSoup(response.text, "html.parser") # print(soup.prettify()) for el in [ e for e in soup.find_all("div") if e and isinstance(e, element.Tag) ]: for div in [ e for e in el.find_all("div") if e and isinstance(e, element.Tag) ]: if div.attrs.get("class") == ["boxcontents"]: msg = div.text.strip().split("\n")[0] if f"You are logged in as {self.email}" not in msg: raise RuntimeError(msg) for el in soup.find("div").find_all("p"): if "current account balance" in el.text: balance = self._round(float(el.find("code").text[1:])) elif "logged in as" in el.text: account = el.find("code").text for el in soup.find_all("a", href=True): if el["href"].endswith("verboseactivity"): response = self._get(el["href"]) verbose_soup = BeautifulSoup(response.text, "html.parser") break return balance, account, verbose_soup def _handle_row(self, r, row): if row[0] == "Balance": r["balances"].append((row[1], float(row[6]))) if row[0] == "Payment": payment = float(row[5]) r["payments"][row[1]] = payment else: payment = 0 return payment
[docs] def get_status( self, rows=False, balances=False, payments=False, raw=False, email=None, password=None, ): email = email or self.email password = password or self.password if not email: raise ValueError("--email is required") if not password: raise ValueError("--password is required") balance, account, soup = self._query() r = {} r["balance"] = balance r["account"] = account r["rows"] = [] r["balances"] = [] r["payments"] = {} payment_total = 0.0 for el in soup.find("table").find_all("tr"): r["rows"].append([el.text for el in el.find_all("td")]) for row in r["rows"]: payment_total += self._handle_row(r, row) if not raw: if r["balances"]: begin_date = datetime.datetime.strptime( r["balances"][0][0], "%Y-%m-%d" ) begin_amount = float(r["balances"][0][1]) end_date = datetime.datetime.strptime( r["balances"][-1][0], "%Y-%m-%d" ) end_amount = float(r["balances"][-1][1]) r["monthly_cost"] = self._round( (begin_amount - (end_amount - payment_total)) / (end_date - begin_date).days * 365 / 12 ) if not rows: del r["rows"] if not balances: del r["balances"] if not payments: del r["payments"] return r