mirror of
https://github.com/Steffo99/io-beep-boop.git
synced 2024-11-21 07:54:20 +00:00
🎉 update some things
This commit is contained in:
parent
cb962bf098
commit
42ce7fa995
14 changed files with 218 additions and 47 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -7,6 +7,9 @@
|
|||
|
||||
# Add your own ignores here!
|
||||
|
||||
input.txt
|
||||
registered.txt
|
||||
unregistered.txt
|
||||
|
||||
|
||||
##################
|
||||
|
|
23
.idea/runConfigurations/registered_fast.xml
Normal file
23
.idea/runConfigurations/registered_fast.xml
Normal 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 "input.txt" --registered "registered.txt" --unregistered "unregistered.txt"" />
|
||||
<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>
|
23
.idea/runConfigurations/registered_slow.xml
Normal file
23
.idea/runConfigurations/registered_slow.xml
Normal 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 "input.txt" --registered "registered.txt" --unregistered "unregistered.txt"" />
|
||||
<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
0
docs/source/_extra/.gitignore
vendored
Normal file
0
docs/source/_static/.gitignore
vendored
Normal file
0
docs/source/_static/.gitignore
vendored
Normal 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.
|
||||
|
||||
|
@ -8,13 +8,13 @@ The interface can be invoked by entering the following in environments where the
|
|||
|
||||
.. code-block:: console
|
||||
|
||||
$ io-beep-boop
|
||||
(.venv)$ io-beep-boop
|
||||
|
||||
All commands can be suffixed with ``--help`` to read their documentation:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ io-beep-boop --help
|
||||
(.venv)$ io-beep-boop --help
|
||||
Usage: io-beep-boop [OPTIONS] COMMAND [ARGS]...
|
||||
|
||||
Options:
|
||||
|
@ -37,12 +37,12 @@ API keys can be passed programmatically as the ``--token`` parameter, or manuall
|
|||
|
||||
.. code-block:: console
|
||||
|
||||
$ io-beep-boop --token="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
||||
(.venv)$ io-beep-boop --token="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ io-beep-boop
|
||||
Token:
|
||||
(.venv)$ io-beep-boop
|
||||
Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
|
||||
|
||||
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
|
||||
---------------------
|
||||
|
||||
.. todo::
|
||||
|
||||
Description of the fast method.
|
||||
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.
|
||||
|
||||
.. 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
|
||||
---------------------
|
||||
|
||||
.. todo::
|
||||
|
||||
Description of the fast method.
|
||||
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.
|
||||
|
||||
.. 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!
|
||||
|
|
|
@ -7,9 +7,10 @@ An experimental wrapper and command line interface for the Italian `IO App API <
|
|||
|
||||
.. toctree::
|
||||
|
||||
installation
|
||||
cli
|
||||
reference
|
||||
trivia
|
||||
misc
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
|
71
docs/source/installation.rst
Normal file
71
docs/source/installation.rst
Normal 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
16
docs/source/misc.rst
Normal 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.
|
|
@ -2,6 +2,8 @@
|
|||
API Reference
|
||||
#############
|
||||
|
||||
.. automodule:: io_beep_boop
|
||||
|
||||
|
||||
:mod:`io_beep_boop.api`
|
||||
=======================
|
||||
|
|
|
@ -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.
|
|
@ -122,7 +122,7 @@ class IOServiceClient(httpx.Client):
|
|||
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?
|
||||
|
||||
|
|
|
@ -65,8 +65,6 @@ class GetMessageResponse(IOModel):
|
|||
|
||||
|
||||
class UserProfile(IOModel):
|
||||
email: str
|
||||
version: int
|
||||
sender_allowed: bool
|
||||
|
||||
|
||||
|
|
|
@ -12,13 +12,15 @@ from ..api.models import SubscriptionsFeed
|
|||
|
||||
def hash_fiscal_code(code: str) -> str:
|
||||
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()
|
||||
lowercased_hash = hexed_hash.lower()
|
||||
return lowercased_hash
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.version_option(package_name="io-beep-boop")
|
||||
@click.option(
|
||||
"-t",
|
||||
"--token",
|
||||
|
@ -65,15 +67,17 @@ def main(ctx: click.Context, token: str, base_url: str):
|
|||
)
|
||||
@click.option(
|
||||
"--start-date",
|
||||
type=datetime.date,
|
||||
type=click.DateTime(formats=["%Y-%m-%d"]),
|
||||
help="The date to start retrieving fiscal codes from.",
|
||||
default=datetime.date.today().isoformat(),
|
||||
prompt=True,
|
||||
)
|
||||
@click.option(
|
||||
"--end-date",
|
||||
type=datetime.date,
|
||||
type=click.DateTime(formats=["%Y-%m-%d"]),
|
||||
help="The date to stop retrieving fiscal codes at.",
|
||||
default=datetime.date.today(),
|
||||
default=datetime.date.today().isoformat(),
|
||||
prompt=True,
|
||||
)
|
||||
@click.option(
|
||||
"--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:
|
||||
for day in days:
|
||||
feed: SubscriptionsFeed = client.get_subscriptions_on_day(date=start_date + datetime.timedelta(days=day))
|
||||
|
||||
api_codes += set(feed.subscriptions)
|
||||
api_codes -= set(feed.unsubscriptions)
|
||||
|
||||
time.sleep(sleep)
|
||||
while True:
|
||||
try:
|
||||
feed: SubscriptionsFeed = client.get_subscriptions_on_day(date=start_date + datetime.timedelta(days=day))
|
||||
except httpx.HTTPStatusError as e:
|
||||
if e.response.status_code == 429:
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
api_codes += set(feed.subscriptions)
|
||||
api_codes -= set(feed.unsubscriptions)
|
||||
finally:
|
||||
time.sleep(sleep)
|
||||
|
||||
# Convert objects back to 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:
|
||||
for code in codes:
|
||||
try:
|
||||
profile = client.get_profile(fiscal_code=code)
|
||||
except httpx.HTTPStatusError:
|
||||
unregistered_file.write(f"{code}\n")
|
||||
else:
|
||||
if not profile.sender_allowed:
|
||||
unregistered_file.write(f"{code}\n")
|
||||
while True:
|
||||
try:
|
||||
profile = client.get_profile(fiscal_code=code)
|
||||
except httpx.HTTPStatusError as e:
|
||||
if e.response.status_code == 429:
|
||||
continue
|
||||
elif e.response.status_code == 404:
|
||||
unregistered_file.write(f"{code}\n")
|
||||
break
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
registered_file.write(f"{code}\n")
|
||||
finally:
|
||||
time.sleep(sleep)
|
||||
if not profile.sender_allowed:
|
||||
unregistered_file.write(f"{code}\n")
|
||||
break
|
||||
else:
|
||||
registered_file.write(f"{code}\n")
|
||||
break
|
||||
finally:
|
||||
time.sleep(sleep)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
Loading…
Reference in a new issue