diff --git a/README.md b/README.md new file mode 100644 index 0000000..220c04a --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +google\_cl +========== + +[![image](https://img.shields.io/pypi/v/google_cl.svg)](https://pypi.python.org/pypi/google_cl) + +[![image](https://img.shields.io/travis/vinitkumar/google_cl.svg)](https://travis-ci.org/vinitkumar/google_cl) + +[![Documentation Status](https://readthedocs.org/projects/google-cl/badge/?version=latest)](https://google-cl.readthedocs.io/en/latest/?badge=latest) + +[![Updates](https://pyup.io/repos/github/vinitkumar/google_cl/shield.svg)](https://pyup.io/repos/github/vinitkumar/google_cl/) + +Pythonic interface to interact with google services + +- Free software: Apache Software License 2.0 +- Documentation: . + +Features +-------- + +TODO: + +- Implement core Google services + diff --git a/README.rst b/README.rst deleted file mode 100644 index f53b5cb..0000000 --- a/README.rst +++ /dev/null @@ -1,74 +0,0 @@ -========= -google_cl -========= - - -.. image:: https://img.shields.io/pypi/v/google_cl.svg - :target: https://pypi.python.org/pypi/google_cl - -.. image:: https://img.shields.io/travis/vinitkumar/google_cl.svg - :target: https://travis-ci.org/vinitkumar/google_cl - -.. image:: https://readthedocs.org/projects/google-cl/badge/?version=latest - :target: https://google-cl.readthedocs.io/en/latest/?badge=latest - :alt: Documentation Status - - -.. image:: https://pyup.io/repos/github/vinitkumar/google_cl/shield.svg - :target: https://pyup.io/repos/github/vinitkumar/google_cl/ - :alt: Updates - - - -Pythonic interface to interact with google services - - -* Free software: Apache Software License 2.0 -* Documentation: https://google-cl.readthedocs.io. - - -Features --------- - -* TODO - -Contributors -+++++++ - -Code Contributors ------------------- - -This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. -.. image:: https://opencollective.com/googlecl/contributors.svg?width=890&button=false - -Financial Contributors ------------------- - -Become a financial contributor and help us sustain our community. Contribute_ - -Individuals -~~~~~~~~~~~~~~~~~~~~~~ - -.. image:: https://opencollective.com/googlecl/individuals.svg?width=890 - :target: https://opencollective.com/googlecl - -Organizations -~~~~~~~~~~~~~~~~~~~~~~ - -Support this project with your organization. Your logo will show up here with a link to your website. Contribute_ - -.. image:: https://opencollective.com/googlecl/organization/0/avatar.svg - :target: https://opencollective.com/googlecl/organization/0/website - -.. _Contribute: https://opencollective.com/googlecl - -* TODO - -Credits -------- - -This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template. - -.. _Cookiecutter: https://github.com/audreyr/cookiecutter -.. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage - diff --git a/google_cl/cli.py b/google_cl/cli.py deleted file mode 100644 index 1cb7b99..0000000 --- a/google_cl/cli.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- - -"""Console script for google_cl.""" -import sys -import click - - -@click.command() -def main(args=None): - """Console script for google_cl.""" - click.echo("Replace this message by putting your code into " - "google_cl.cli.main") - click.echo("See click documentation at http://click.pocoo.org/") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) # pragma: no cover diff --git a/setup.py b/setup.py index 9f8ba98..995c4c2 100644 --- a/setup.py +++ b/setup.py @@ -5,15 +5,14 @@ from setuptools import setup, find_packages -with open('README.rst') as readme_file: +with open('README.md') as readme_file: readme = readme_file.read() with open('HISTORY.rst') as history_file: history = history_file.read() -requirements = ['Click>=6.0', ] - -setup_requirements = ['pytest-runner', ] +setup_requirements = ['google-auth==1.6.3', 'pytest-runner', 'requests_oauthlib', 'requests' ] +requirements = ['google-auth==1.6.3', 'pytest-runner', 'requests_oauthlib', 'requests' ] test_requirements = ['pytest', ] @@ -36,7 +35,7 @@ description="Pythonic interface to interact with google services", entry_points={ 'console_scripts': [ - 'google_cl=google_cl.cli:main', + 'google=src.cli:cli', ], }, install_requires=requirements, @@ -45,7 +44,7 @@ include_package_data=True, keywords='google_cl', name='google_cl', - packages=find_packages(include=['google_cl']), + packages=find_packages(include=['src']), setup_requires=setup_requirements, test_suite='tests', tests_require=test_requirements, diff --git a/google_cl/__init__.py b/src/__init__.py similarity index 100% rename from google_cl/__init__.py rename to src/__init__.py diff --git a/src/authorize.py b/src/authorize.py new file mode 100644 index 0000000..734de71 --- /dev/null +++ b/src/authorize.py @@ -0,0 +1,114 @@ +# Source of this file: https://github.com/gilesknap/gphotos-sync/blob/master/gphotos/authorize.py + +import logging +from typing import List, Optional + +from requests.adapters import HTTPAdapter +from requests_oauthlib import OAuth2Session + +from pathlib import Path +from urllib3.util.retry import Retry + + + +from json import load, dump, JSONDecodeError + +logger = logging.getLogger(__name__) + +# OAuth endpoints given in the Google API documentation +authorization_base_url = "https://accounts.google.com/o/oauth2/v2/auth" +token_uri = "https://www.googleapis.com/oauth2/v4/token" + + + +class Authorize: + def __init__( + self, + scope: List[str], + token_file: Path, + secrets_file: Path, + max_retries: int = 5, + ): + self.max_retries = max_retries + self.scope: List[str] = scope + self.token_file: Path = token_file + self.session = None + self.token = None + + + try: + with secrets_file.open("r") as stream: + all_json = load(stream) + + secrets = all_json["installed"] + sef.client_id = secrets["client_id"] + self.client_secret = secrets["client_secret"] + self.redirect_uri = secrets["redirect_uris"][0] + self.token_uri = secrets["token_uri"] + self.extra = { + "client_id": self.client_id, + "client_secret": self.client_secret, + } + except (JSONDecodeError, IOError): + print(f"missing or bad secrets file: {secrets_file}") + exit(1) + + def load_token(self) -> Optional[str]: + try: + with self.token_file.open("r") as stream: + token = load(stream) + except (JSONDecodeError, IOError): + return None + return token + + def save_token(self, token: str): + with self.token_file.open("w") as stream: + dump(token, stream) + self.token_file.chmod(0o600) + + def authorize(self): + token = self.load_token() + + if token: + self.session = OAuth2Session( + self.client_id, + token=token, + auto_refresh_url=self.token_uri, + auto_refresh_kwargs=self.extra, + token_updater=self.save_token, + ) + else: + self.session = OAuth2Session( + self.client_id, + scope=self.scope, + redirect_uri=self.redirect_uri, + auto_refresh_url=self.token_uri, + auto_refresh_kwargs=self.extra, + token_updater=self.save_token, + ) + + authorization_url, _ = self.session.authorization_url( + authorization_base_url, access_type="offline", prompt="select_account" + ) + print("Please go here and authorize,", authorization_url) + + response_code = input("Paste the response token here:") + self.token = self.session.fetch_token( + self.token_uri, client_secret=self.client_secret, code=response_code) + self.save_token(self.token) + + # set up retry behaviour for authorize session + + retries = Retry( + total=self.max_retries, + backoff_factor=0.5, + status_forcelist=[500, 502, 503, 504], + method_whitelist=frozenset(["GET", "POST"]), + raise_on_status=False, + respect_retry_after_header=True, + ) + + self.session.mount("https://", HTTPAdapter(max_retries=retries)) + + + diff --git a/src/cli.py b/src/cli.py new file mode 100755 index 0000000..54de1e1 --- /dev/null +++ b/src/cli.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +"""Console script for google_cl.""" +import sys +import click +from src.authorize import Authorize + +@click.group() +def cli(): + click.echo("Sunday Monday") + +@cli.group() +def login(): + Authorize() + +@cli.group() +def picasa(): + click.echo("This is a list") + + +@cli.group() +def contacts(): + click.echo("contacts placeholder") + + +@picasa.command() +def photolist(): + click.echo("This is coming from picasa list") diff --git a/google_cl/google_cl.py b/src/google_cl.py similarity index 100% rename from google_cl/google_cl.py rename to src/google_cl.py