From 4f97180d8a221aa85a46d69d8286c9b37fa40110 Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Mon, 22 Aug 2022 10:53:35 -0300
Subject: [PATCH 01/13] build(setup): :package: Created pypers package setup
---
README.md | 2 +-
pages/examples/newsletter.ipynb | 2 +-
public/template/new page.ipynb | 4 +-
requirements.txt | 94 ++++-----------------------------
setup.py | 21 ++++++++
5 files changed, 34 insertions(+), 89 deletions(-)
create mode 100644 setup.py
diff --git a/README.md b/README.md
index 1421e6a..31edc96 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ See the [Getting started with authentication](https://cloud.google.com/docs/auth
This project has some dependencies (see [`requirements.txt`](requirements.txt)) that are not included in the standard python library, so it is necessary to install them runnung the following command:
```bash
-pip install -r requirements.txt
+pip install .
```
diff --git a/pages/examples/newsletter.ipynb b/pages/examples/newsletter.ipynb
index 5f7fd23..18b6449 100644
--- a/pages/examples/newsletter.ipynb
+++ b/pages/examples/newsletter.ipynb
@@ -1 +1 @@
-{"cells":[{"cell_type":"markdown","metadata":{},"source":["# **News 📰**"]},{"cell_type":"markdown","metadata":{},"source":["## **🔧 Config**"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from src import page\n","from public.python.sheets import Sheets\n","\n","# https://docs.google.com/spreadsheets/d/1gHyAd0czD_clb49oRxC8hjYMl8Mvhgl3kjBG7BqnA78/edit\n","users = Sheets.fetch_table(\n"," \"1gHyAd0czD_clb49oRxC8hjYMl8Mvhgl3kjBG7BqnA78\", \"Subscribers\"\n",")\n","users.email_column = \"email\"\n","users.name_column = \"name\"\n"]},{"cell_type":"markdown","metadata":{},"source":["## **🔠 Props**"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import datetime\n","\n","_now = datetime.datetime.now()\n","date = _now.strftime(f\"%B %d of %Y\")\n","\n","date\n"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from public.python import utils\n","\n","\n","@page.user_prop\n","def first_name(user):\n"," return utils.get_first_name(user[users.name_column])\n"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import ipywidgets as widgets\n","\n","title = widgets.Text(description=\"Title:\", value=\"Lorem ipsum dolor sit amet\")\n","\n","title\n"]},{"cell_type":"markdown","metadata":{},"source":["## **🔍 Preview**"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["page.preview()\n"]},{"cell_type":"markdown","metadata":{},"source":["## **📄 Template**"]},{"cell_type":"code","execution_count":null,"metadata":{"vscode":{"languageId":"html"}},"outputs":[],"source":["%%script html\n","\n","\n","\n","
News 📰\n","
\n"," {{ title }}\n","
\n","\n","
\n"," In metus est, sodales sit amet tellus id, fringilla gravida lorem\n","
\n","
💸 ECONOMICS\n","\n","
\n","\n","
\n"," Mauris volutpat pulvinar nunc, a mattis ex vehicula ac. Pellentesque molestie erat quis lacus porttitor, quis malesuada elit commodo. Mauris vehicula aliquam ligula at consequat. Sed pharetra dolor urna, posuere congue lorem porta a. In hac habitasse platea dictumst.\n","
\n","\n","
\n"," Source: News.\n","
\n","\n","
\n"," To the next, {{ first_name }}! 👋\n","
\n","\n","
\n"," We always arrive at your inbox around 06:09. Some email servers are stubborn and slow… Others are even worse and throw us into spam and/or promotions. Whenever you can't find us in your inbox, look in these two.\n","
\n","\n","
\n","\n","
\n"," Luciano Felix, {{ date }}.\n","
\n","\n","
\n"," News, A newsletter example.\n","
\n"," 200 R. Quatá, São Paulo - SP, 04546-041\n","
\n","\n","
\n"," Unsubscribe\n"," |\n"," Contact us\n","
\n","
\n","\n",""]},{"cell_type":"markdown","metadata":{},"source":["## **🖌️ Style**"]},{"cell_type":"code","execution_count":null,"metadata":{"vscode":{"languageId":"html"}},"outputs":[],"source":["%%script html\n",""]},{"cell_type":"markdown","metadata":{},"source":["## 📧 Sending"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["page.send()\n"]}],"metadata":{"kernelspec":{"display_name":"Python 3.10.4 ('env': venv)","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.4"},"orig_nbformat":4,"vscode":{"interpreter":{"hash":"359d27c1ede3156a31bf43fc0e3aefa4af1cc43923c1239e05b911c5a2f535d7"}}},"nbformat":4,"nbformat_minor":2}
+{"cells":[{"cell_type":"markdown","metadata":{},"source":["# **News 📰**"]},{"cell_type":"markdown","metadata":{},"source":["## **🔧 Config**"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[],"source":["from pypers import page\n","from public.python.sheets import Sheets\n","\n","# https://docs.google.com/spreadsheets/d/1gHyAd0czD_clb49oRxC8hjYMl8Mvhgl3kjBG7BqnA78/edit\n","users = Sheets.fetch_table(\n"," \"1gHyAd0czD_clb49oRxC8hjYMl8Mvhgl3kjBG7BqnA78\", \"Subscribers\"\n",")\n","users.email_column = \"email\"\n","users.name_column = \"name\"\n"]},{"cell_type":"markdown","metadata":{},"source":["## **🔠 Props**"]},{"cell_type":"code","execution_count":2,"metadata":{},"outputs":[{"data":{"text/plain":["'August 22 of 2022'"]},"execution_count":2,"metadata":{},"output_type":"execute_result"}],"source":["import datetime\n","\n","_now = datetime.datetime.now()\n","date = _now.strftime(f\"%B %d of %Y\")\n","\n","date\n"]},{"cell_type":"code","execution_count":3,"metadata":{},"outputs":[],"source":["from public.python import utils\n","\n","\n","@page.user_prop\n","def first_name(user):\n"," return utils.get_first_name(user[users.name_column])\n"]},{"cell_type":"code","execution_count":4,"metadata":{},"outputs":[{"data":{"application/vnd.jupyter.widget-view+json":{"model_id":"5b005c762f51466d8a654afb086f4dce","version_major":2,"version_minor":0},"text/plain":["Text(value='Lorem ipsum dolor sit amet', description='Title:')"]},"execution_count":4,"metadata":{},"output_type":"execute_result"}],"source":["import ipywidgets as widgets\n","\n","title = widgets.Text(description=\"Title:\", value=\"Lorem ipsum dolor sit amet\")\n","\n","title\n"]},{"cell_type":"markdown","metadata":{},"source":["## **🔍 Preview**"]},{"cell_type":"code","execution_count":5,"metadata":{},"outputs":[{"data":{"application/vnd.jupyter.widget-view+json":{"model_id":"4829daab86a74ec082bf7f51352a555d","version_major":2,"version_minor":0},"text/plain":["HBox(children=(Dropdown(description='Preview as:', disabled=True, layout=Layout(flex='1 1 100%'), options=('Lu…"]},"metadata":{},"output_type":"display_data"},{"data":{"text/html":["\n","\n","\n","
\n","
News 📰\n","
\n"," Lorem ipsum dolor sit amet\n","
\n","
\n"," In metus est, sodales sit amet tellus id, fringilla gravida lorem\n","
\n","
💸 ECONOMICS\n","
\n","
\n"," Mauris volutpat pulvinar nunc, a mattis ex vehicula ac. Pellentesque molestie erat quis lacus porttitor, quis malesuada elit commodo. Mauris vehicula aliquam ligula at consequat. Sed pharetra dolor urna, posuere congue lorem porta a. In hac habitasse platea dictumst.\n","
\n","
\n"," Source: News.\n","
\n","
\n"," To the next, Luna! 👋\n","
\n","
\n"," We always arrive at your inbox around 06:09. Some email servers are stubborn and slow… Others are even worse and throw us into spam and/or promotions. Whenever you can't find us in your inbox, look in these two.\n","
\n","
\n","
\n","Luciano Felix, August 22 of 2022.\n","
\n","
\n"," News, A newsletter example.\n","
\n"," 200 R. Quatá, São Paulo - SP, 04546-041\n","
\n","
\n","Unsubscribe\n"," |\n"," Contact us\n","
\n","
\n","
\n"],"text/plain":[""]},"metadata":{},"output_type":"display_data"}],"source":["page.preview()\n"]},{"cell_type":"markdown","metadata":{},"source":["## **📄 Template**"]},{"cell_type":"code","execution_count":6,"metadata":{"vscode":{"languageId":"html"}},"outputs":[{"name":"stdout","output_type":"stream","text":["Couldn't find program: 'html'\n"]}],"source":["%%script html\n","\n","\n","\n","
News 📰\n","
\n"," {{ title }}\n","
\n","\n","
\n"," In metus est, sodales sit amet tellus id, fringilla gravida lorem\n","
\n","
💸 ECONOMICS\n","\n","
\n","\n","
\n"," Mauris volutpat pulvinar nunc, a mattis ex vehicula ac. Pellentesque molestie erat quis lacus porttitor, quis malesuada elit commodo. Mauris vehicula aliquam ligula at consequat. Sed pharetra dolor urna, posuere congue lorem porta a. In hac habitasse platea dictumst.\n","
\n","\n","
\n"," Source: News.\n","
\n","\n","
\n"," To the next, {{ first_name }}! 👋\n","
\n","\n","
\n"," We always arrive at your inbox around 06:09. Some email servers are stubborn and slow… Others are even worse and throw us into spam and/or promotions. Whenever you can't find us in your inbox, look in these two.\n","
\n","\n","
\n","\n","
\n"," Luciano Felix, {{ date }}.\n","
\n","\n","
\n"," News, A newsletter example.\n","
\n"," 200 R. Quatá, São Paulo - SP, 04546-041\n","
\n","\n","
\n"," Unsubscribe\n"," |\n"," Contact us\n","
\n","
\n","\n",""]},{"cell_type":"markdown","metadata":{},"source":["## **🖌️ Style**"]},{"cell_type":"code","execution_count":7,"metadata":{"vscode":{"languageId":"html"}},"outputs":[{"name":"stdout","output_type":"stream","text":["Couldn't find program: 'html'\n"]}],"source":["%%script html\n",""]},{"cell_type":"markdown","metadata":{},"source":["## 📧 Sending"]},{"cell_type":"code","execution_count":8,"metadata":{},"outputs":[{"data":{"application/vnd.jupyter.widget-view+json":{"model_id":"62a0cf38e03044fc89cc492353e974e0","version_major":2,"version_minor":0},"text/plain":["HBox(children=(Output(), VBox(children=(HBox(children=(Dropdown(description='Send as:', layout=Layout(flex='1 …"]},"execution_count":8,"metadata":{},"output_type":"execute_result"}],"source":["page.send()\n"]}],"metadata":{"kernelspec":{"display_name":"Python 3.10.4 ('env': venv)","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.4"},"orig_nbformat":4,"vscode":{"interpreter":{"hash":"06533af5f6072c33c887b7ac5ff0332542541b8cfbdba87749e169dacaf4c9ce"}}},"nbformat":4,"nbformat_minor":2}
diff --git a/public/template/new page.ipynb b/public/template/new page.ipynb
index 3de7270..2791958 100644
--- a/public/template/new page.ipynb
+++ b/public/template/new page.ipynb
@@ -21,7 +21,7 @@
"outputs": [],
"source": [
"import pandas as pd\n",
- "from src import page\n",
+ "from pypers import page\n",
"\n",
"users = pd.read_csv(\"public/data/mailing-list.csv\")\n",
"users.email_column = \"email\"\n",
@@ -171,7 +171,7 @@
"orig_nbformat": 4,
"vscode": {
"interpreter": {
- "hash": "359d27c1ede3156a31bf43fc0e3aefa4af1cc43923c1239e05b911c5a2f535d7"
+ "hash": "06533af5f6072c33c887b7ac5ff0332542541b8cfbdba87749e169dacaf4c9ce"
}
}
},
diff --git a/requirements.txt b/requirements.txt
index 7aff8a2..16cc3ae 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,85 +1,9 @@
-argon2-cffi==21.3.0
-argon2-cffi-bindings==21.2.0
-asttokens==2.0.5
-attrs==21.4.0
-backcall==0.2.0
-beautifulsoup4==4.11.1
-bleach==5.0.1
-cachetools==5.2.0
-certifi==2022.6.15
-cffi==1.15.1
-charset-normalizer==2.1.0
-colorama==0.4.5
-cssutils==2.5.1
-debugpy==1.6.2
-decorator==5.1.1
-defusedxml==0.7.1
-entrypoints==0.4
-executing==0.9.0
-fastjsonschema==2.16.1
-google-api-core==2.8.2
-google-api-python-client==2.54.0
-google-auth==2.9.1
-google-auth-httplib2==0.1.0
-google-auth-oauthlib==0.5.2
-googleapis-common-protos==1.56.4
-httplib2==0.20.4
-idna==3.3
-ipykernel==6.15.1
-ipython==8.4.0
-ipython-genutils==0.2.0
-ipywidgets==7.7.1
-jedi==0.18.1
-Jinja2==3.1.2
-jsonschema==4.7.2
-jupyter-client==7.3.4
-jupyter-core==4.11.1
-jupyterlab-pygments==0.2.2
-jupyterlab-widgets==1.1.1
-MarkupSafe==2.1.1
-matplotlib-inline==0.1.3
-mistune==0.8.4
-nbclient==0.6.6
-nbconvert==6.5.0
-nbformat==5.4.0
-nest-asyncio==1.5.5
-notebook==6.4.12
-numpy==1.23.1
-oauthlib==3.2.0
-packaging==21.3
-pandas==1.4.3
-pandocfilters==1.5.0
-parso==0.8.3
-pickleshare==0.7.5
-prometheus-client==0.14.1
-prompt-toolkit==3.0.30
-protobuf==4.21.3
-psutil==5.9.1
-pure-eval==0.2.2
-pyasn1==0.4.8
-pyasn1-modules==0.2.8
-pycparser==2.21
-Pygments==2.12.0
-pyparsing==3.0.9
-pyrsistent==0.18.1
-python-dateutil==2.8.2
-pytz==2022.1
-pywin32==304
-pywinpty==2.0.6
-pyzmq==23.2.0
-requests==2.28.1
-requests-oauthlib==1.3.1
-rsa==4.9
-Send2Trash==1.8.0
-six==1.16.0
-soupsieve==2.3.2.post1
-stack-data==0.3.0
-terminado==0.15.0
-tinycss2==1.1.1
-tornado==6.2
-traitlets==5.3.0
-uritemplate==4.1.1
-urllib3==1.26.10
-wcwidth==0.2.5
-webencodings==0.5.1
-widgetsnbextension==3.6.1
+google-api-python-client
+google-auth-httplib2
+google-auth-oauthlib
+jinja2
+beautifulsoup4
+cssutils
+pandas
+ipykernel
+ipywidgets
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..9b7b42a
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,21 @@
+from setuptools import setup
+
+
+def parse_requirements(filename):
+ lines = (line.strip() for line in open(filename))
+
+ return [line for line in lines if line and not line.startswith("#")]
+
+
+if __name__ == "__main__":
+ setup(
+ name="Pypers",
+ version="1.0.0",
+ description="Mail templating and sending with Jupyter",
+ url="https://github.com/FelixLuciano/pypers",
+ author="Luciano Felix",
+ packages=["pypers"],
+ package_dir={"pypers": "src"},
+ license="MIT",
+ install_requires=parse_requirements("requirements.txt"),
+ )
From 71aea31a1c245e10186f461e43c8a6a908fb36a5 Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Wed, 24 Aug 2022 10:20:24 -0300
Subject: [PATCH 02/13] feat(Template): :recycle: Standalone template folder
---
public/python/utils.py | 2 --
{.vscode => template/.vscode}/extensions.json | 0
{.vscode => template/.vscode}/settings.json | 0
{.vscode => template/.vscode}/tasks.json | 25 +++----------------
.../pages}/examples/newsletter.ipynb | 0
.../public}/components/hello.j2 | 0
.../public}/data/mailing-list.csv | 0
{public => template/public}/python/sheets.py | 2 +-
template/public/python/utils.py | 24 ++++++++++++++++++
.../public}/style/normalize.css | 0
10 files changed, 28 insertions(+), 25 deletions(-)
delete mode 100644 public/python/utils.py
rename {.vscode => template/.vscode}/extensions.json (100%)
rename {.vscode => template/.vscode}/settings.json (100%)
rename {.vscode => template/.vscode}/tasks.json (53%)
rename {pages => template/pages}/examples/newsletter.ipynb (100%)
rename {public => template/public}/components/hello.j2 (100%)
rename {public => template/public}/data/mailing-list.csv (100%)
rename {public => template/public}/python/sheets.py (96%)
create mode 100644 template/public/python/utils.py
rename {public => template/public}/style/normalize.css (100%)
diff --git a/public/python/utils.py b/public/python/utils.py
deleted file mode 100644
index 72902f0..0000000
--- a/public/python/utils.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def get_first_name(full_name):
- return full_name.split(" ")[0]
diff --git a/.vscode/extensions.json b/template/.vscode/extensions.json
similarity index 100%
rename from .vscode/extensions.json
rename to template/.vscode/extensions.json
diff --git a/.vscode/settings.json b/template/.vscode/settings.json
similarity index 100%
rename from .vscode/settings.json
rename to template/.vscode/settings.json
diff --git a/.vscode/tasks.json b/template/.vscode/tasks.json
similarity index 53%
rename from .vscode/tasks.json
rename to template/.vscode/tasks.json
index f7fba9a..9a4b12a 100644
--- a/.vscode/tasks.json
+++ b/template/.vscode/tasks.json
@@ -11,26 +11,6 @@
}
],
"tasks": [
- {
- "label": "Setup environment",
- "detail": "Setup project environsment and install dependencies (Run once!)",
- "icon": {
- "id": "package"
- },
- "options": {
- "statusbar": {
- "backgroundColor": "statusBarItem.warningBackground",
- "filePattern": "README.md"
- }
- },
- "args": [
- "src",
- "setup"
- ],
- "presentation": {
- "close": true
- }
- },
{
"label": "New page",
"detail": "Create a new page.",
@@ -38,9 +18,10 @@
"id": "file-add"
},
"args": [
- "src",
+ "-m",
+ "pypers",
"create",
- "${input:pageLocation}"
+ "pages${pathSeparator}${input:pageLocation}"
],
"presentation": {
"reveal": "never",
diff --git a/pages/examples/newsletter.ipynb b/template/pages/examples/newsletter.ipynb
similarity index 100%
rename from pages/examples/newsletter.ipynb
rename to template/pages/examples/newsletter.ipynb
diff --git a/public/components/hello.j2 b/template/public/components/hello.j2
similarity index 100%
rename from public/components/hello.j2
rename to template/public/components/hello.j2
diff --git a/public/data/mailing-list.csv b/template/public/data/mailing-list.csv
similarity index 100%
rename from public/data/mailing-list.csv
rename to template/public/data/mailing-list.csv
diff --git a/public/python/sheets.py b/template/public/python/sheets.py
similarity index 96%
rename from public/python/sheets.py
rename to template/public/python/sheets.py
index 9f39f8d..53b9395 100644
--- a/public/python/sheets.py
+++ b/template/public/python/sheets.py
@@ -2,7 +2,7 @@
import pandas as pd
from googleapiclient.discovery import build
-from src import google
+from pypers import google
google.SCOPES.append("https://www.googleapis.com/auth/spreadsheets.readonly")
diff --git a/template/public/python/utils.py b/template/public/python/utils.py
new file mode 100644
index 0000000..6bb8f6b
--- /dev/null
+++ b/template/public/python/utils.py
@@ -0,0 +1,24 @@
+def get_first_name(full_name):
+ return full_name.split(" ")[0]
+
+
+def find_row(series, col, value):
+ for i, row in series.iterrows():
+ if row[col] == value:
+ return i
+
+ return -1
+
+
+def date_diff(base, diff, value_col, date_col):
+ filter_ = []
+
+ for _, row in base.iterrows():
+ index = find_row(diff, value_col, row[value_col])
+
+ if index > 0:
+ filter_.append(row[date_col] > diff.loc[index][date_col])
+ else:
+ filter_.append(True)
+
+ return base[filter_]
diff --git a/public/style/normalize.css b/template/public/style/normalize.css
similarity index 100%
rename from public/style/normalize.css
rename to template/public/style/normalize.css
From 1de26992201c8282a2ef40b15cc2a916690fd19a Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Wed, 24 Aug 2022 10:21:27 -0300
Subject: [PATCH 03/13] fix: :bug: Workspace check
---
src/Workspace.py | 6 ++----
src/__init__.py | 31 +++++++++++++++----------------
2 files changed, 17 insertions(+), 20 deletions(-)
diff --git a/src/Workspace.py b/src/Workspace.py
index fa82ea8..3679c7c 100644
--- a/src/Workspace.py
+++ b/src/Workspace.py
@@ -17,15 +17,13 @@ class Workspace:
)
@staticmethod
- def check_vsc_ipynb_file():
+ def get_ipynb_file():
scope = vars(__main__)
if "__vsc_ipynb_file__" not in scope:
raise Exception("Pypers only work at VS Code Jupyter!")
- @staticmethod
- def get_ipynb_file():
- return Path(vars(__main__)["__vsc_ipynb_file__"])
+ return scope["__vsc_ipynb_file__"]
@staticmethod
def get_ipynb():
diff --git a/src/__init__.py b/src/__init__.py
index 832fa6d..ccc3564 100644
--- a/src/__init__.py
+++ b/src/__init__.py
@@ -1,22 +1,21 @@
-import logging
-import warnings
+if __name__ != "__main__":
+ import logging
+ import warnings
-import cssutils
+ import cssutils
-from .Create import Create as create
-from .Google import Google as google
-from .Page import Page as page
-from .Preview import Preview as preview
-from .Send import Send as send
-from .Workspace import Workspace as workspace
+ from .Create import Create as create
+ from .Google import Google as google
+ from .Page import Page as page
+ from .Preview import Preview as preview
+ from .Send import Send as send
+ from .Workspace import Workspace as workspace
-workspace.check_vsc_ipynb_file()
+ warnings.simplefilter(action='ignore')
-warnings.simplefilter(action='ignore')
+ cssutils.ser.prefs.keepComments = False
+ cssutils.ser.prefs.lineSeparator = ""
+ cssutils.ser.prefs.propertyNameSpacer = ""
-cssutils.ser.prefs.keepComments = False
-cssutils.ser.prefs.lineSeparator = ""
-cssutils.ser.prefs.propertyNameSpacer = ""
-
-cssutils.log.setLevel(logging.CRITICAL)
+ cssutils.log.setLevel(logging.CRITICAL)
From f6e1437d30d418269ff6a56bab69af1d32479177 Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Wed, 24 Aug 2022 10:22:27 -0300
Subject: [PATCH 04/13] refactor(setup): :fire: Removed setup script
---
src/__main__.py | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/src/__main__.py b/src/__main__.py
index 378bc38..56253d5 100644
--- a/src/__main__.py
+++ b/src/__main__.py
@@ -4,17 +4,10 @@
def main(args):
- if args.action == "setup":
- import subprocess
- import venv
-
- venv.create("env", with_pip=True)
- subprocess.run([Path("env", "Scripts", "pip"), "-r", "requirements.txt"])
-
- elif args.action == "create":
+ if args.action == "create":
from os import startfile
- from Create import Create
+ from .Create import Create
new_page = Create(args.dest)
@@ -27,7 +20,6 @@ def main(args):
def parse_arguments():
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(dest="action")
- setup = subparser.add_parser("setup")
create = subparser.add_parser("create")
create.add_argument(
From 1b71fd3356488b751987c31840b6956f2751ed9b Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Wed, 24 Aug 2022 10:23:32 -0300
Subject: [PATCH 05/13] refactor(credentials): :rewind: Folder location
---
src/Google.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Google.py b/src/Google.py
index a75f0bb..7471d99 100644
--- a/src/Google.py
+++ b/src/Google.py
@@ -20,8 +20,8 @@ class Google:
@cache
@staticmethod
def authenticate(_is_retry=False):
- credentials_file = Path("env", "credentials.json")
- token_file = Path("env", "token.json")
+ credentials_file = Path("credentials.json")
+ token_file = Path("token.json")
if token_file.exists():
Google.credentials = Credentials.from_authorized_user_file(
From c6e9038dd9420ad8c487e5ae9e1240a53502edc7 Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Wed, 24 Aug 2022 10:24:43 -0300
Subject: [PATCH 06/13] feat(package): :sparkles: Package data
---
setup.py | 1 +
src/Create.py | 4 ++--
src/Preview.py | 18 +++++++++++++-----
{public/template => src/data}/new page.ipynb | 0
{public/template => src/data}/preview.html | 0
5 files changed, 16 insertions(+), 7 deletions(-)
rename {public/template => src/data}/new page.ipynb (100%)
rename {public/template => src/data}/preview.html (100%)
diff --git a/setup.py b/setup.py
index 9b7b42a..93d3dc6 100644
--- a/setup.py
+++ b/setup.py
@@ -16,6 +16,7 @@ def parse_requirements(filename):
author="Luciano Felix",
packages=["pypers"],
package_dir={"pypers": "src"},
+ package_data={"pypers": ["data/*"]},
license="MIT",
install_requires=parse_requirements("requirements.txt"),
)
diff --git a/src/Create.py b/src/Create.py
index 1786920..da508a8 100644
--- a/src/Create.py
+++ b/src/Create.py
@@ -3,8 +3,8 @@
class Create:
- BASEDIR = Path("pages")
- TEMPLATE_FILENAME = Path("public", "template", "New Page.ipynb")
+ BASEDIR = Path.cwd()
+ TEMPLATE_FILENAME = Path(__file__).parent.joinpath("data", "New Page.ipynb")
def __init__(self, filename: Path):
self.filename = self.BASEDIR.joinpath(filename).with_suffix(
diff --git a/src/Preview.py b/src/Preview.py
index 059cff5..eaa406e 100644
--- a/src/Preview.py
+++ b/src/Preview.py
@@ -1,3 +1,5 @@
+from pathlib import Path
+
import ipywidgets as widgets
from bs4 import BeautifulSoup
from IPython.display import HTML, clear_output, display
@@ -11,10 +13,9 @@ class Preview:
@staticmethod
def render(page, user):
content = BeautifulSoup(page.render(user), "html.parser")
+ template = Path(__file__).parent.joinpath("data", "preview.html")
- with open(
- "public/template/preview.html", "r", encoding="utf-8"
- ) as template_file:
+ with open(template, "r", encoding="utf-8") as template_file:
template = BeautifulSoup(template_file, "html.parser")
anchor = template.select_one("page-preview")
@@ -29,9 +30,16 @@ def display(page):
if hasattr(users, "name_column"):
if isinstance(users.name_column, str):
- mails = users[users.name_column] + " <" + users[users.email_column] + ">"
+ mails = (
+ users[users.name_column] + " <" + users[users.email_column] + ">"
+ )
else:
- mails = users.loc[:, users.name_column].apply(' - '.join, 1) + " <" + users[users.email_column] + ">"
+ mails = (
+ users.loc[:, users.name_column].apply(" - ".join, 1)
+ + " <"
+ + users[users.email_column]
+ + ">"
+ )
else:
mails = users[users.email_column]
diff --git a/public/template/new page.ipynb b/src/data/new page.ipynb
similarity index 100%
rename from public/template/new page.ipynb
rename to src/data/new page.ipynb
diff --git a/public/template/preview.html b/src/data/preview.html
similarity index 100%
rename from public/template/preview.html
rename to src/data/preview.html
From 7f399adbe9dd18367dcd9c6ff116cf754a620c9f Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Wed, 24 Aug 2022 10:28:30 -0300
Subject: [PATCH 07/13] docs(README): :memo: Install command and example image
---
README.md | 12 +++++++++---
{public => assets}/image/example.jpg | Bin
2 files changed, 9 insertions(+), 3 deletions(-)
rename {public => assets}/image/example.jpg (100%)
diff --git a/README.md b/README.md
index 31edc96..20a76f8 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
Mail templating and sending with Jupyter
-
+
@@ -21,10 +21,16 @@ See the [Getting started with authentication](https://cloud.google.com/docs/auth
## Install dependencies
-This project has some dependencies (see [`requirements.txt`](requirements.txt)) that are not included in the standard python library, so it is necessary to install them runnung the following command:
+This project has some dependencies (see [`requirements.txt`](requirements.txt)) that are not included in the standard python library, so after cloning this template, it is necessary to install them in an environment running the following command:
```bash
-pip install .
+pip install https://github.com/FelixLuciano/pypers/archive/refs/heads/main.tar.gz
+```
+
+You can also [download the latest version](https://github.com/FelixLuciano/pypers/archive/main.tar.gz) of the package and install it as a module by following:
+
+```bash
+pip install ./path-to/pypers-main.tar.gz
```
diff --git a/public/image/example.jpg b/assets/image/example.jpg
similarity index 100%
rename from public/image/example.jpg
rename to assets/image/example.jpg
From 6f442815fd6ebda73a54222feb22f748cf5c4848 Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Wed, 24 Aug 2022 10:46:56 -0300
Subject: [PATCH 08/13] feat(template): :see_no_evil: specific .gitignore
---
.gitignore | 8 ---
template/.gitignore | 168 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 168 insertions(+), 8 deletions(-)
create mode 100644 template/.gitignore
diff --git a/.gitignore b/.gitignore
index b7bb3c9..68bc17f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -158,11 +158,3 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
-
-# Google API oauth2 client credentials secrets
-# Sensitive file! It must never be exposed.
-/credentials.json
-
-# Google API authorized user file
-# Sensitive file! It must never be exposed.
-/token.json
diff --git a/template/.gitignore b/template/.gitignore
new file mode 100644
index 0000000..b7bb3c9
--- /dev/null
+++ b/template/.gitignore
@@ -0,0 +1,168 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
+# Google API oauth2 client credentials secrets
+# Sensitive file! It must never be exposed.
+/credentials.json
+
+# Google API authorized user file
+# Sensitive file! It must never be exposed.
+/token.json
From 4641daf80fbb906e0c4a5cd095e825eadcff60a7 Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Wed, 24 Aug 2022 10:53:13 -0300
Subject: [PATCH 09/13] feat(UX): :sparkles: Print file
---
src/Create.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/Create.py b/src/Create.py
index da508a8..bbb4ac9 100644
--- a/src/Create.py
+++ b/src/Create.py
@@ -27,3 +27,5 @@ def create_file(self):
with open(self.filename, "w", encoding="utf-8") as page_file:
json.dump(template, page_file)
+
+ print(f"Created {self.filename.absolute()}")
From 976a806d20a7f4cd4617c47fdedc6e5ec76f2d33 Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Wed, 24 Aug 2022 11:32:20 -0300
Subject: [PATCH 10/13] docs: :memo: Shorter repo URL
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 20a76f8..08f86ae 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ See the [Getting started with authentication](https://cloud.google.com/docs/auth
This project has some dependencies (see [`requirements.txt`](requirements.txt)) that are not included in the standard python library, so after cloning this template, it is necessary to install them in an environment running the following command:
```bash
-pip install https://github.com/FelixLuciano/pypers/archive/refs/heads/main.tar.gz
+pip install https://github.com/FelixLuciano/pypers/archive/main.tar.gz
```
You can also [download the latest version](https://github.com/FelixLuciano/pypers/archive/main.tar.gz) of the package and install it as a module by following:
From 840bc5511734b829ecdef28d48f5197255193b8e Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Mon, 29 Aug 2022 08:21:36 -0300
Subject: [PATCH 11/13] refactor: :coffin: Remove unused code
---
src/Workspace.py | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/src/Workspace.py b/src/Workspace.py
index 3679c7c..11e174a 100644
--- a/src/Workspace.py
+++ b/src/Workspace.py
@@ -6,16 +6,6 @@
class Workspace:
- HIDDEN_FILES = (
- "**/env",
- "**/src",
- "**/.vscode",
- "**/public/image",
- "**/public/template",
- ".gitignore",
- "requirements.txt",
- )
-
@staticmethod
def get_ipynb_file():
scope = vars(__main__)
@@ -41,6 +31,6 @@ def get_html_source():
cell_source = cell["source"]
if len(cell_source) > 1 and cell_source[0] == "%%script html\n":
- source.extend(cell["source"][1:])
+ source.extend(cell_source[1:])
return "".join(source)
From 557f3cb56cc3ea9b27833e45cd7c6feaa3bb4520 Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Mon, 29 Aug 2022 08:25:04 -0300
Subject: [PATCH 12/13] refactor: :art: Format
---
src/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/__init__.py b/src/__init__.py
index ccc3564..7e56619 100644
--- a/src/__init__.py
+++ b/src/__init__.py
@@ -12,7 +12,7 @@
from .Workspace import Workspace as workspace
- warnings.simplefilter(action='ignore')
+ warnings.simplefilter(action="ignore")
cssutils.ser.prefs.keepComments = False
cssutils.ser.prefs.lineSeparator = ""
From 12ab2f1e7f390075018090cb7266f509336d1efd Mon Sep 17 00:00:00 2001
From: FelixLuciano
Date: Mon, 29 Aug 2022 08:59:48 -0300
Subject: [PATCH 13/13] docs(README): :memo: Update install instructions
---
README.md | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 08f86ae..a89e46f 100644
--- a/README.md
+++ b/README.md
@@ -19,18 +19,22 @@ In order to send the pages by Gmail or fetch data from Google Sheets, Google Clo
See the [Getting started with authentication](https://cloud.google.com/docs/authentication/getting-started) for Google Cloud API. Then put your key at `[PROJECT FOLDER]/credentials.json`. and it's ready to use!
-## Install dependencies
+## Install
-This project has some dependencies (see [`requirements.txt`](requirements.txt)) that are not included in the standard python library, so after cloning this template, it is necessary to install them in an environment running the following command:
+```bash
+pip install https://github.com/FelixLuciano/pypers/archive/refs/tags/1.0.0.tar.gz
+```
+
+You can also [download the latest version](https://github.com/FelixLuciano/pypers/archive/main.tar.gz) (but not recommended) of the package and install it locally. Or directly:
```bash
pip install https://github.com/FelixLuciano/pypers/archive/main.tar.gz
```
-You can also [download the latest version](https://github.com/FelixLuciano/pypers/archive/main.tar.gz) of the package and install it as a module by following:
+## Create a new page
```bash
-pip install ./path-to/pypers-main.tar.gz
+python -m pypers create [path/to/page]
```