platzky 0.2.5__tar.gz → 0.2.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {platzky-0.2.5 → platzky-0.2.6}/PKG-INFO +1 -1
  2. {platzky-0.2.5 → platzky-0.2.6}/platzky/blog/blog.py +1 -3
  3. {platzky-0.2.5 → platzky-0.2.6}/platzky/config.py +2 -1
  4. {platzky-0.2.5 → platzky-0.2.6}/platzky/db/db.py +12 -8
  5. {platzky-0.2.5 → platzky-0.2.6}/platzky/db/google_json_db.py +1 -3
  6. {platzky-0.2.5 → platzky-0.2.6}/platzky/db/graph_ql_db.py +38 -12
  7. {platzky-0.2.5 → platzky-0.2.6}/platzky/db/json_db.py +6 -7
  8. {platzky-0.2.5 → platzky-0.2.6}/platzky/models.py +3 -2
  9. {platzky-0.2.5 → platzky-0.2.6}/platzky/platzky.py +1 -6
  10. {platzky-0.2.5 → platzky-0.2.6}/platzky/plugins/redirections/entrypoint.py +4 -8
  11. {platzky-0.2.5 → platzky-0.2.6}/platzky/plugins/sendmail/entrypoint.py +2 -6
  12. {platzky-0.2.5 → platzky-0.2.6}/platzky/seo/seo.py +4 -9
  13. {platzky-0.2.5 → platzky-0.2.6}/platzky/templates/base.html +2 -8
  14. {platzky-0.2.5 → platzky-0.2.6}/platzky/templates/head_meta.html +1 -2
  15. platzky-0.2.6/pyproject.toml +87 -0
  16. platzky-0.2.5/pyproject.toml +0 -47
  17. {platzky-0.2.5 → platzky-0.2.6}/README.md +0 -0
  18. {platzky-0.2.5 → platzky-0.2.6}/platzky/__init__.py +1 -1
  19. {platzky-0.2.5 → platzky-0.2.6}/platzky/blog/__init__.py +0 -0
  20. {platzky-0.2.5 → platzky-0.2.6}/platzky/blog/comment_form.py +0 -0
  21. {platzky-0.2.5 → platzky-0.2.6}/platzky/db/__init__.py +0 -0
  22. {platzky-0.2.5 → platzky-0.2.6}/platzky/db/db_loader.py +0 -0
  23. {platzky-0.2.5 → platzky-0.2.6}/platzky/db/json_file_db.py +0 -0
  24. {platzky-0.2.5 → platzky-0.2.6}/platzky/plugin_loader.py +0 -0
  25. {platzky-0.2.5 → platzky-0.2.6}/platzky/plugins/google-tag-manager/entrypoint.py +0 -0
  26. {platzky-0.2.5 → platzky-0.2.6}/platzky/static/blog.css +0 -0
  27. {platzky-0.2.5 → platzky-0.2.6}/platzky/templates/404.html +0 -0
  28. {platzky-0.2.5 → platzky-0.2.6}/platzky/templates/blog.html +0 -0
  29. {platzky-0.2.5 → platzky-0.2.6}/platzky/templates/body_meta.html +0 -0
  30. {platzky-0.2.5 → platzky-0.2.6}/platzky/templates/feed.xml +0 -0
  31. {platzky-0.2.5 → platzky-0.2.6}/platzky/templates/home.html +0 -0
  32. {platzky-0.2.5 → platzky-0.2.6}/platzky/templates/page.html +0 -0
  33. {platzky-0.2.5 → platzky-0.2.6}/platzky/templates/post.html +0 -0
  34. {platzky-0.2.5 → platzky-0.2.6}/platzky/templates/robots.txt +0 -0
  35. {platzky-0.2.5 → platzky-0.2.6}/platzky/templates/sitemap.xml +0 -0
  36. {platzky-0.2.5 → platzky-0.2.6}/platzky/www_handler.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: platzky
3
- Version: 0.2.5
3
+ Version: 0.2.6
4
4
  Summary: Not only blog engine
5
5
  License: MIT
6
6
  Requires-Python: >=3.10,<4.0
@@ -35,9 +35,7 @@ def create_blog_blueprint(db, blog_prefix: str, locale_func):
35
35
  @blog.route("/feed", methods=["GET"])
36
36
  def get_feed():
37
37
  lang = locale_func()
38
- response = make_response(
39
- render_template("feed.xml", posts=db.get_all_posts(lang))
40
- )
38
+ response = make_response(render_template("feed.xml", posts=db.get_all_posts(lang)))
41
39
  response.headers["Content-Type"] = "application/xml"
42
40
  return response
43
41
 
@@ -1,7 +1,8 @@
1
1
  import sys
2
2
  import typing as t
3
+
3
4
  import yaml
4
- from pydantic import ConfigDict, BaseModel, Field
5
+ from pydantic import BaseModel, ConfigDict, Field
5
6
 
6
7
  from .db.db import DBConfig
7
8
  from .db.db_loader import get_db_module
@@ -1,10 +1,10 @@
1
+ from abc import ABC, abstractmethod
1
2
  from functools import partial
2
3
  from typing import Any, Callable
3
4
 
4
5
  from pydantic import BaseModel, Field
5
6
 
6
- from abc import abstractmethod, ABC
7
- from ..models import MenuItem, Post, Page, Color
7
+ from ..models import Color, MenuItem, Page, Post
8
8
 
9
9
 
10
10
  class DB(ABC):
@@ -28,7 +28,7 @@ class DB(ABC):
28
28
  f"Method {name} defined in {cls.__name__} does not exist in superclasses"
29
29
  )
30
30
 
31
- def extend(self, function_name: str, function: Callable) -> None:
31
+ def extend(self, function_name: str, function: Callable[..., Any]) -> None:
32
32
  """
33
33
  Add a function to the DB object. The function must take the DB object as first parameter.
34
34
 
@@ -37,9 +37,7 @@ class DB(ABC):
37
37
  function (Callable): The function to add to the DB object.
38
38
  """
39
39
  if not callable(function):
40
- raise ValueError(
41
- f"The provided func for '{function_name}' is not callable."
42
- )
40
+ raise ValueError(f"The provided func for '{function_name}' is not callable.")
43
41
  try:
44
42
  bound_function = partial(function, self)
45
43
  setattr(self, function_name, bound_function)
@@ -71,7 +69,13 @@ class DB(ABC):
71
69
  pass
72
70
 
73
71
  @abstractmethod
74
- def get_logo_url(self) -> str:
72
+ def get_logo_url(
73
+ self,
74
+ ) -> str: # TODO provide alternative text along with the URL of logo
75
+ pass
76
+
77
+ @abstractmethod
78
+ def get_favicon_url(self) -> str:
75
79
  pass
76
80
 
77
81
  @abstractmethod
@@ -83,7 +87,7 @@ class DB(ABC):
83
87
  pass
84
88
 
85
89
  @abstractmethod
86
- def get_plugins_data(self) -> list:
90
+ def get_plugins_data(self) -> list[Any]:
87
91
  pass
88
92
 
89
93
  @abstractmethod
@@ -22,9 +22,7 @@ def db_from_config(config: GoogleJsonDbConfig):
22
22
 
23
23
  def get_db(config):
24
24
  google_json_db_config = GoogleJsonDbConfig.model_validate(config)
25
- return GoogleJsonDb(
26
- google_json_db_config.bucket_name, google_json_db_config.source_blob_name
27
- )
25
+ return GoogleJsonDb(google_json_db_config.bucket_name, google_json_db_config.source_blob_name)
28
26
 
29
27
 
30
28
  def get_blob(bucket_name, source_blob_name):
@@ -5,8 +5,8 @@ from gql import Client, gql
5
5
  from gql.transport.aiohttp import AIOHTTPTransport
6
6
  from pydantic import Field
7
7
 
8
- from .db import DB, DBConfig
9
8
  from ..models import Color, Post
9
+ from .db import DB, DBConfig
10
10
 
11
11
 
12
12
  def db_config_type():
@@ -58,9 +58,7 @@ class GraphQL(DB):
58
58
  self.module_name = "graph_ql_db"
59
59
  self.db_name = "GraphQLDb"
60
60
  full_token = "bearer " + token
61
- transport = AIOHTTPTransport(
62
- url=endpoint, headers={"Authorization": full_token}
63
- )
61
+ transport = AIOHTTPTransport(url=endpoint, headers={"Authorization": full_token})
64
62
  self.client = Client(transport=transport)
65
63
  super().__init__()
66
64
 
@@ -97,9 +95,7 @@ class GraphQL(DB):
97
95
  }
98
96
  """
99
97
  )
100
- raw_ql_posts = self.client.execute(all_posts, variable_values={"lang": lang})[
101
- "posts"
102
- ]
98
+ raw_ql_posts = self.client.execute(all_posts, variable_values={"lang": lang})["posts"]
103
99
 
104
100
  return [Post.model_validate(_standarize_post(post)) for post in raw_ql_posts]
105
101
 
@@ -127,7 +123,7 @@ class GraphQL(DB):
127
123
  slug
128
124
  author {
129
125
  name
130
- }
126
+ }
131
127
  contentInRichText {
132
128
  markdown
133
129
  html
@@ -191,9 +187,7 @@ class GraphQL(DB):
191
187
  }
192
188
  """
193
189
  )
194
- return self.client.execute(post, variable_values={"tag": tag, "lang": lang})[
195
- "posts"
196
- ]
190
+ return self.client.execute(post, variable_values={"tag": tag, "lang": lang})["posts"]
197
191
 
198
192
  def add_comment(self, author_name, comment, post_slug):
199
193
  add_comment = gql(
@@ -224,7 +218,39 @@ class GraphQL(DB):
224
218
  return str("")
225
219
 
226
220
  def get_logo_url(self):
227
- return ""
221
+ logo = gql(
222
+ """
223
+ query myquery {
224
+ logos(stage: PUBLISHED) {
225
+ logo {
226
+ alternateText
227
+ image {
228
+ url
229
+ }
230
+ }
231
+ }
232
+ }
233
+ """
234
+ )
235
+ try:
236
+ return self.client.execute(logo)["logos"][0]["logo"]["image"]["url"]
237
+ except IndexError:
238
+ return ""
239
+
240
+ def get_favicon_url(self):
241
+ favicon = gql(
242
+ """
243
+ query myquery {
244
+ favicons(stage: PUBLISHED) {
245
+ favicon {
246
+ url
247
+ }
248
+ }
249
+ }
250
+ """
251
+ )
252
+
253
+ return self.client.execute(favicon)["favicons"][0]["favicon"]["url"]
228
254
 
229
255
  def get_primary_color(self) -> Color:
230
256
  return Color()
@@ -3,8 +3,8 @@ from typing import Any, Dict
3
3
 
4
4
  from pydantic import Field
5
5
 
6
- from .db import DB, DBConfig
7
6
  from ..models import MenuItem, Post
7
+ from .db import DB, DBConfig
8
8
 
9
9
 
10
10
  def db_config_type():
@@ -51,9 +51,7 @@ class Json(DB):
51
51
  # TODO: add test for non-existing page
52
52
  def get_page(self, slug):
53
53
  list_of_pages = (
54
- page
55
- for page in self._get_site_content().get("pages")
56
- if page["slug"] == slug
54
+ page for page in self._get_site_content().get("pages") if page["slug"] == slug
57
55
  )
58
56
  page = Post.model_validate(next(list_of_pages))
59
57
  return page
@@ -64,9 +62,7 @@ class Json(DB):
64
62
  return menu_items_list
65
63
 
66
64
  def get_posts_by_tag(self, tag, lang):
67
- return (
68
- post for post in self._get_site_content()["posts"] if tag in post["tags"]
69
- )
65
+ return (post for post in self._get_site_content()["posts"] if tag in post["tags"])
70
66
 
71
67
  def _get_site_content(self):
72
68
  content = self.data.get("site_content")
@@ -77,6 +73,9 @@ class Json(DB):
77
73
  def get_logo_url(self):
78
74
  return self._get_site_content().get("logo_url", "")
79
75
 
76
+ def get_favicon_url(self):
77
+ return self._get_site_content().get("favicon_url", "")
78
+
80
79
  def get_font(self) -> str:
81
80
  return self._get_site_content().get("font", "")
82
81
 
@@ -1,6 +1,7 @@
1
- from pydantic import BaseModel
2
1
  import datetime
2
+
3
3
  import humanize
4
+ from pydantic import BaseModel
4
5
 
5
6
 
6
7
  class Image(BaseModel):
@@ -40,7 +41,7 @@ class Post(BaseModel):
40
41
  def __lt__(self, other):
41
42
  if isinstance(other, Post):
42
43
  return self.date < other.date
43
- return NotImplemented
44
+ raise NotImplementedError("Posts can only be compared with other posts")
44
45
 
45
46
 
46
47
  Page = Post
@@ -87,12 +87,6 @@ def create_engine(config: Config, db) -> Engine:
87
87
  session["language"] = lang
88
88
  return redirect(request.referrer)
89
89
 
90
- @app.route("/logo", methods=["GET"])
91
- def logo():
92
- return redirect(
93
- "https://www.problematy.pl/wp-content/uploads/2023/08/kolor_poziom.png"
94
- )
95
-
96
90
  def url_link(x: str) -> str:
97
91
  return urllib.parse.quote(x, safe="")
98
92
 
@@ -108,6 +102,7 @@ def create_engine(config: Config, db) -> Engine:
108
102
  "url_link": url_link,
109
103
  "menu_items": app.db.get_menu_items(),
110
104
  "logo_url": app.db.get_logo_url(),
105
+ "favicon_url": app.db.get_favicon_url(),
111
106
  "font": app.db.get_font(),
112
107
  "primary_color": app.db.get_primary_color(),
113
108
  "secondary_color": app.db.get_secondary_color(),
@@ -27,8 +27,7 @@ def graph_ql_db_get_redirections(self):
27
27
  """
28
28
  )
29
29
  return {
30
- x["source"]: x["destination"]
31
- for x in self.client.execute(redirections)["redirections"]
30
+ x["source"]: x["destination"] for x in self.client.execute(redirections)["redirections"]
32
31
  }
33
32
 
34
33
 
@@ -37,11 +36,8 @@ class Redirection(BaseModel):
37
36
  destiny: str
38
37
 
39
38
 
40
- def parse_redirections(config: dict) -> list[Redirection]:
41
- return [
42
- Redirection(source=source, destiny=destiny)
43
- for source, destiny in config.items()
44
- ]
39
+ def parse_redirections(config: dict[str, str]) -> list[Redirection]:
40
+ return [Redirection(source=source, destiny=destiny) for source, destiny in config.items()]
45
41
 
46
42
 
47
43
  def setup_routes(app, redirections):
@@ -62,7 +58,7 @@ def redirect_with_name(destiny, code, name):
62
58
  return named_redirect
63
59
 
64
60
 
65
- def process(app, config: dict) -> object:
61
+ def process(app, config: dict[str, str]) -> object:
66
62
  redirections = parse_redirections(config)
67
63
  setup_routes(app, redirections)
68
64
  return app
@@ -3,12 +3,8 @@ import smtplib
3
3
  from pydantic import BaseModel, Field
4
4
 
5
5
 
6
- def send_mail(
7
- sender_email, password, smtp_server, port, receiver_email, subject, message
8
- ):
9
- full_message = (
10
- f"From: {sender_email}\nTo: {receiver_email}\nSubject: {subject}\n\n{message}"
11
- )
6
+ def send_mail(sender_email, password, smtp_server, port, receiver_email, subject, message):
7
+ full_message = f"From: {sender_email}\nTo: {receiver_email}\nSubject: {subject}\n\n{message}"
12
8
  server = smtplib.SMTP_SSL(smtp_server, port)
13
9
  server.ehlo()
14
10
  server.login(sender_email, password)
@@ -1,6 +1,7 @@
1
1
  import typing as t
2
2
  import urllib.parse
3
3
  from os.path import dirname
4
+
4
5
  from flask import Blueprint, current_app, make_response, render_template, request
5
6
 
6
7
 
@@ -14,9 +15,7 @@ def create_seo_blueprint(db, config: dict[str, t.Any]):
14
15
 
15
16
  @seo.route("/robots.txt")
16
17
  def robots():
17
- robots_response = render_template(
18
- "robots.txt", domain=request.host, mimetype="text/plain"
19
- )
18
+ robots_response = render_template("robots.txt", domain=request.host, mimetype="text/plain")
20
19
  response = make_response(robots_response)
21
20
  response.headers["Content-Type"] = "text/plain"
22
21
  return response
@@ -45,12 +44,8 @@ def create_seo_blueprint(db, config: dict[str, t.Any]):
45
44
  static_urls = list()
46
45
  for rule in current_app.url_map.iter_rules():
47
46
  if not str(rule).startswith("/admin") and not str(rule).startswith("/user"):
48
- if (
49
- rule.methods is not None
50
- and "GET" in rule.methods
51
- and len(rule.arguments) == 0
52
- ):
53
- url = {"loc": f"{host_base}{str(rule)}"}
47
+ if rule.methods is not None and "GET" in rule.methods and len(rule.arguments) == 0:
48
+ url = {"loc": f"{host_base}{rule!s}"}
54
49
  static_urls.append(url)
55
50
 
56
51
  # Dynamic routes with dynamic content
@@ -11,7 +11,7 @@
11
11
  <link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css" />
12
12
  <link rel="stylesheet" href="{{ font.url }}">
13
13
  {% endblock %}
14
- <title>{% block title %} {{app_name}} {% endblock %}</title>
14
+ <title>{% block title %}{{app_name}}{% endblock %}</title>
15
15
  <meta name="description" content=" {% block description %} {% endblock %} ">
16
16
  <style>
17
17
  html,
@@ -98,13 +98,7 @@
98
98
  <i class="fas fa-sliders-h"></i>
99
99
  </button>
100
100
  {% endif %}
101
- <a class="navbar-brand" href="/">
102
- {% if logo_url %}
103
- <img src="{{ logo_url }}" class="logo" >
104
- {% else %}
105
- {{ _(app_name) }}
106
- {% endif %}
107
- </a>
101
+ <a class="navbar-brand" href="/">{% if logo_url %}<img src="{{ logo_url }}" class="logo">{% else %}{{_(app_name)}}{% endif %}</a>
108
102
  <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
109
103
  aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
110
104
  <i class="fas fa-bars"></i>
@@ -2,8 +2,7 @@
2
2
  <meta charset="utf-8">
3
3
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
4
4
  <link rel="alternate" hreflang="{{ current_language }}" href="{{ request.base_url }}"/>
5
- <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.png') }}" type="image/x-icon">
6
-
5
+ <link rel="shortcut icon" href="{{ favicon_url }}" type="image/x-icon">
7
6
  <link rel="canonical" href="{{ request.base_url }}"/>
8
7
 
9
8
  <!-- Bootstrap CSS -->
@@ -0,0 +1,87 @@
1
+ [tool.poetry]
2
+ name = "platzky"
3
+ version = "0.2.6"
4
+ description = "Not only blog engine"
5
+ authors = []
6
+ license = "MIT"
7
+ readme = "README.md"
8
+
9
+ [tool.poetry.dependencies]
10
+ python = "^3.10"
11
+ Flask = "3.0.3"
12
+ PyYAML = "^6.0"
13
+ Flask-Babel = "^4.0.0"
14
+ Flask-WTF = "^1.2.1"
15
+ gql = "^3.4.0"
16
+ aiohttp = "^3.9.5"
17
+ Flask-Minify = "^0.42"
18
+ google-cloud-storage = "^2.5.0"
19
+ humanize = "^4.9.0"
20
+ pydantic = "^2.7.1"
21
+
22
+ [tool.poetry.group.dev.dependencies]
23
+ pytest = "^8.2.1"
24
+ coverage = "^7.5.1"
25
+ pytest-cov = "^5.0.0"
26
+ freezegun = "^1.2.2"
27
+ black = "^24.8.0"
28
+ ruff = "^0.4.4"
29
+ pyright = "^1.1.364"
30
+ beautifulsoup4 = "^4.12.3"
31
+
32
+
33
+ [build-system]
34
+ requires = ["poetry-core"]
35
+ build-backend = "poetry.core.masonry.api"
36
+
37
+ [tool.coverage.run]
38
+ omit = [
39
+ "tests/*",
40
+ "*/__init__.py"
41
+ ]
42
+
43
+ [tool.coverage.report]
44
+ exclude_lines = [
45
+ "@abstractmethod",
46
+ "@abc.abstractmethod"
47
+ ]
48
+
49
+ [tool.pyright]
50
+ pythonVersion = "3.10"
51
+ pythonPlatform = "All"
52
+
53
+ typeCheckingMode = "strict"
54
+ reportMissingImports = true
55
+ reportMissingTypeStubs = false
56
+ reportMissingParameterType = false
57
+ reportUnknownArgumentType = false
58
+ reportUnknownMemberType = false
59
+ reportUnknownVariableType = false
60
+ reportUnknownParameterType = false
61
+ reportUnusedFunction = false
62
+ reportUnnecessaryTypeIgnoreComment = true
63
+ reportUntypedClassDecorator = false
64
+ reportUnusedClass = false
65
+ reportUntypedBaseClass = false
66
+
67
+ [tool.black]
68
+ line-length = 100
69
+ target-version = ["py310"]
70
+
71
+ [tool.ruff]
72
+ line-length = 100
73
+ target-version = "py310"
74
+ show-fixes = true
75
+ select = [
76
+ "I", # isort
77
+ "F", # Pyflakes
78
+ "E", # pycodestyle Error
79
+ "W", # pycodestyle Warning
80
+ "RUF", # Ruff-specific rules
81
+ ]
82
+ ignore = []
83
+
84
+ [tool.pytest.ini_options]
85
+ markers = [
86
+ "skip_coverage: skip coverage for this test"
87
+ ]
@@ -1,47 +0,0 @@
1
- [tool.poetry]
2
- name = "platzky"
3
- version = "0.2.5"
4
- description = "Not only blog engine"
5
- authors = []
6
- license = "MIT"
7
- readme = "README.md"
8
-
9
- [tool.poetry.dependencies]
10
- python = "^3.10"
11
- Flask = "3.0.3"
12
- PyYAML = "^6.0"
13
- Flask-Babel = "^4.0.0"
14
- Flask-WTF = "^1.2.1"
15
- gql = "^3.4.0"
16
- aiohttp = "^3.9.5"
17
- Flask-Minify = "^0.42"
18
- google-cloud-storage = "^2.5.0"
19
- humanize = "^4.9.0"
20
- pydantic = "^2.7.1"
21
-
22
- [tool.poetry.group.dev.dependencies]
23
- pytest = "^8.2.1"
24
- coverage = "^7.5.1"
25
- pytest-cov = "^5.0.0"
26
- freezegun = "^1.2.2"
27
- black = "^24.4.2"
28
- ruff = "^0.4.4"
29
- pyright = "^1.1.364"
30
- beautifulsoup4 = "^4.12.3"
31
-
32
-
33
- [build-system]
34
- requires = ["poetry-core"]
35
- build-backend = "poetry.core.masonry.api"
36
-
37
- [tool.coverage.run]
38
- omit = [
39
- "tests/*",
40
- "*/__init__.py"
41
- ]
42
-
43
- [tool.coverage.report]
44
- exclude_lines = [
45
- "@abstractmethod",
46
- "@abc.abstractmethod"
47
- ]
File without changes
@@ -1,3 +1,3 @@
1
+ from .platzky import Engine as Engine
1
2
  from .platzky import create_app_from_config as create_app_from_config
2
3
  from .platzky import create_engine as create_engine
3
- from .platzky import Engine as Engine
File without changes
File without changes
File without changes
File without changes