From 749ff0fc4ac643daa87b25bb695b9911f0b9a590 Mon Sep 17 00:00:00 2001 From: Wen Junhua Date: Tue, 15 Mar 2022 00:21:00 -0400 Subject: [PATCH 1/5] refator: move source to flaskr folder --- .gitignore | 3 + flaskr/__init__.py | 1 + flaskr/__main__.py | 46 ++++++++++ __main__.py => flaskr/app.py | 92 +++++++++---------- constants.py => flaskr/constants.py | 0 {templates => flaskr/templates}/base.html | 0 {templates => flaskr/templates}/index.html | 0 {templates => flaskr/templates}/nav.html | 0 {tools => flaskr/tools}/__init__.py | 0 .../tools}/facebook_bot/FacebookBot.py | 0 .../tools}/facebook_bot/__init__.py | 0 .../tools}/facebook_bot/constants.py | 0 {tools => flaskr/tools}/facebook_bot/utils.py | 0 {tools => flaskr/tools}/post.py | 0 .../tools}/telegram_bot/TelegramBot.py | 0 .../tools}/telegram_bot/__init__.py | 0 .../tools}/telegram_bot/__main__.py | 0 .../tools}/telegram_bot/constants.py | 0 {tools => flaskr/tools}/telegram_bot/util.py | 0 .../tools}/twitter_bot/TwitterBot.py | 0 .../tools}/twitter_bot/__init__.py | 0 .../tools}/twitter_bot/secrets.py | 0 requirements.txt | 3 +- 23 files changed, 97 insertions(+), 48 deletions(-) create mode 100644 flaskr/__init__.py create mode 100644 flaskr/__main__.py rename __main__.py => flaskr/app.py (53%) rename constants.py => flaskr/constants.py (100%) rename {templates => flaskr/templates}/base.html (100%) rename {templates => flaskr/templates}/index.html (100%) rename {templates => flaskr/templates}/nav.html (100%) rename {tools => flaskr/tools}/__init__.py (100%) rename {tools => flaskr/tools}/facebook_bot/FacebookBot.py (100%) rename {tools => flaskr/tools}/facebook_bot/__init__.py (100%) rename {tools => flaskr/tools}/facebook_bot/constants.py (100%) rename {tools => flaskr/tools}/facebook_bot/utils.py (100%) rename {tools => flaskr/tools}/post.py (100%) rename {tools => flaskr/tools}/telegram_bot/TelegramBot.py (100%) rename {tools => flaskr/tools}/telegram_bot/__init__.py (100%) rename {tools => flaskr/tools}/telegram_bot/__main__.py (100%) rename {tools => flaskr/tools}/telegram_bot/constants.py (100%) rename {tools => flaskr/tools}/telegram_bot/util.py (100%) rename {tools => flaskr/tools}/twitter_bot/TwitterBot.py (100%) rename {tools => flaskr/tools}/twitter_bot/__init__.py (100%) rename {tools => flaskr/tools}/twitter_bot/secrets.py (100%) diff --git a/.gitignore b/.gitignore index 9c58de5..ea86adb 100644 --- a/.gitignore +++ b/.gitignore @@ -113,5 +113,8 @@ dmypy.json # Pyre type checker .pyre/ +# VS Code +.vscode/ + # Ignore config files config.ini \ No newline at end of file diff --git a/flaskr/__init__.py b/flaskr/__init__.py new file mode 100644 index 0000000..d3efdaf --- /dev/null +++ b/flaskr/__init__.py @@ -0,0 +1 @@ +from .__main__ import app \ No newline at end of file diff --git a/flaskr/__main__.py b/flaskr/__main__.py new file mode 100644 index 0000000..abe2ac7 --- /dev/null +++ b/flaskr/__main__.py @@ -0,0 +1,46 @@ +"""The frontend for the social media manager""" +import os +import configparser + + +try: + from constants import DEFAULT_CONFIG_FILE, UPLOAD_FOLDER + from app import app +except ImportError: + from .constants import DEFAULT_CONFIG_FILE, UPLOAD_FOLDER + from .app import app + + +def create_default_config() -> None: + """Create a default configuration file""" + config = configparser.ConfigParser() + # config["twitter"] = { + # "api_key": "", + # "api_secret": "", + # "access_token": "", + # "access_token_secret": "", + # "bearer_token": "", + # } + config["facebook"] = { + "token": "", + "group_id": "", + } + config["telegram"] = { + "token": "", + "group_id": "", + } + with open(DEFAULT_CONFIG_FILE, "w") as configfile: + config.write(configfile) + + +if __name__ == "__main__": + if not os.path.exists(DEFAULT_CONFIG_FILE): + create_default_config() + credentials = configparser.ConfigParser() + credentials.read(DEFAULT_CONFIG_FILE) + + # Check if upload folder exists + if not os.path.exists(UPLOAD_FOLDER): + os.mkdir(UPLOAD_FOLDER) + + app.run(debug=False) diff --git a/__main__.py b/flaskr/app.py similarity index 53% rename from __main__.py rename to flaskr/app.py index bd78e98..53802ea 100644 --- a/__main__.py +++ b/flaskr/app.py @@ -1,31 +1,65 @@ -"""The frontend for the social media manager""" import os -import configparser -from flask import Flask, render_template, request, redirect, flash, Response +from flask import Flask +from flask import render_template, request, redirect, flash, Response from werkzeug.utils import secure_filename -from constants import ERROR_FLASH, FACEBOOK, IMAGE_KEY, INFO_FLASH, SOCIAL_MEDIA, HOME_PAGE, SELECT_AT_LEAST_ONE_SOCIAL_MEDIA, SUCCESS_MESSAGE, EMPTY_CONTENT, CONTENT_KEY, EMPTY_STRING, DEFAULT_CONFIG_FILE, TELEGRAM, TWITTER, UPLOAD_FOLDER -from tools import post_to_twitter -from tools.post import post_to_facebook, post_to_telegram +try: + from .constants import ( + ERROR_FLASH, + FACEBOOK, + IMAGE_KEY, + INFO_FLASH, + SOCIAL_MEDIA, + HOME_PAGE, + SELECT_AT_LEAST_ONE_SOCIAL_MEDIA, + SUCCESS_MESSAGE, + EMPTY_CONTENT, + CONTENT_KEY, + EMPTY_STRING, + TELEGRAM, + TWITTER, + UPLOAD_FOLDER, + ) + from .tools import post_to_twitter + from .tools.post import post_to_facebook, post_to_telegram +except ImportError: + from constants import ( + ERROR_FLASH, + FACEBOOK, + IMAGE_KEY, + INFO_FLASH, + SOCIAL_MEDIA, + HOME_PAGE, + SELECT_AT_LEAST_ONE_SOCIAL_MEDIA, + SUCCESS_MESSAGE, + EMPTY_CONTENT, + CONTENT_KEY, + EMPTY_STRING, + TELEGRAM, + TWITTER, + UPLOAD_FOLDER, + ) + from tools import post_to_twitter + from tools.post import post_to_facebook, post_to_telegram app = Flask(__name__) app.secret_key = os.urandom(128).hex() -app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER +app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER + credentials = None func_dict = { FACEBOOK: post_to_facebook, TELEGRAM: post_to_telegram, - # TWITTER: post_to_twitter, } -@app.route('/', methods=['GET', 'POST']) +@app.route("/", methods=["GET", "POST"]) def index() -> Response: """The home page for the social media manager""" if request.method == "GET": - return render_template('index.html') + return render_template("index.html") is_success = True @@ -42,7 +76,6 @@ def index() -> Response: img_path = os.path.join(UPLOAD_FOLDER, filename) image.save(img_path) - # Check if user picked at least one social media. to_post = set() for attr in request.form: @@ -65,7 +98,7 @@ def index() -> Response: except Exception as e: flash(f"Error posting to {post_type}: {e}", ERROR_FLASH) is_success = False - + # Remove the image after posting if img_path is not None: os.remove(img_path) @@ -73,38 +106,3 @@ def index() -> Response: if is_success: flash(SUCCESS_MESSAGE, INFO_FLASH) return redirect(HOME_PAGE) - - -def create_default_config() -> None: - """Create a default configuration file""" - config = configparser.ConfigParser() - # config["twitter"] = { - # "api_key": "", - # "api_secret": "", - # "access_token": "", - # "access_token_secret": "", - # "bearer_token": "", - # } - config["facebook"] = { - "token": "", - "group_id": "", - } - config["telegram"] = { - "token": "", - "group_id": "", - } - with open(DEFAULT_CONFIG_FILE, 'w') as configfile: - config.write(configfile) - - -if __name__ == "__main__": - if not os.path.exists(DEFAULT_CONFIG_FILE): - create_default_config() - credentials = configparser.ConfigParser() - credentials.read(DEFAULT_CONFIG_FILE) - - # Check if upload folder exists - if not os.path.exists(UPLOAD_FOLDER): - os.mkdir(UPLOAD_FOLDER) - - app.run(debug=False) diff --git a/constants.py b/flaskr/constants.py similarity index 100% rename from constants.py rename to flaskr/constants.py diff --git a/templates/base.html b/flaskr/templates/base.html similarity index 100% rename from templates/base.html rename to flaskr/templates/base.html diff --git a/templates/index.html b/flaskr/templates/index.html similarity index 100% rename from templates/index.html rename to flaskr/templates/index.html diff --git a/templates/nav.html b/flaskr/templates/nav.html similarity index 100% rename from templates/nav.html rename to flaskr/templates/nav.html diff --git a/tools/__init__.py b/flaskr/tools/__init__.py similarity index 100% rename from tools/__init__.py rename to flaskr/tools/__init__.py diff --git a/tools/facebook_bot/FacebookBot.py b/flaskr/tools/facebook_bot/FacebookBot.py similarity index 100% rename from tools/facebook_bot/FacebookBot.py rename to flaskr/tools/facebook_bot/FacebookBot.py diff --git a/tools/facebook_bot/__init__.py b/flaskr/tools/facebook_bot/__init__.py similarity index 100% rename from tools/facebook_bot/__init__.py rename to flaskr/tools/facebook_bot/__init__.py diff --git a/tools/facebook_bot/constants.py b/flaskr/tools/facebook_bot/constants.py similarity index 100% rename from tools/facebook_bot/constants.py rename to flaskr/tools/facebook_bot/constants.py diff --git a/tools/facebook_bot/utils.py b/flaskr/tools/facebook_bot/utils.py similarity index 100% rename from tools/facebook_bot/utils.py rename to flaskr/tools/facebook_bot/utils.py diff --git a/tools/post.py b/flaskr/tools/post.py similarity index 100% rename from tools/post.py rename to flaskr/tools/post.py diff --git a/tools/telegram_bot/TelegramBot.py b/flaskr/tools/telegram_bot/TelegramBot.py similarity index 100% rename from tools/telegram_bot/TelegramBot.py rename to flaskr/tools/telegram_bot/TelegramBot.py diff --git a/tools/telegram_bot/__init__.py b/flaskr/tools/telegram_bot/__init__.py similarity index 100% rename from tools/telegram_bot/__init__.py rename to flaskr/tools/telegram_bot/__init__.py diff --git a/tools/telegram_bot/__main__.py b/flaskr/tools/telegram_bot/__main__.py similarity index 100% rename from tools/telegram_bot/__main__.py rename to flaskr/tools/telegram_bot/__main__.py diff --git a/tools/telegram_bot/constants.py b/flaskr/tools/telegram_bot/constants.py similarity index 100% rename from tools/telegram_bot/constants.py rename to flaskr/tools/telegram_bot/constants.py diff --git a/tools/telegram_bot/util.py b/flaskr/tools/telegram_bot/util.py similarity index 100% rename from tools/telegram_bot/util.py rename to flaskr/tools/telegram_bot/util.py diff --git a/tools/twitter_bot/TwitterBot.py b/flaskr/tools/twitter_bot/TwitterBot.py similarity index 100% rename from tools/twitter_bot/TwitterBot.py rename to flaskr/tools/twitter_bot/TwitterBot.py diff --git a/tools/twitter_bot/__init__.py b/flaskr/tools/twitter_bot/__init__.py similarity index 100% rename from tools/twitter_bot/__init__.py rename to flaskr/tools/twitter_bot/__init__.py diff --git a/tools/twitter_bot/secrets.py b/flaskr/tools/twitter_bot/secrets.py similarity index 100% rename from tools/twitter_bot/secrets.py rename to flaskr/tools/twitter_bot/secrets.py diff --git a/requirements.txt b/requirements.txt index e2ef260..687ac79 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ tweepy facebook-sdk -flask \ No newline at end of file +flask +pytest \ No newline at end of file From 00a648c90b5c85cc04217978b45b73d446d443b7 Mon Sep 17 00:00:00 2001 From: Wen Junhua Date: Tue, 15 Mar 2022 00:21:07 -0400 Subject: [PATCH 2/5] doc: update doc --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 735a193..28e2980 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,10 @@ A Tool to help post on various social media simultaneously ## Quick Start guide 1. Install dependencies using `pip install -r requirements.txt` -2. Start the website by running `python3 .` +2. Start the website by running `python3 flaskr` 3. Stop the website using `Ctrl+C` 4. Fill up the configuration under `config.ini` -5. Run the server again by running `python3 .` +5. Run the server again by running `python3 flaskr` 6. Navigate to [`http://localhost:5000/`](http://localhost:5000/) 7. Fill up the form From 2c04d842aede96c542f3e5c51c4acbbac053b6f6 Mon Sep 17 00:00:00 2001 From: Wen Junhua Date: Tue, 15 Mar 2022 00:21:12 -0400 Subject: [PATCH 3/5] feat: add tests --- tests/__init__.py | 0 tests/conftest.py | 24 ++++++++++++++++++++++++ tests/test_homepage.py | 20 ++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/test_homepage.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..4e094a5 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,24 @@ +import pytest +from flaskr import app as flask_app + +@pytest.fixture() +def app(): + flask_app.config.update({ + "TESTING": True, + }) + + # other setup can go here + + yield flask_app + + # clean up / reset resources here + + +@pytest.fixture() +def client(app): + return app.test_client() + + +@pytest.fixture() +def runner(app): + return app.test_cli_runner() \ No newline at end of file diff --git a/tests/test_homepage.py b/tests/test_homepage.py new file mode 100644 index 0000000..e9da2eb --- /dev/null +++ b/tests/test_homepage.py @@ -0,0 +1,20 @@ +def test_homepage(client): + """Test homepage.""" + response = client.get("/") + assert response.status_code == 200 + assert "No content provided" not in response.data.decode() + + +def test_empty_post_request_fail(client): + """Test empty post request.""" + response = client.post("/", follow_redirects=True) + assert response.status_code == 200 + assert "No content provided" in response.data.decode() + + +def test_no_social_media_selected_fails(client): + """Test no social media selected.""" + response = client.post("/", data={"content": "test"}, follow_redirects=True) + assert response.status_code == 200 + assert "Please select at least 1 social media" in response.data.decode() + From b4aa4c504826e063918d21a35df37f7e9be5cb48 Mon Sep 17 00:00:00 2001 From: Wen Junhua Date: Tue, 15 Mar 2022 00:22:12 -0400 Subject: [PATCH 4/5] Create python-app.yml --- .github/workflows/python-app.yml | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/python-app.yml diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 0000000..759d90e --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,36 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python application + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.10 + uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest From 018d95f541b4d1421ac661add4f749e2598f8cb2 Mon Sep 17 00:00:00 2001 From: Wen Junhua Date: Sat, 19 Mar 2022 01:38:30 -0400 Subject: [PATCH 5/5] fix: fix imports --- flaskr/app.py | 80 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/flaskr/app.py b/flaskr/app.py index 66e5ca4..6575e77 100644 --- a/flaskr/app.py +++ b/flaskr/app.py @@ -5,33 +5,61 @@ from flask import Flask, abort, render_template, request, redirect, flash, Response from werkzeug.utils import secure_filename -from constants import ( - BODY_KEY, - CSV_KEY, - EMAIL_KEY, - ERROR_FLASH, - FACEBOOK, - IMAGE_KEY, - INFO_FLASH, - SOCIAL_MEDIA, - HOME_PAGE, - SELECT_AT_LEAST_ONE_SOCIAL_MEDIA, - SUBJECT_KEY, - SUCCESS_MESSAGE, - EMPTY_CONTENT, - CONTENT_KEY, - EMPTY_STRING, - DEFAULT_CONFIG_FILE, - TELEGRAM, - TWITTER, - EMAIL_PAGE, - UPLOAD_FOLDER, -) -from tools.post import post_to_facebook, post_to_telegram, post_to_twitter -from tools.email.email_sc import EmailIntegration, create_email -from tools.email import extract_csv -from tools.utils import replace_string +try: + from constants import ( + BODY_KEY, + CSV_KEY, + EMAIL_KEY, + ERROR_FLASH, + FACEBOOK, + IMAGE_KEY, + INFO_FLASH, + SOCIAL_MEDIA, + HOME_PAGE, + SELECT_AT_LEAST_ONE_SOCIAL_MEDIA, + SUBJECT_KEY, + SUCCESS_MESSAGE, + EMPTY_CONTENT, + CONTENT_KEY, + EMPTY_STRING, + DEFAULT_CONFIG_FILE, + TELEGRAM, + TWITTER, + EMAIL_PAGE, + UPLOAD_FOLDER, + ) + from tools.post import post_to_facebook, post_to_telegram, post_to_twitter + from tools.email.email_sc import EmailIntegration, create_email + from tools.email import extract_csv + from tools.utils import replace_string +except ImportError as e: + from .constants import ( + BODY_KEY, + CSV_KEY, + EMAIL_KEY, + ERROR_FLASH, + FACEBOOK, + IMAGE_KEY, + INFO_FLASH, + SOCIAL_MEDIA, + HOME_PAGE, + SELECT_AT_LEAST_ONE_SOCIAL_MEDIA, + SUBJECT_KEY, + SUCCESS_MESSAGE, + EMPTY_CONTENT, + CONTENT_KEY, + EMPTY_STRING, + DEFAULT_CONFIG_FILE, + TELEGRAM, + TWITTER, + EMAIL_PAGE, + UPLOAD_FOLDER, + ) + from .tools.post import post_to_facebook, post_to_telegram, post_to_twitter + from .tools.email.email_sc import EmailIntegration, create_email + from .tools.email import extract_csv + from .tools.utils import replace_string # Set up logger logging.basicConfig(