1
Fork 0
mirror of https://github.com/Steffo99/io-beep-boop.git synced 2024-11-22 00:14:18 +00:00

🎉 update some things

This commit is contained in:
Steffo 2022-04-28 18:05:05 +02:00
parent cb962bf098
commit 42ce7fa995
Signed by: steffo
GPG key ID: 6965406171929D01
14 changed files with 218 additions and 47 deletions

3
.gitignore vendored
View file

@ -7,6 +7,9 @@
# Add your own ignores here! # Add your own ignores here!
input.txt
registered.txt
unregistered.txt
################## ##################

View file

@ -0,0 +1,23 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="registered-fast" type="PythonConfigurationType" factoryName="Python">
<module name="io-beep-boop" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="$PROJECT_DIR$/.venv/bin/python" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="io_beep_boop.cli" />
<option name="PARAMETERS" value="-t 7aae3e1c5f484311b7c7e6596ba54e9b registered-fast --input &quot;input.txt&quot; --registered &quot;registered.txt&quot; --unregistered &quot;unregistered.txt&quot;" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="true" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
</component>

View file

@ -0,0 +1,23 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="registered-slow" type="PythonConfigurationType" factoryName="Python">
<module name="io-beep-boop" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="$PROJECT_DIR$/.venv/bin/python" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="io_beep_boop.cli" />
<option name="PARAMETERS" value="-t 7aae3e1c5f484311b7c7e6596ba54e9b registered-slow --input &quot;input.txt&quot; --registered &quot;registered.txt&quot; --unregistered &quot;unregistered.txt&quot;" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="true" />
<option name="MODULE_MODE" value="true" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
</component>

0
docs/source/_extra/.gitignore vendored Normal file
View file

0
docs/source/_static/.gitignore vendored Normal file
View file

View file

@ -1,6 +1,6 @@
###################### ################################
Command-line interface Using the command-line interface
###################### ################################
:mod:`io_beep_boop` includes a command line interface to (hopefully) facilitate the execution of certain tasks with the IO API. :mod:`io_beep_boop` includes a command line interface to (hopefully) facilitate the execution of certain tasks with the IO API.
@ -8,13 +8,13 @@ The interface can be invoked by entering the following in environments where the
.. code-block:: console .. code-block:: console
$ io-beep-boop (.venv)$ io-beep-boop
All commands can be suffixed with ``--help`` to read their documentation: All commands can be suffixed with ``--help`` to read their documentation:
.. code-block:: console .. code-block:: console
$ io-beep-boop --help (.venv)$ io-beep-boop --help
Usage: io-beep-boop [OPTIONS] COMMAND [ARGS]... Usage: io-beep-boop [OPTIONS] COMMAND [ARGS]...
Options: Options:
@ -37,12 +37,12 @@ API keys can be passed programmatically as the ``--token`` parameter, or manuall
.. code-block:: console .. code-block:: console
$ io-beep-boop --token="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" (.venv)$ io-beep-boop --token="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
.. code-block:: console .. code-block:: console
$ io-beep-boop (.venv)$ io-beep-boop
Token: Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Discover who is registered to a given service Discover who is registered to a given service
@ -54,22 +54,43 @@ Given a text file containing a list of fiscal codes separated by newlines, :mod:
Using the fast method Using the fast method
--------------------- ---------------------
.. todo:: By using the :meth:`~io_beep_boop.api.client.get_subscriptions_on_day` method on all days in a given date range, :mod:`io_beep_boop` can determine the users who registered to the service in those days.
Description of the fast method.
.. code-block:: console .. code-block:: console
$ io-beep-boop registered-fast (.venv)$ io-beep-boop registered-fast
.. warning::
Internally, this uses the ``/subscription-feed/`` endpoint, which is not enabled by default for all API keys, and requires manual approval by the IO Team.
If the token is not enabled to access the endpoint, a :class:`httpx.HTTPStatusError` will be raised.
.. code-block:: console
$ io-beep-boop registered-fast
...
httpx.HTTPStatusError: Client error '403 Forbidden' for url 'https://api.io.italia.it/api/v1/subscriptions-feed/2022-04-28'
For more information check: https://httpstatuses.com/403
Using the slow method Using the slow method
--------------------- ---------------------
.. todo:: By using the :meth:`io_beep_boop.api.client.get_profile` methods on all profiles with the given fiscal codes, :mod:`io_beep_boop` can determine which ones of those users are registered to the service and which ones are not.
Description of the fast method.
.. code-block:: console .. code-block:: console
$ io-beep-boop registered-slow (.venv)$ io-beep-boop registered-slow
By default, the method performs a single HTTP request per second, in order to avoid rate limits; this can be changed with the ``--sleep`` option:
.. code-block:: console
(.venv)$ io-beep-boop registered-slow --sleep 5.0
.. warning::
This endpoint performs a HTTP request for every single fiscal code in your given input document, which works, but may not be allowed by IO App API Terms of Service due to the extreme amount of requests possibly generated.
Try to keep the ``--sleep`` delay high!

View file

@ -7,9 +7,10 @@ An experimental wrapper and command line interface for the Italian `IO App API <
.. toctree:: .. toctree::
installation
cli cli
reference reference
trivia misc
Indices and tables Indices and tables

View file

@ -0,0 +1,71 @@
############
Installation
############
This page will act as a tutorial on how to safely setup a Python environment on a computer, and on how to install :mod:`io_beep_boop` inside.
.. hint::
If you are a Python developer, and you intend to use this library to develop new tools, you might be interested to know that:
- :mod:`io_beep_boop` is distributed through the `Python Package Index <https://pypi.org/>`_, so you can install it with any Python dependency manager and use it in any Python project;
- :mod:`io_beep_boop` supports :pep:`518`, so it may be installed from source using ``pip install .``;
- :mod:`io_beep_boop` uses `Poetry <https://python-poetry.org/>`_ as a dependency manager.
Installing Python
=================
:mod:`io_beep_boop` requires Python 3.10 or later.
You can download Python at the `Downloads page of the official website <https://www.python.org/downloads/>`_.
Ensure that "Add Python to the PATH" is checked during installation, or you will not be able to run scripts from the command line!
Creating a :mod:`venv`
======================
To prevent dependency conflicts, it is highly suggested to create a new :mod:`venv` ( *v*\ irtual *env*\ ironment) to install :mod:`io_beep_boop` in.
To do so, open a terminal or command prompt, create a new folder, access it, and when inside run ``python -m venv .venv``:
.. code-block:: console
$ cd Documents/Workspaces
$ mkdir IOTools
$ cd IOTools
$ python -m venv .venv
Once created, run the following command to access the venv:
.. code-block:: console
$ source ./.venv/bin/activate
.. code-block:: doscon
> .\.venv\Scripts\activate
You will have to activate the :mod:`venv` on every subsequent terminal session, or you won't be able to access the packages installed inside it!
.. seealso::
`Installing packages using pip and virtual environments <https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/>`_ by the Python Packaging Authority.
Installing :mod:`io_beep_boop`
==============================
Once enabled the venv, you can install packages in it by using :mod:`pip`.
Specifically, you'll want to install :mod:`io_beep_boop`:
.. code-block:: console
(.venv)$ pip install io-beep-boop
...
Successfully installed io-beep-boop-0.1.0
The installation is complete!
You may proceed to :doc:`cli`.

16
docs/source/misc.rst Normal file
View file

@ -0,0 +1,16 @@
#############
Miscellaneous
#############
Inspired by
===========
The `ComuneDiRivoli/parlaConIO GitHub repository <https://github.com/ComuneDiRivoli/parlaConIO>`_.
Package name
============
.. figure:: io-dota.webp
The package name is a tribute to the Dota 2 hero of the same name, Io.

View file

@ -2,6 +2,8 @@
API Reference API Reference
############# #############
.. automodule:: io_beep_boop
:mod:`io_beep_boop.api` :mod:`io_beep_boop.api`
======================= =======================

View file

@ -1,7 +0,0 @@
######
Trivia
######
.. figure:: io-dota.webp
The package name is a tribute to the Dota 2 hero of the same name, Io.

View file

@ -122,7 +122,7 @@ class IOServiceClient(httpx.Client):
https://developer.io.italia.it/openapi.html#operation/getSubscriptionsFeedForDate https://developer.io.italia.it/openapi.html#operation/getSubscriptionsFeedForDate
""" """
return self.get(f"/subscriptions-feed/{date.isoformat()}") return self.get(f"/subscriptions-feed/{date.strftime('%Y-%m-%d')}")
# TODO: Service-level endpoints - since it seems weird to me that a service can spawn endless clones of itself? # TODO: Service-level endpoints - since it seems weird to me that a service can spawn endless clones of itself?

View file

@ -65,8 +65,6 @@ class GetMessageResponse(IOModel):
class UserProfile(IOModel): class UserProfile(IOModel):
email: str
version: int
sender_allowed: bool sender_allowed: bool

View file

@ -12,13 +12,15 @@ from ..api.models import SubscriptionsFeed
def hash_fiscal_code(code: str) -> str: def hash_fiscal_code(code: str) -> str:
uppercased_code = code.upper() uppercased_code = code.upper()
hashed_code = hashlib.sha256(uppercased_code) encoded_code = bytes(uppercased_code, encoding="utf8")
hashed_code = hashlib.sha256(encoded_code)
hexed_hash = hashed_code.hexdigest() hexed_hash = hashed_code.hexdigest()
lowercased_hash = hexed_hash.lower() lowercased_hash = hexed_hash.lower()
return lowercased_hash return lowercased_hash
@click.group() @click.group()
@click.version_option(package_name="io-beep-boop")
@click.option( @click.option(
"-t", "-t",
"--token", "--token",
@ -65,15 +67,17 @@ def main(ctx: click.Context, token: str, base_url: str):
) )
@click.option( @click.option(
"--start-date", "--start-date",
type=datetime.date, type=click.DateTime(formats=["%Y-%m-%d"]),
help="The date to start retrieving fiscal codes from.", help="The date to start retrieving fiscal codes from.",
default=datetime.date.today().isoformat(),
prompt=True, prompt=True,
) )
@click.option( @click.option(
"--end-date", "--end-date",
type=datetime.date, type=click.DateTime(formats=["%Y-%m-%d"]),
help="The date to stop retrieving fiscal codes at.", help="The date to stop retrieving fiscal codes at.",
default=datetime.date.today(), default=datetime.date.today().isoformat(),
prompt=True,
) )
@click.option( @click.option(
"--sleep", "--sleep",
@ -99,12 +103,19 @@ def registered_fast(ctx: click.Context, input_file: t.TextIO, registered_file: t
with click.progressbar(range(0, total_days), length=total_days, label="Retrieving data from the API...") as days: with click.progressbar(range(0, total_days), length=total_days, label="Retrieving data from the API...") as days:
for day in days: for day in days:
feed: SubscriptionsFeed = client.get_subscriptions_on_day(date=start_date + datetime.timedelta(days=day)) while True:
try:
api_codes += set(feed.subscriptions) feed: SubscriptionsFeed = client.get_subscriptions_on_day(date=start_date + datetime.timedelta(days=day))
api_codes -= set(feed.unsubscriptions) except httpx.HTTPStatusError as e:
if e.response.status_code == 429:
time.sleep(sleep) continue
else:
raise
else:
api_codes += set(feed.subscriptions)
api_codes -= set(feed.unsubscriptions)
finally:
time.sleep(sleep)
# Convert objects back to fiscal codes # Convert objects back to fiscal codes
click.echo("Calculating registered fiscal codes...") click.echo("Calculating registered fiscal codes...")
@ -162,17 +173,26 @@ def registered_slow(ctx: click.Context, input_file: t.TextIO, registered_file: t
with click.progressbar(input_codes, label="Performing checks...") as codes: with click.progressbar(input_codes, label="Performing checks...") as codes:
for code in codes: for code in codes:
try: while True:
profile = client.get_profile(fiscal_code=code) try:
except httpx.HTTPStatusError: profile = client.get_profile(fiscal_code=code)
unregistered_file.write(f"{code}\n") except httpx.HTTPStatusError as e:
else: if e.response.status_code == 429:
if not profile.sender_allowed: continue
unregistered_file.write(f"{code}\n") elif e.response.status_code == 404:
unregistered_file.write(f"{code}\n")
break
else:
raise
else: else:
registered_file.write(f"{code}\n") if not profile.sender_allowed:
finally: unregistered_file.write(f"{code}\n")
time.sleep(sleep) break
else:
registered_file.write(f"{code}\n")
break
finally:
time.sleep(sleep)
if __name__ == "__main__": if __name__ == "__main__":