platzky 0.1.19__tar.gz → 0.2.1__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 (54) hide show
  1. platzky-0.2.1/PKG-INFO +40 -0
  2. platzky-0.2.1/README.md +16 -0
  3. platzky-0.2.1/platzky/blog/__init__.py +0 -0
  4. platzky-0.2.1/platzky/blog/blog.py +93 -0
  5. platzky-0.2.1/platzky/blog/comment_form.py +15 -0
  6. platzky-0.2.1/platzky/config.py +66 -0
  7. platzky-0.2.1/platzky/db/__init__.py +0 -0
  8. platzky-0.2.1/platzky/db/db.py +99 -0
  9. platzky-0.2.1/platzky/db/db_loader.py +32 -0
  10. platzky-0.2.1/platzky/db/google_json_db.py +56 -0
  11. {platzky-0.1.19 → platzky-0.2.1}/platzky/db/graph_ql_db.py +113 -57
  12. platzky-0.2.1/platzky/db/json_db.py +104 -0
  13. platzky-0.2.1/platzky/db/json_file_db.py +41 -0
  14. platzky-0.2.1/platzky/models.py +64 -0
  15. platzky-0.2.1/platzky/platzky.py +154 -0
  16. platzky-0.2.1/platzky/plugin_loader.py +39 -0
  17. platzky-0.2.1/platzky/plugins/redirections/entrypoint.py +68 -0
  18. platzky-0.2.1/platzky/plugins/sendmail/entrypoint.py +43 -0
  19. platzky-0.2.1/platzky/seo/seo.py +77 -0
  20. {platzky-0.1.19 → platzky-0.2.1}/platzky/static/blog.css +5 -12
  21. platzky-0.2.1/platzky/templates/base.html +167 -0
  22. {platzky-0.1.19 → platzky-0.2.1}/platzky/templates/blog.html +3 -2
  23. platzky-0.2.1/platzky/templates/body_meta.html +7 -0
  24. {platzky-0.1.19 → platzky-0.2.1}/platzky/templates/feed.xml +1 -1
  25. {platzky-0.1.19 → platzky-0.2.1}/platzky/templates/head_meta.html +5 -15
  26. {platzky-0.1.19 → platzky-0.2.1}/platzky/templates/post.html +2 -2
  27. {platzky-0.1.19 → platzky-0.2.1}/platzky/www_handler.py +7 -4
  28. platzky-0.2.1/pyproject.toml +47 -0
  29. platzky-0.1.19/PKG-INFO +0 -43
  30. platzky-0.1.19/README.md +0 -21
  31. platzky-0.1.19/platzky/blog/blog.py +0 -67
  32. platzky-0.1.19/platzky/blog/comment_form.py +0 -11
  33. platzky-0.1.19/platzky/blog/db.py +0 -18
  34. platzky-0.1.19/platzky/blog/post_formatter.py +0 -16
  35. platzky-0.1.19/platzky/config.py +0 -63
  36. platzky-0.1.19/platzky/db/google_json_db.py +0 -33
  37. platzky-0.1.19/platzky/db/json_db.py +0 -46
  38. platzky-0.1.19/platzky/db/json_file_db.py +0 -25
  39. platzky-0.1.19/platzky/db_loader.py +0 -11
  40. platzky-0.1.19/platzky/platzky.py +0 -94
  41. platzky-0.1.19/platzky/plugin_loader.py +0 -42
  42. platzky-0.1.19/platzky/plugins/redirections/entrypoint.py +0 -46
  43. platzky-0.1.19/platzky/plugins/sendmail/entrypoint.py +0 -22
  44. platzky-0.1.19/platzky/seo/seo.py +0 -67
  45. platzky-0.1.19/platzky/templates/base.html +0 -52
  46. platzky-0.1.19/platzky/templates/body_meta.html +0 -24
  47. platzky-0.1.19/pyproject.toml +0 -28
  48. platzky-0.1.19/setup.py +0 -43
  49. {platzky-0.1.19 → platzky-0.2.1}/platzky/__init__.py +0 -0
  50. {platzky-0.1.19 → platzky-0.2.1}/platzky/templates/404.html +0 -0
  51. {platzky-0.1.19 → platzky-0.2.1}/platzky/templates/home.html +0 -0
  52. {platzky-0.1.19 → platzky-0.2.1}/platzky/templates/page.html +0 -0
  53. {platzky-0.1.19 → platzky-0.2.1}/platzky/templates/robots.txt +0 -0
  54. {platzky-0.1.19 → platzky-0.2.1}/platzky/templates/sitemap.xml +0 -0
platzky-0.2.1/PKG-INFO ADDED
@@ -0,0 +1,40 @@
1
+ Metadata-Version: 2.1
2
+ Name: platzky
3
+ Version: 0.2.1
4
+ Summary: Not only blog engine
5
+ License: MIT
6
+ Requires-Python: >=3.10,<4.0
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Requires-Dist: Flask (==3.0.3)
13
+ Requires-Dist: Flask-Babel (>=4.0.0,<5.0.0)
14
+ Requires-Dist: Flask-Minify (>=0.42,<0.43)
15
+ Requires-Dist: Flask-WTF (>=1.2.1,<2.0.0)
16
+ Requires-Dist: PyYAML (>=6.0,<7.0)
17
+ Requires-Dist: aiohttp (>=3.9.5,<4.0.0)
18
+ Requires-Dist: google-cloud-storage (>=2.5.0,<3.0.0)
19
+ Requires-Dist: gql (>=3.4.0,<4.0.0)
20
+ Requires-Dist: humanize (>=4.9.0,<5.0.0)
21
+ Requires-Dist: pydantic (>=2.7.1,<3.0.0)
22
+ Description-Content-Type: text/markdown
23
+
24
+ ![Github Actions](https://github.com/platzky/platzky/actions/workflows/tests.yml/badge.svg?event=push&branch=main)
25
+ [![Coverage Status](https://coveralls.io/repos/github/platzky/platzky/badge.svg?branch=main)](https://coveralls.io/github/platzky/platzky?branch=main)
26
+
27
+ # platzky
28
+
29
+ Platzky is engine which aims to provide simple and easy way to create and run web applications in python.
30
+
31
+ # How to use?
32
+
33
+ 1. Install platzky with your favorite dependency management tool (`pip install platzky` or `poetry add platzky`).
34
+ 2. Copy `config-template.yml` to your project directory and fill it with your data.
35
+ 3. Run `flask --app "platzky.platzky:create_app(config_path='PATH_TO_YOUR_CONFIG_FILE')`
36
+
37
+ ## Example
38
+
39
+ For examples check e2e tests in `tests/e2e` directory and Makefile.
40
+
@@ -0,0 +1,16 @@
1
+ ![Github Actions](https://github.com/platzky/platzky/actions/workflows/tests.yml/badge.svg?event=push&branch=main)
2
+ [![Coverage Status](https://coveralls.io/repos/github/platzky/platzky/badge.svg?branch=main)](https://coveralls.io/github/platzky/platzky?branch=main)
3
+
4
+ # platzky
5
+
6
+ Platzky is engine which aims to provide simple and easy way to create and run web applications in python.
7
+
8
+ # How to use?
9
+
10
+ 1. Install platzky with your favorite dependency management tool (`pip install platzky` or `poetry add platzky`).
11
+ 2. Copy `config-template.yml` to your project directory and fill it with your data.
12
+ 3. Run `flask --app "platzky.platzky:create_app(config_path='PATH_TO_YOUR_CONFIG_FILE')`
13
+
14
+ ## Example
15
+
16
+ For examples check e2e tests in `tests/e2e` directory and Makefile.
File without changes
@@ -0,0 +1,93 @@
1
+ from os.path import dirname
2
+
3
+ from flask import Blueprint, make_response, render_template, request
4
+ from markupsafe import Markup
5
+
6
+ from . import comment_form
7
+
8
+
9
+ def create_blog_blueprint(db, blog_prefix: str, locale_func):
10
+ url_prefix = blog_prefix
11
+ blog = Blueprint(
12
+ "blog",
13
+ __name__,
14
+ url_prefix=url_prefix,
15
+ template_folder=f"{dirname(__file__)}/../templates",
16
+ )
17
+
18
+ @blog.app_template_filter()
19
+ def markdown(text):
20
+ return Markup(text)
21
+
22
+ @blog.errorhandler(404)
23
+ def page_not_found(e):
24
+ return render_template("404.html", title="404"), 404
25
+
26
+ @blog.route("/", methods=["GET"])
27
+ def all_posts():
28
+ lang = locale_func()
29
+ posts = db.get_all_posts(lang)
30
+ if not posts:
31
+ return page_not_found("no posts")
32
+ posts_sorted = sorted(posts, reverse=True)
33
+ return render_template("blog.html", posts=posts_sorted)
34
+
35
+ @blog.route("/feed", methods=["GET"])
36
+ def get_feed():
37
+ lang = locale_func()
38
+ response = make_response(
39
+ render_template("feed.xml", posts=db.get_all_posts(lang))
40
+ )
41
+ response.headers["Content-Type"] = "application/xml"
42
+ return response
43
+
44
+ @blog.route("/<post_slug>", methods=["POST"])
45
+ def post_comment(post_slug):
46
+ comment = request.form.to_dict()
47
+ db.add_comment(
48
+ post_slug=post_slug,
49
+ author_name=comment["author_name"],
50
+ comment=comment["comment"],
51
+ )
52
+ return get_post(post_slug=post_slug)
53
+
54
+ @blog.route("/<post_slug>", methods=["GET"])
55
+ def get_post(post_slug):
56
+ post = db.get_post(post_slug)
57
+ try:
58
+ post = db.get_post(post_slug)
59
+ return render_template(
60
+ "post.html",
61
+ post=post,
62
+ post_slug=post_slug,
63
+ form=comment_form.CommentForm(),
64
+ comment_sent=request.args.get("comment_sent"),
65
+ )
66
+ except ValueError:
67
+ return page_not_found(f"no post with slug {post_slug}")
68
+ except Exception as e:
69
+ return page_not_found(str(e))
70
+
71
+ @blog.route("/page/<path:page_slug>", methods=["GET"])
72
+ def get_page(
73
+ page_slug,
74
+ ): # TODO refactor to share code with get_post since they are very similar
75
+ try:
76
+ page = db.get_page(page_slug)
77
+ if cover_image := page.coverImage:
78
+ cover_image_url = cover_image.url
79
+ else:
80
+ cover_image_url = None
81
+ return render_template("page.html", page=page, cover_image=cover_image_url)
82
+ except ValueError:
83
+ return page_not_found("no page with slug {page_slug}")
84
+ except Exception as e:
85
+ return page_not_found(str(e))
86
+
87
+ @blog.route("/tag/<path:tag>", methods=["GET"])
88
+ def get_posts_from_tag(tag):
89
+ lang = locale_func()
90
+ posts = db.get_posts_by_tag(tag, lang)
91
+ return render_template("blog.html", posts=posts, subtitle=f" - tag: {tag}")
92
+
93
+ return blog
@@ -0,0 +1,15 @@
1
+ from flask_babel import lazy_gettext
2
+ from flask_wtf import FlaskForm
3
+ from wtforms import StringField, SubmitField
4
+ from wtforms.validators import DataRequired
5
+ from wtforms.widgets import TextArea
6
+
7
+
8
+ class CommentForm(FlaskForm):
9
+ author_name = StringField(str(lazy_gettext("Name")), validators=[DataRequired()])
10
+ comment = StringField(
11
+ str(lazy_gettext("Type comment here")),
12
+ validators=[DataRequired()],
13
+ widget=TextArea(),
14
+ )
15
+ submit = SubmitField(str(lazy_gettext("Comment")))
@@ -0,0 +1,66 @@
1
+ import sys
2
+ import typing as t
3
+ import yaml
4
+ from pydantic import ConfigDict, BaseModel, Field
5
+
6
+ from .db.db import DBConfig
7
+ from .db.db_loader import get_db_module
8
+
9
+
10
+ class StrictBaseModel(BaseModel):
11
+ model_config = ConfigDict(frozen=True)
12
+
13
+
14
+ class LanguageConfig(StrictBaseModel):
15
+ name: str = Field(alias="name")
16
+ flag: str = Field(alias="flag")
17
+ domain: t.Optional[str] = Field(default=None, alias="domain")
18
+
19
+
20
+ Languages = dict[str, LanguageConfig]
21
+ LanguagesMapping = t.Mapping[str, t.Mapping[str, str]]
22
+
23
+
24
+ def languages_dict(languages: Languages) -> LanguagesMapping:
25
+ return {name: lang.model_dump() for name, lang in languages.items()}
26
+
27
+
28
+ class Config(StrictBaseModel):
29
+ app_name: str = Field(alias="APP_NAME")
30
+ secret_key: str = Field(alias="SECRET_KEY")
31
+ db: DBConfig = Field(alias="DB")
32
+ use_www: bool = Field(default=True, alias="USE_WWW")
33
+ seo_prefix: str = Field(default="/", alias="SEO_PREFIX")
34
+ blog_prefix: str = Field(default="/", alias="BLOG_PREFIX")
35
+ languages: Languages = Field(default_factory=dict, alias="LANGUAGES")
36
+ domain_to_lang: dict[str, str] = Field(default_factory=dict, alias="DOMAIN_TO_LANG")
37
+ translation_directories: list[str] = Field(
38
+ default_factory=list,
39
+ alias="TRANSLATION_DIRECTORIES",
40
+ )
41
+ debug: bool = Field(default=False, alias="DEBUG")
42
+ testing: bool = Field(default=False, alias="TESTING")
43
+
44
+ @classmethod
45
+ def model_validate(
46
+ cls,
47
+ obj: t.Any,
48
+ *,
49
+ strict: bool | None = None,
50
+ from_attributes: bool | None = None,
51
+ context: dict[str, t.Any] | None = None,
52
+ ) -> "Config":
53
+ db_cfg_type = get_db_module(obj["DB"]["TYPE"]).db_config_type()
54
+ obj["DB"] = db_cfg_type.model_validate(obj["DB"])
55
+ return super().model_validate(
56
+ obj, strict=strict, from_attributes=from_attributes, context=context
57
+ )
58
+
59
+ @classmethod
60
+ def parse_yaml(cls, path: str) -> "Config":
61
+ try:
62
+ with open(path, "r") as f:
63
+ return cls.model_validate(yaml.safe_load(f))
64
+ except FileNotFoundError:
65
+ print(f"Config file not found: {path}", file=sys.stderr)
66
+ raise SystemExit(1)
File without changes
@@ -0,0 +1,99 @@
1
+ from functools import partial
2
+ from typing import Any, Callable
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+ from abc import abstractmethod, ABC
7
+ from ..models import MenuItem, Post, Page, Color
8
+
9
+
10
+ class DB(ABC):
11
+ db_name: str = "DB"
12
+ module_name: str = "db"
13
+ config_type: type
14
+
15
+ def __init_subclass__(cls, *args, **kw):
16
+ """Check that all methods defined in the subclass exist in the superclasses.
17
+ This is to prevent subclasses from adding new methods to the DB object.
18
+ """
19
+ super().__init_subclass__(*args, **kw)
20
+ for name in cls.__dict__:
21
+ if name.startswith("_"):
22
+ continue
23
+ for superclass in cls.__mro__[1:]:
24
+ if name in dir(superclass):
25
+ break
26
+ else:
27
+ raise TypeError(
28
+ f"Method {name} defined in {cls.__name__} does not exist in superclasses"
29
+ )
30
+
31
+ def extend(self, function_name: str, function: Callable) -> None:
32
+ """
33
+ Add a function to the DB object. The function must take the DB object as first parameter.
34
+
35
+ Parameters:
36
+ function_name (str): The name of the function to add.
37
+ function (Callable): The function to add to the DB object.
38
+ """
39
+ if not callable(function):
40
+ raise ValueError(
41
+ f"The provided func for '{function_name}' is not callable."
42
+ )
43
+ try:
44
+ bound_function = partial(function, self)
45
+ setattr(self, function_name, bound_function)
46
+ except Exception as e:
47
+ raise ValueError(f"Failed to extend DB with function {function_name}: {e}")
48
+
49
+ @abstractmethod
50
+ def get_all_posts(self, lang) -> list[Post]:
51
+ pass
52
+
53
+ @abstractmethod
54
+ def get_menu_items(self) -> list[MenuItem]:
55
+ pass
56
+
57
+ @abstractmethod
58
+ def get_post(self, slug) -> Post:
59
+ pass
60
+
61
+ @abstractmethod
62
+ def get_page(self, slug) -> Page:
63
+ pass
64
+
65
+ @abstractmethod
66
+ def get_posts_by_tag(self, tag, lang) -> Any:
67
+ pass
68
+
69
+ @abstractmethod
70
+ def add_comment(self, author_name, comment, post_slug) -> None:
71
+ pass
72
+
73
+ @abstractmethod
74
+ def get_logo_url(self) -> str:
75
+ pass
76
+
77
+ @abstractmethod
78
+ def get_primary_color(self) -> Color:
79
+ pass
80
+
81
+ @abstractmethod
82
+ def get_secondary_color(self) -> Color:
83
+ pass
84
+
85
+ @abstractmethod
86
+ def get_plugins_data(self) -> list:
87
+ pass
88
+
89
+ @abstractmethod
90
+ def get_font(self) -> str:
91
+ pass
92
+
93
+ @abstractmethod
94
+ def get_site_content(self) -> str:
95
+ pass
96
+
97
+
98
+ class DBConfig(BaseModel):
99
+ type: str = Field(alias="TYPE")
@@ -0,0 +1,32 @@
1
+ import importlib.util
2
+ import os
3
+ import sys
4
+ from os.path import abspath, dirname
5
+
6
+
7
+ def get_db(db_config):
8
+ db_name = db_config.type
9
+ db = get_db_module(db_name)
10
+ return db.db_from_config(db_config)
11
+
12
+
13
+ def get_db_module(db_type):
14
+ """
15
+ Load db module from db_type
16
+ This function is used to load db module dynamically as it is specified in config file.
17
+ :param db_type: name of db module
18
+ :return: db module
19
+ """
20
+ db_dir = dirname(abspath(__file__))
21
+ parent_module_name = ".".join(__name__.split(".")[:-1])
22
+ module_name = f"{parent_module_name}.{db_type}_db"
23
+ spec = importlib.util.spec_from_file_location(
24
+ module_name, os.path.join(db_dir, f"{db_type}_db.py")
25
+ )
26
+ assert spec is not None
27
+ db = importlib.util.module_from_spec(spec)
28
+ sys.modules[f"{db_type}_db"] = db
29
+ assert spec.loader is not None
30
+ spec.loader.exec_module(db)
31
+
32
+ return db
@@ -0,0 +1,56 @@
1
+ import json
2
+
3
+ from google.cloud.storage import Client
4
+ from pydantic import Field
5
+
6
+ from .db import DBConfig
7
+ from .json_db import Json
8
+
9
+
10
+ def db_config_type():
11
+ return GoogleJsonDbConfig
12
+
13
+
14
+ class GoogleJsonDbConfig(DBConfig):
15
+ bucket_name: str = Field(alias="BUCKET_NAME")
16
+ source_blob_name: str = Field(alias="SOURCE_BLOB_NAME")
17
+
18
+
19
+ def db_from_config(config: GoogleJsonDbConfig):
20
+ return GoogleJsonDb(config.bucket_name, config.source_blob_name)
21
+
22
+
23
+ def get_db(config):
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
+ )
28
+
29
+
30
+ def get_blob(bucket_name, source_blob_name):
31
+ storage_client = Client()
32
+ bucket = storage_client.bucket(bucket_name)
33
+ return bucket.blob(source_blob_name)
34
+
35
+
36
+ def get_data(blob):
37
+ raw_data = blob.download_as_text(client=None)
38
+ return json.loads(raw_data)
39
+
40
+
41
+ class GoogleJsonDb(Json):
42
+ def __init__(self, bucket_name, source_blob_name):
43
+ self.bucket_name = bucket_name
44
+ self.source_blob_name = source_blob_name
45
+
46
+ self.blob = get_blob(self.bucket_name, self.source_blob_name)
47
+ data = get_data(self.blob)
48
+ super().__init__(data)
49
+
50
+ self.module_name = "google_json_db"
51
+ self.db_name = "GoogleJsonDb"
52
+
53
+ def __save_entry(self, entry):
54
+ data = get_data(self.blob)
55
+ data["data"].append(entry)
56
+ self.blob.upload_from_string(json.dumps(data), content_type="application/json")
@@ -1,22 +1,58 @@
1
- #TODO rename file, extract it to another library, remove qgl and aiohttp from dependencies
1
+ # TODO rename file, extract it to another library, remove qgl and aiohttp from dependencies
2
2
 
3
- from gql import gql, Client
3
+
4
+ from gql import Client, gql
4
5
  from gql.transport.aiohttp import AIOHTTPTransport
5
- import json
6
- from platzky.blog.db import DB
6
+ from pydantic import Field
7
+
8
+ from .db import DB, DBConfig
9
+ from ..models import Color, Post
10
+
11
+
12
+ def db_config_type():
13
+ return GraphQlDbConfig
14
+
15
+
16
+ class GraphQlDbConfig(DBConfig):
17
+ endpoint: str = Field(alias="CMS_ENDPOINT")
18
+ token: str = Field(alias="CMS_TOKEN")
19
+
7
20
 
21
+ def get_db(config: GraphQlDbConfig):
22
+ return GraphQL(config.endpoint, config.token)
8
23
 
9
- def get_db(config):
10
- endpoint = config["DB"]["CMS_ENDPOINT"]
11
- token = config["DB"]["CMS_TOKEN"]
12
- return GraphQL(endpoint, token)
24
+
25
+ def db_from_config(config: GraphQlDbConfig):
26
+ return GraphQL(config.endpoint, config.token)
27
+
28
+
29
+ def _standarize_post(post):
30
+ return {
31
+ "author": post["author"]["name"],
32
+ "slug": post["slug"],
33
+ "title": post["title"],
34
+ "excerpt": post["excerpt"],
35
+ "contentInMarkdown": post["contentInRichText"]["html"],
36
+ "comments": post["comments"],
37
+ "tags": post["tags"],
38
+ "language": post["language"],
39
+ "coverImage": {
40
+ "url": post["coverImage"]["image"]["url"],
41
+ },
42
+ "date": post["date"],
43
+ }
13
44
 
14
45
 
15
46
  class GraphQL(DB):
16
47
  def __init__(self, endpoint, token):
17
- full_token = 'bearer ' + token
18
- transport = AIOHTTPTransport(url=endpoint, headers={'Authorization': full_token})
48
+ self.module_name = "graph_ql_db"
49
+ self.db_name = "GraphQLDb"
50
+ full_token = "bearer " + token
51
+ transport = AIOHTTPTransport(
52
+ url=endpoint, headers={"Authorization": full_token}
53
+ )
19
54
  self.client = Client(transport=transport)
55
+ super().__init__()
20
56
 
21
57
  def get_all_posts(self, lang):
22
58
  all_posts = gql(
@@ -24,22 +60,38 @@ class GraphQL(DB):
24
60
  query MyQuery($lang: Lang!) {
25
61
  posts(where: {language: $lang}, orderBy: date_DESC, stage: PUBLISHED){
26
62
  createdAt
63
+ author {
64
+ name
65
+ }
66
+ contentInRichText {
67
+ html
68
+ }
69
+ comments {
70
+ comment
71
+ author
72
+ createdAt
73
+ }
27
74
  date
28
75
  title
29
76
  excerpt
30
77
  slug
31
78
  tags
79
+ language
32
80
  coverImage {
33
81
  alternateText
34
82
  image {
35
83
  url
36
84
  }
37
- }
85
+ }
38
86
  }
39
87
  }
40
88
  """
41
89
  )
42
- return self.client.execute(all_posts, variable_values={"lang": lang})['posts']
90
+ raw_ql_posts = self.client.execute(all_posts, variable_values={"lang": lang})[
91
+ "posts"
92
+ ]
93
+
94
+ return [Post.model_validate(_standarize_post(post)) for post in raw_ql_posts]
43
95
 
44
96
  def get_menu_items(self):
45
97
  menu_items = gql(
@@ -52,17 +104,23 @@ class GraphQL(DB):
52
104
  }
53
105
  """
54
106
  )
55
- return self.client.execute(menu_items)['menuItems']
107
+ return self.client.execute(menu_items)["menuItems"]
56
108
 
57
109
  def get_post(self, slug):
58
110
  post = gql(
59
111
  """
60
112
  query MyQuery($slug: String!) {
61
113
  post(where: {slug: $slug}, stage: PUBLISHED) {
114
+ date
115
+ language
62
116
  title
117
+ slug
118
+ author {
119
+ name
120
+ }
63
121
  contentInRichText {
64
- text
65
122
  markdown
123
+ html
66
124
  }
67
125
  excerpt
68
126
  tags
@@ -76,13 +134,16 @@ class GraphQL(DB):
76
134
  author
77
135
  comment
78
136
  date: createdAt
79
- }
137
+ }
80
138
  }
81
139
  }
82
- """)
83
- return self.client.execute(post, variable_values={"slug": slug})['post']
140
+ """
141
+ )
84
142
 
85
- #TODO Cleanup page logic of internationalization (now it depends on translation of slugs)
143
+ post_raw = self.client.execute(post, variable_values={"slug": slug})["post"]
144
+ return Post.model_validate(_standarize_post(post_raw))
145
+
146
+ # TODO Cleanup page logic of internationalization (now it depends on translation of slugs)
86
147
  def get_page(self, slug):
87
148
  post = gql(
88
149
  """
@@ -96,8 +157,9 @@ class GraphQL(DB):
96
157
  }
97
158
  }
98
159
  }
99
- """)
100
- return self.client.execute(post, variable_values={"slug": slug})['page']
160
+ """
161
+ )
162
+ return self.client.execute(post, variable_values={"slug": slug})["page"]
101
163
 
102
164
  def get_posts_by_tag(self, tag, lang):
103
165
  post = gql(
@@ -117,41 +179,11 @@ class GraphQL(DB):
117
179
  }
118
180
  }
119
181
  }
120
- """)
121
- return self.client.execute(post, variable_values={"tag": tag, "lang": lang})['posts']
122
-
123
- def get_all_providers(self):
124
- all_providers = gql(
125
- """
126
- query MyQuery {
127
- providers(stage: PUBLISHED) {
128
- link
129
- name
130
- offer
131
- currency
132
- }
133
- }
134
182
  """
135
183
  )
136
- providers = self.client.execute(all_providers)["providers"]
137
- for provider in providers:
138
- provider["offer"] = json.loads(provider["offer"])
139
- return providers
140
-
141
- def get_all_questions(self):
142
- all_questions = gql(
143
- """
144
- query MyQuery {
145
- questions(stage: PUBLISHED) {
146
- question
147
- field
148
- inputType
149
- }
150
- }
151
- """
152
- )
153
- query = self.client.execute(all_questions)
154
- return query['questions']
184
+ return self.client.execute(post, variable_values={"tag": tag, "lang": lang})[
185
+ "posts"
186
+ ]
155
187
 
156
188
  def add_comment(self, author_name, comment, post_slug):
157
189
  add_comment = gql(
@@ -167,7 +199,31 @@ class GraphQL(DB):
167
199
  id
168
200
  }
169
201
  }
170
- """)
171
- self.client.execute(add_comment, variable_values={
172
- "author": author_name, "comment": comment, "slug": post_slug
173
- })
202
+ """
203
+ )
204
+ self.client.execute(
205
+ add_comment,
206
+ variable_values={
207
+ "author": author_name,
208
+ "comment": comment,
209
+ "slug": post_slug,
210
+ },
211
+ )
212
+
213
+ def get_font(self):
214
+ return str("")
215
+
216
+ def get_logo_url(self):
217
+ return ""
218
+
219
+ def get_primary_color(self) -> Color:
220
+ return Color()
221
+
222
+ def get_secondary_color(self):
223
+ return Color()
224
+
225
+ def get_site_content(self):
226
+ return ""
227
+
228
+ def get_plugins_data(self):
229
+ return []