forked from platypush/platypush
Compare commits
127 commits
master
...
29-generic
Author | SHA1 | Date | |
---|---|---|---|
486cd66885 | |||
72c7444a45 | |||
951950c864 | |||
d7278857e5 | |||
3e6ebdd23b | |||
8cd5cb3338 | |||
1af7ece881 | |||
5c68365188 | |||
7f575bacaa | |||
5995d045e1 | |||
c89ed24f4b | |||
1b791156bd | |||
e617fc75d4 | |||
041f64c80f | |||
aa5b52db2f | |||
5f09d449f4 | |||
6ec8a991df | |||
958ef6b987 | |||
16c55b45f6 | |||
b9b7404230 | |||
c0ffea681f | |||
2aab1d090d | |||
2cc80e7f16 | |||
deb25196d2 | |||
1880a99052 | |||
3513ee3e1c | |||
0d0995d71d | |||
2898a33752 | |||
0919a0055d | |||
5b3e1317f4 | |||
1df71cb54a | |||
0689e05e96 | |||
89560e7c38 | |||
30dfdeecb0 | |||
f57f940d57 | |||
117f92e5b4 | |||
a5541c33b0 | |||
b23f45f45e | |||
088cf23958 | |||
e8f4b7c10e | |||
dd12d57552 | |||
5aa3750807 | |||
f760d44224 | |||
8d91fec771 | |||
c22c17a55d | |||
46df3a6a98 | |||
8e06b8c727 | |||
30a024befb | |||
b16af0a97f | |||
c7970842d7 | |||
7df67aca82 | |||
d29b377cf1 | |||
8d57cf06c2 | |||
975d37c562 | |||
90f067de61 | |||
f45df5d4d3 | |||
975991ba69 | |||
d22fbcd9db | |||
47f8520f3b | |||
d261b9bb9b | |||
9981cc4746 | |||
3e4b13d20f | |||
321a61d06d | |||
b22df768eb | |||
8e2154f2b5 | |||
a9751f21f1 | |||
135965176d | |||
ef6b57df31 | |||
7d4bd20df0 | |||
e6bfa1c50f | |||
332c91252c | |||
b35c761a43 | |||
08c0779347 | |||
595ebe49ca | |||
548d487e73 | |||
20530c2b6d | |||
9ddcf5eaeb | |||
2aa8778078 | |||
72617b4b75 | |||
be4d1e8e01 | |||
db4ad5825e | |||
4471001110 | |||
f17245e8c7 | |||
67ff585f6c | |||
17615ff028 | |||
532217be12 | |||
f301fd7e69 | |||
58861afb1c | |||
8ec9c8f203 | |||
3435f591eb | |||
19223bbbe1 | |||
453652ef76 | |||
b2ff66aa62 | |||
655d56f4da | |||
f52b556219 | |||
947b50b937 | |||
db7c2095ea | |||
e40b668380 | |||
d3dc86a5e2 | |||
28026b0428 | |||
44707731a8 | |||
948f37afd4 | |||
3b4f7d3dad | |||
2eeb1d4fea | |||
26ffc0b0e1 | |||
7b1a63e287 | |||
1c6ff2fa49 | |||
d311629403 | |||
d52ae2fb80 | |||
061268cdaf | |||
91ff47167b | |||
fe0f3202fe | |||
8a70f1d38e | |||
4b7eeaa4ed | |||
b43ed169c7 | |||
0dac2c0e92 | |||
28b3672432 | |||
9f2793118b | |||
9d9ec1dc59 | |||
b9c78ad913 | |||
91ff8d811f | |||
783238642d | |||
53da19b638 | |||
7459f0115b | |||
2c4c27855d | |||
9c25a131fa | |||
4ee7e4db29 |
541 changed files with 8138 additions and 2275 deletions
|
@ -6,11 +6,12 @@ repos:
|
||||||
hooks:
|
hooks:
|
||||||
# - id: trailing-whitespace
|
# - id: trailing-whitespace
|
||||||
# - id: end-of-file-fixer
|
# - id: end-of-file-fixer
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
- id: check-json
|
- id: check-json
|
||||||
- id: check-xml
|
- id: check-xml
|
||||||
- id: check-symlinks
|
- id: check-symlinks
|
||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
|
args: ['--maxkb=1500']
|
||||||
|
|
||||||
- repo: https://github.com/Lucas-C/pre-commit-hooks-nodejs
|
- repo: https://github.com/Lucas-C/pre-commit-hooks-nodejs
|
||||||
rev: v1.1.2
|
rev: v1.1.2
|
||||||
|
|
|
@ -20,6 +20,7 @@ Events
|
||||||
platypush/events/custom.rst
|
platypush/events/custom.rst
|
||||||
platypush/events/dbus.rst
|
platypush/events/dbus.rst
|
||||||
platypush/events/distance.rst
|
platypush/events/distance.rst
|
||||||
|
platypush/events/entities.rst
|
||||||
platypush/events/file.rst
|
platypush/events/file.rst
|
||||||
platypush/events/foursquare.rst
|
platypush/events/foursquare.rst
|
||||||
platypush/events/geo.rst
|
platypush/events/geo.rst
|
||||||
|
|
5
docs/source/platypush/events/entities.rst
Normal file
5
docs/source/platypush/events/entities.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
``entities``
|
||||||
|
============
|
||||||
|
|
||||||
|
.. automodule:: platypush.message.event.entities
|
||||||
|
:members:
|
5
docs/source/platypush/plugins/entities.rst
Normal file
5
docs/source/platypush/plugins/entities.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
``entities``
|
||||||
|
============
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.entities
|
||||||
|
:members:
|
|
@ -32,6 +32,7 @@ Plugins
|
||||||
platypush/plugins/db.rst
|
platypush/plugins/db.rst
|
||||||
platypush/plugins/dbus.rst
|
platypush/plugins/dbus.rst
|
||||||
platypush/plugins/dropbox.rst
|
platypush/plugins/dropbox.rst
|
||||||
|
platypush/plugins/entities.rst
|
||||||
platypush/plugins/esp.rst
|
platypush/plugins/esp.rst
|
||||||
platypush/plugins/ffmpeg.rst
|
platypush/plugins/ffmpeg.rst
|
||||||
platypush/plugins/file.rst
|
platypush/plugins/file.rst
|
||||||
|
|
|
@ -9,11 +9,13 @@ import argparse
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from .bus.redis import RedisBus
|
from .bus.redis import RedisBus
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .context import register_backends, register_plugins
|
from .context import register_backends, register_plugins
|
||||||
from .cron.scheduler import CronScheduler
|
from .cron.scheduler import CronScheduler
|
||||||
|
from .entities import init_entities_engine, EntitiesEngine
|
||||||
from .event.processor import EventProcessor
|
from .event.processor import EventProcessor
|
||||||
from .logger import Logger
|
from .logger import Logger
|
||||||
from .message.event import Event
|
from .message.event import Event
|
||||||
|
@ -96,6 +98,7 @@ class Daemon:
|
||||||
self.no_capture_stdout = no_capture_stdout
|
self.no_capture_stdout = no_capture_stdout
|
||||||
self.no_capture_stderr = no_capture_stderr
|
self.no_capture_stderr = no_capture_stderr
|
||||||
self.event_processor = EventProcessor()
|
self.event_processor = EventProcessor()
|
||||||
|
self.entities_engine: Optional[EntitiesEngine] = None
|
||||||
self.requests_to_process = requests_to_process
|
self.requests_to_process = requests_to_process
|
||||||
self.processed_requests = 0
|
self.processed_requests = 0
|
||||||
self.cron_scheduler = None
|
self.cron_scheduler = None
|
||||||
|
@ -199,16 +202,25 @@ class Daemon:
|
||||||
"""Stops the backends and the bus"""
|
"""Stops the backends and the bus"""
|
||||||
from .plugins import RunnablePlugin
|
from .plugins import RunnablePlugin
|
||||||
|
|
||||||
for backend in self.backends.values():
|
if self.backends:
|
||||||
backend.stop()
|
for backend in self.backends.values():
|
||||||
|
backend.stop()
|
||||||
|
|
||||||
for plugin in get_enabled_plugins().values():
|
for plugin in get_enabled_plugins().values():
|
||||||
if isinstance(plugin, RunnablePlugin):
|
if isinstance(plugin, RunnablePlugin):
|
||||||
plugin.stop()
|
plugin.stop()
|
||||||
|
|
||||||
self.bus.stop()
|
if self.bus:
|
||||||
|
self.bus.stop()
|
||||||
|
self.bus = None
|
||||||
|
|
||||||
if self.cron_scheduler:
|
if self.cron_scheduler:
|
||||||
self.cron_scheduler.stop()
|
self.cron_scheduler.stop()
|
||||||
|
self.cron_scheduler = None
|
||||||
|
|
||||||
|
if self.entities_engine:
|
||||||
|
self.entities_engine.stop()
|
||||||
|
self.entities_engine = None
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Start the daemon"""
|
"""Start the daemon"""
|
||||||
|
@ -230,6 +242,9 @@ class Daemon:
|
||||||
# Initialize the plugins
|
# Initialize the plugins
|
||||||
register_plugins(bus=self.bus)
|
register_plugins(bus=self.bus)
|
||||||
|
|
||||||
|
# Initialize the entities engine
|
||||||
|
self.entities_engine = init_entities_engine()
|
||||||
|
|
||||||
# Start the cron scheduler
|
# Start the cron scheduler
|
||||||
if Config.get_cronjobs():
|
if Config.get_cronjobs():
|
||||||
self.cron_scheduler = CronScheduler(jobs=Config.get_cronjobs())
|
self.cron_scheduler = CronScheduler(jobs=Config.get_cronjobs())
|
||||||
|
|
|
@ -3,8 +3,7 @@ import os
|
||||||
from typing import Optional, Union, List, Dict, Any
|
from typing import Optional, Union, List, Dict, Any
|
||||||
|
|
||||||
from sqlalchemy import create_engine, Column, Integer, String, DateTime
|
from sqlalchemy import create_engine, Column, Integer, String, DateTime
|
||||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
from sqlalchemy.orm import sessionmaker, scoped_session, declarative_base
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
|
||||||
|
|
||||||
from platypush.backend import Backend
|
from platypush.backend import Backend
|
||||||
from platypush.config import Config
|
from platypush.config import Config
|
||||||
|
@ -17,10 +16,10 @@ Session = scoped_session(sessionmaker())
|
||||||
|
|
||||||
|
|
||||||
class Covid19Update(Base):
|
class Covid19Update(Base):
|
||||||
""" Models the Covid19Data table """
|
"""Models the Covid19Data table"""
|
||||||
|
|
||||||
__tablename__ = 'covid19data'
|
__tablename__ = 'covid19data'
|
||||||
__table_args__ = ({'sqlite_autoincrement': True})
|
__table_args__ = {'sqlite_autoincrement': True}
|
||||||
|
|
||||||
country = Column(String, primary_key=True)
|
country = Column(String, primary_key=True)
|
||||||
confirmed = Column(Integer, nullable=False, default=0)
|
confirmed = Column(Integer, nullable=False, default=0)
|
||||||
|
@ -40,7 +39,12 @@ class Covid19Backend(Backend):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
def __init__(self, country: Optional[Union[str, List[str]]], poll_seconds: Optional[float] = 3600.0, **kwargs):
|
def __init__(
|
||||||
|
self,
|
||||||
|
country: Optional[Union[str, List[str]]],
|
||||||
|
poll_seconds: Optional[float] = 3600.0,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
:param country: Default country (or list of countries) to retrieve the stats for. It can either be the full
|
:param country: Default country (or list of countries) to retrieve the stats for. It can either be the full
|
||||||
country name or the country code. Special values:
|
country name or the country code. Special values:
|
||||||
|
@ -56,7 +60,9 @@ class Covid19Backend(Backend):
|
||||||
super().__init__(poll_seconds=poll_seconds, **kwargs)
|
super().__init__(poll_seconds=poll_seconds, **kwargs)
|
||||||
self._plugin: Covid19Plugin = get_plugin('covid19')
|
self._plugin: Covid19Plugin = get_plugin('covid19')
|
||||||
self.country: List[str] = self._plugin._get_countries(country)
|
self.country: List[str] = self._plugin._get_countries(country)
|
||||||
self.workdir = os.path.join(os.path.expanduser(Config.get('workdir')), 'covid19')
|
self.workdir = os.path.join(
|
||||||
|
os.path.expanduser(Config.get('workdir')), 'covid19'
|
||||||
|
)
|
||||||
self.dbfile = os.path.join(self.workdir, 'data.db')
|
self.dbfile = os.path.join(self.workdir, 'data.db')
|
||||||
os.makedirs(self.workdir, exist_ok=True)
|
os.makedirs(self.workdir, exist_ok=True)
|
||||||
|
|
||||||
|
@ -67,22 +73,30 @@ class Covid19Backend(Backend):
|
||||||
self.logger.info('Stopped Covid19 backend')
|
self.logger.info('Stopped Covid19 backend')
|
||||||
|
|
||||||
def _process_update(self, summary: Dict[str, Any], session: Session):
|
def _process_update(self, summary: Dict[str, Any], session: Session):
|
||||||
update_time = datetime.datetime.fromisoformat(summary['Date'].replace('Z', '+00:00'))
|
update_time = datetime.datetime.fromisoformat(
|
||||||
|
summary['Date'].replace('Z', '+00:00')
|
||||||
|
)
|
||||||
|
|
||||||
self.bus.post(Covid19UpdateEvent(
|
self.bus.post(
|
||||||
country=summary['Country'],
|
Covid19UpdateEvent(
|
||||||
country_code=summary['CountryCode'],
|
country=summary['Country'],
|
||||||
confirmed=summary['TotalConfirmed'],
|
country_code=summary['CountryCode'],
|
||||||
deaths=summary['TotalDeaths'],
|
confirmed=summary['TotalConfirmed'],
|
||||||
recovered=summary['TotalRecovered'],
|
deaths=summary['TotalDeaths'],
|
||||||
update_time=update_time,
|
recovered=summary['TotalRecovered'],
|
||||||
))
|
update_time=update_time,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
session.merge(Covid19Update(country=summary['CountryCode'],
|
session.merge(
|
||||||
confirmed=summary['TotalConfirmed'],
|
Covid19Update(
|
||||||
deaths=summary['TotalDeaths'],
|
country=summary['CountryCode'],
|
||||||
recovered=summary['TotalRecovered'],
|
confirmed=summary['TotalConfirmed'],
|
||||||
last_updated_at=update_time))
|
deaths=summary['TotalDeaths'],
|
||||||
|
recovered=summary['TotalRecovered'],
|
||||||
|
last_updated_at=update_time,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def loop(self):
|
def loop(self):
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
|
@ -90,23 +104,30 @@ class Covid19Backend(Backend):
|
||||||
if not summaries:
|
if not summaries:
|
||||||
return
|
return
|
||||||
|
|
||||||
engine = create_engine('sqlite:///{}'.format(self.dbfile), connect_args={'check_same_thread': False})
|
engine = create_engine(
|
||||||
|
'sqlite:///{}'.format(self.dbfile),
|
||||||
|
connect_args={'check_same_thread': False},
|
||||||
|
)
|
||||||
Base.metadata.create_all(engine)
|
Base.metadata.create_all(engine)
|
||||||
Session.configure(bind=engine)
|
Session.configure(bind=engine)
|
||||||
session = Session()
|
session = Session()
|
||||||
|
|
||||||
last_records = {
|
last_records = {
|
||||||
record.country: record
|
record.country: record
|
||||||
for record in session.query(Covid19Update).filter(Covid19Update.country.in_(self.country)).all()
|
for record in session.query(Covid19Update)
|
||||||
|
.filter(Covid19Update.country.in_(self.country))
|
||||||
|
.all()
|
||||||
}
|
}
|
||||||
|
|
||||||
for summary in summaries:
|
for summary in summaries:
|
||||||
country = summary['CountryCode']
|
country = summary['CountryCode']
|
||||||
last_record = last_records.get(country)
|
last_record = last_records.get(country)
|
||||||
if not last_record or \
|
if (
|
||||||
summary['TotalConfirmed'] != last_record.confirmed or \
|
not last_record
|
||||||
summary['TotalDeaths'] != last_record.deaths or \
|
or summary['TotalConfirmed'] != last_record.confirmed
|
||||||
summary['TotalRecovered'] != last_record.recovered:
|
or summary['TotalDeaths'] != last_record.deaths
|
||||||
|
or summary['TotalRecovered'] != last_record.recovered
|
||||||
|
):
|
||||||
self._process_update(summary=summary, session=session)
|
self._process_update(summary=summary, session=session)
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
|
@ -6,15 +6,28 @@ from typing import Optional, List
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from sqlalchemy import create_engine, Column, String, DateTime
|
from sqlalchemy import create_engine, Column, String, DateTime
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.orm import sessionmaker, scoped_session, declarative_base
|
||||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
|
||||||
|
|
||||||
from platypush.backend import Backend
|
from platypush.backend import Backend
|
||||||
from platypush.config import Config
|
from platypush.config import Config
|
||||||
from platypush.message.event.github import GithubPushEvent, GithubCommitCommentEvent, GithubCreateEvent, \
|
from platypush.message.event.github import (
|
||||||
GithubDeleteEvent, GithubEvent, GithubForkEvent, GithubWikiEvent, GithubIssueCommentEvent, GithubIssueEvent, \
|
GithubPushEvent,
|
||||||
GithubMemberEvent, GithubPublicEvent, GithubPullRequestEvent, GithubPullRequestReviewCommentEvent, \
|
GithubCommitCommentEvent,
|
||||||
GithubReleaseEvent, GithubSponsorshipEvent, GithubWatchEvent
|
GithubCreateEvent,
|
||||||
|
GithubDeleteEvent,
|
||||||
|
GithubEvent,
|
||||||
|
GithubForkEvent,
|
||||||
|
GithubWikiEvent,
|
||||||
|
GithubIssueCommentEvent,
|
||||||
|
GithubIssueEvent,
|
||||||
|
GithubMemberEvent,
|
||||||
|
GithubPublicEvent,
|
||||||
|
GithubPullRequestEvent,
|
||||||
|
GithubPullRequestReviewCommentEvent,
|
||||||
|
GithubReleaseEvent,
|
||||||
|
GithubSponsorshipEvent,
|
||||||
|
GithubWatchEvent,
|
||||||
|
)
|
||||||
|
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
Session = scoped_session(sessionmaker())
|
Session = scoped_session(sessionmaker())
|
||||||
|
@ -71,8 +84,17 @@ class GithubBackend(Backend):
|
||||||
|
|
||||||
_base_url = 'https://api.github.com'
|
_base_url = 'https://api.github.com'
|
||||||
|
|
||||||
def __init__(self, user: str, user_token: str, repos: Optional[List[str]] = None, org: Optional[str] = None,
|
def __init__(
|
||||||
poll_seconds: int = 60, max_events_per_scan: Optional[int] = 10, *args, **kwargs):
|
self,
|
||||||
|
user: str,
|
||||||
|
user_token: str,
|
||||||
|
repos: Optional[List[str]] = None,
|
||||||
|
org: Optional[str] = None,
|
||||||
|
poll_seconds: int = 60,
|
||||||
|
max_events_per_scan: Optional[int] = 10,
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
If neither ``repos`` nor ``org`` is specified then the backend will monitor all new events on user level.
|
If neither ``repos`` nor ``org`` is specified then the backend will monitor all new events on user level.
|
||||||
|
|
||||||
|
@ -102,17 +124,23 @@ class GithubBackend(Backend):
|
||||||
|
|
||||||
def _request(self, uri: str, method: str = 'get') -> dict:
|
def _request(self, uri: str, method: str = 'get') -> dict:
|
||||||
method = getattr(requests, method.lower())
|
method = getattr(requests, method.lower())
|
||||||
return method(self._base_url + uri, auth=(self.user, self.user_token),
|
return method(
|
||||||
headers={'Accept': 'application/vnd.github.v3+json'}).json()
|
self._base_url + uri,
|
||||||
|
auth=(self.user, self.user_token),
|
||||||
|
headers={'Accept': 'application/vnd.github.v3+json'},
|
||||||
|
).json()
|
||||||
|
|
||||||
def _init_db(self):
|
def _init_db(self):
|
||||||
engine = create_engine('sqlite:///{}'.format(self.dbfile), connect_args={'check_same_thread': False})
|
engine = create_engine(
|
||||||
|
'sqlite:///{}'.format(self.dbfile),
|
||||||
|
connect_args={'check_same_thread': False},
|
||||||
|
)
|
||||||
Base.metadata.create_all(engine)
|
Base.metadata.create_all(engine)
|
||||||
Session.configure(bind=engine)
|
Session.configure(bind=engine)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _to_datetime(time_string: str) -> datetime.datetime:
|
def _to_datetime(time_string: str) -> datetime.datetime:
|
||||||
""" Convert ISO 8061 string format with leading 'Z' into something understandable by Python """
|
"""Convert ISO 8061 string format with leading 'Z' into something understandable by Python"""
|
||||||
return datetime.datetime.fromisoformat(time_string[:-1] + '+00:00')
|
return datetime.datetime.fromisoformat(time_string[:-1] + '+00:00')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -128,7 +156,11 @@ class GithubBackend(Backend):
|
||||||
def _get_last_event_time(self, uri: str):
|
def _get_last_event_time(self, uri: str):
|
||||||
with self.db_lock:
|
with self.db_lock:
|
||||||
record = self._get_or_create_resource(uri=uri, session=Session())
|
record = self._get_or_create_resource(uri=uri, session=Session())
|
||||||
return record.last_updated_at.replace(tzinfo=datetime.timezone.utc) if record.last_updated_at else None
|
return (
|
||||||
|
record.last_updated_at.replace(tzinfo=datetime.timezone.utc)
|
||||||
|
if record.last_updated_at
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
def _update_last_event_time(self, uri: str, last_updated_at: datetime.datetime):
|
def _update_last_event_time(self, uri: str, last_updated_at: datetime.datetime):
|
||||||
with self.db_lock:
|
with self.db_lock:
|
||||||
|
@ -158,9 +190,18 @@ class GithubBackend(Backend):
|
||||||
'WatchEvent': GithubWatchEvent,
|
'WatchEvent': GithubWatchEvent,
|
||||||
}
|
}
|
||||||
|
|
||||||
event_type = event_mapping[event['type']] if event['type'] in event_mapping else GithubEvent
|
event_type = (
|
||||||
return event_type(event_type=event['type'], actor=event['actor'], repo=event.get('repo', {}),
|
event_mapping[event['type']]
|
||||||
payload=event['payload'], created_at=cls._to_datetime(event['created_at']))
|
if event['type'] in event_mapping
|
||||||
|
else GithubEvent
|
||||||
|
)
|
||||||
|
return event_type(
|
||||||
|
event_type=event['type'],
|
||||||
|
actor=event['actor'],
|
||||||
|
repo=event.get('repo', {}),
|
||||||
|
payload=event['payload'],
|
||||||
|
created_at=cls._to_datetime(event['created_at']),
|
||||||
|
)
|
||||||
|
|
||||||
def _events_monitor(self, uri: str, method: str = 'get'):
|
def _events_monitor(self, uri: str, method: str = 'get'):
|
||||||
def thread():
|
def thread():
|
||||||
|
@ -175,7 +216,10 @@ class GithubBackend(Backend):
|
||||||
fired_events = []
|
fired_events = []
|
||||||
|
|
||||||
for event in events:
|
for event in events:
|
||||||
if self.max_events_per_scan and len(fired_events) >= self.max_events_per_scan:
|
if (
|
||||||
|
self.max_events_per_scan
|
||||||
|
and len(fired_events) >= self.max_events_per_scan
|
||||||
|
):
|
||||||
break
|
break
|
||||||
|
|
||||||
event_time = self._to_datetime(event['created_at'])
|
event_time = self._to_datetime(event['created_at'])
|
||||||
|
@ -189,14 +233,19 @@ class GithubBackend(Backend):
|
||||||
for event in fired_events:
|
for event in fired_events:
|
||||||
self.bus.post(event)
|
self.bus.post(event)
|
||||||
|
|
||||||
self._update_last_event_time(uri=uri, last_updated_at=new_last_event_time)
|
self._update_last_event_time(
|
||||||
|
uri=uri, last_updated_at=new_last_event_time
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.warning('Encountered exception while fetching events from {}: {}'.format(
|
self.logger.warning(
|
||||||
uri, str(e)))
|
'Encountered exception while fetching events from {}: {}'.format(
|
||||||
|
uri, str(e)
|
||||||
|
)
|
||||||
|
)
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
finally:
|
|
||||||
if self.wait_stop(timeout=self.poll_seconds):
|
if self.wait_stop(timeout=self.poll_seconds):
|
||||||
break
|
break
|
||||||
|
|
||||||
return thread
|
return thread
|
||||||
|
|
||||||
|
@ -206,12 +255,30 @@ class GithubBackend(Backend):
|
||||||
|
|
||||||
if self.repos:
|
if self.repos:
|
||||||
for repo in self.repos:
|
for repo in self.repos:
|
||||||
monitors.append(threading.Thread(target=self._events_monitor('/networks/{repo}/events'.format(repo=repo))))
|
monitors.append(
|
||||||
|
threading.Thread(
|
||||||
|
target=self._events_monitor(
|
||||||
|
'/networks/{repo}/events'.format(repo=repo)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
if self.org:
|
if self.org:
|
||||||
monitors.append(threading.Thread(target=self._events_monitor('/orgs/{org}/events'.format(org=self.org))))
|
monitors.append(
|
||||||
|
threading.Thread(
|
||||||
|
target=self._events_monitor(
|
||||||
|
'/orgs/{org}/events'.format(org=self.org)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if not (self.repos or self.org):
|
if not (self.repos or self.org):
|
||||||
monitors.append(threading.Thread(target=self._events_monitor('/users/{user}/events'.format(user=self.user))))
|
monitors.append(
|
||||||
|
threading.Thread(
|
||||||
|
target=self._events_monitor(
|
||||||
|
'/users/{user}/events'.format(user=self.user)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
for monitor in monitors:
|
for monitor in monitors:
|
||||||
monitor.start()
|
monitor.start()
|
||||||
|
@ -222,4 +289,5 @@ class GithubBackend(Backend):
|
||||||
|
|
||||||
self.logger.info('Github backend terminated')
|
self.logger.info('Github backend terminated')
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from flask import Blueprint, abort, request, Response
|
from flask import Blueprint, abort, request
|
||||||
|
from flask.wrappers import Response
|
||||||
|
|
||||||
from platypush.backend.http.app import template_folder
|
from platypush.backend.http.app import template_folder
|
||||||
from platypush.backend.http.app.utils import authenticate, logger, send_message
|
from platypush.backend.http.app.utils import authenticate, logger, send_message
|
||||||
|
@ -14,8 +15,8 @@ __routes__ = [
|
||||||
|
|
||||||
|
|
||||||
@execute.route('/execute', methods=['POST'])
|
@execute.route('/execute', methods=['POST'])
|
||||||
@authenticate()
|
@authenticate(json=True)
|
||||||
def execute():
|
def execute_route():
|
||||||
"""Endpoint to execute commands"""
|
"""Endpoint to execute commands"""
|
||||||
try:
|
try:
|
||||||
msg = json.loads(request.data.decode('utf-8'))
|
msg = json.loads(request.data.decode('utf-8'))
|
||||||
|
|
|
@ -8,22 +8,27 @@ from platypush.backend.http.app import template_folder
|
||||||
|
|
||||||
|
|
||||||
img_folder = os.path.join(template_folder, 'img')
|
img_folder = os.path.join(template_folder, 'img')
|
||||||
|
fonts_folder = os.path.join(template_folder, 'fonts')
|
||||||
icons_folder = os.path.join(template_folder, 'icons')
|
icons_folder = os.path.join(template_folder, 'icons')
|
||||||
resources = Blueprint('resources', __name__, template_folder=template_folder)
|
resources = Blueprint('resources', __name__, template_folder=template_folder)
|
||||||
favicon = Blueprint('favicon', __name__, template_folder=template_folder)
|
favicon = Blueprint('favicon', __name__, template_folder=template_folder)
|
||||||
img = Blueprint('img', __name__, template_folder=template_folder)
|
img = Blueprint('img', __name__, template_folder=template_folder)
|
||||||
|
icons = Blueprint('icons', __name__, template_folder=template_folder)
|
||||||
|
fonts = Blueprint('fonts', __name__, template_folder=template_folder)
|
||||||
|
|
||||||
# Declare routes list
|
# Declare routes list
|
||||||
__routes__ = [
|
__routes__ = [
|
||||||
resources,
|
resources,
|
||||||
favicon,
|
favicon,
|
||||||
img,
|
img,
|
||||||
|
icons,
|
||||||
|
fonts,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@resources.route('/resources/<path:path>', methods=['GET'])
|
@resources.route('/resources/<path:path>', methods=['GET'])
|
||||||
def resources_path(path):
|
def resources_path(path):
|
||||||
""" Custom static resources """
|
"""Custom static resources"""
|
||||||
path_tokens = path.split('/')
|
path_tokens = path.split('/')
|
||||||
http_conf = Config.get('backend.http')
|
http_conf = Config.get('backend.http')
|
||||||
resource_dirs = http_conf.get('resource_dirs', {})
|
resource_dirs = http_conf.get('resource_dirs', {})
|
||||||
|
@ -42,9 +47,11 @@ def resources_path(path):
|
||||||
real_path = real_base_path
|
real_path = real_base_path
|
||||||
|
|
||||||
file_path = [
|
file_path = [
|
||||||
s for s in re.sub(
|
s
|
||||||
r'^{}(.*)$'.format(base_path), '\\1', path # lgtm [py/regex-injection]
|
for s in re.sub(
|
||||||
).split('/') if s
|
r'^{}(.*)$'.format(base_path), '\\1', path # lgtm [py/regex-injection]
|
||||||
|
).split('/')
|
||||||
|
if s
|
||||||
]
|
]
|
||||||
|
|
||||||
for p in file_path[:-1]:
|
for p in file_path[:-1]:
|
||||||
|
@ -61,20 +68,26 @@ def resources_path(path):
|
||||||
|
|
||||||
@favicon.route('/favicon.ico', methods=['GET'])
|
@favicon.route('/favicon.ico', methods=['GET'])
|
||||||
def serve_favicon():
|
def serve_favicon():
|
||||||
""" favicon.ico icon """
|
"""favicon.ico icon"""
|
||||||
return send_from_directory(template_folder, 'favicon.ico')
|
return send_from_directory(template_folder, 'favicon.ico')
|
||||||
|
|
||||||
|
|
||||||
@img.route('/img/<path:path>', methods=['GET'])
|
@img.route('/img/<path:path>', methods=['GET'])
|
||||||
def imgpath(path):
|
def imgpath(path):
|
||||||
""" Default static images """
|
"""Default static images"""
|
||||||
return send_from_directory(img_folder, path)
|
return send_from_directory(img_folder, path)
|
||||||
|
|
||||||
|
|
||||||
@img.route('/icons/<path:path>', methods=['GET'])
|
@icons.route('/icons/<path:path>', methods=['GET'])
|
||||||
def iconpath(path):
|
def iconpath(path):
|
||||||
""" Default static icons """
|
"""Default static icons"""
|
||||||
return send_from_directory(icons_folder, path)
|
return send_from_directory(icons_folder, path)
|
||||||
|
|
||||||
|
|
||||||
|
@fonts.route('/fonts/<path:path>', methods=['GET'])
|
||||||
|
def fontpath(path):
|
||||||
|
"""Default fonts"""
|
||||||
|
return send_from_directory(fonts_folder, path)
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -3,7 +3,8 @@ import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from flask import abort, request, redirect, Response, current_app
|
from flask import abort, request, redirect, jsonify, current_app
|
||||||
|
from flask.wrappers import Response
|
||||||
from redis import Redis
|
from redis import Redis
|
||||||
|
|
||||||
# NOTE: The HTTP service will *only* work on top of a Redis bus. The default
|
# NOTE: The HTTP service will *only* work on top of a Redis bus. The default
|
||||||
|
@ -184,7 +185,37 @@ def _authenticate_csrf_token():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def authenticate(redirect_page='', skip_auth_methods=None, check_csrf_token=False):
|
def authenticate(
|
||||||
|
redirect_page='',
|
||||||
|
skip_auth_methods=None,
|
||||||
|
check_csrf_token=False,
|
||||||
|
json=False,
|
||||||
|
):
|
||||||
|
def on_auth_fail(has_users=True):
|
||||||
|
if json:
|
||||||
|
if has_users:
|
||||||
|
return (
|
||||||
|
jsonify(
|
||||||
|
{
|
||||||
|
'message': 'Not logged in',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
401,
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
jsonify(
|
||||||
|
{
|
||||||
|
'message': 'Please register a user through '
|
||||||
|
'the web panel first',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
412,
|
||||||
|
)
|
||||||
|
|
||||||
|
target_page = 'login' if has_users else 'register'
|
||||||
|
return redirect(f'/{target_page}?redirect={redirect_page or request.url}', 307)
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
|
@ -213,9 +244,7 @@ def authenticate(redirect_page='', skip_auth_methods=None, check_csrf_token=Fals
|
||||||
if session_auth_ok:
|
if session_auth_ok:
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
return redirect(
|
return on_auth_fail()
|
||||||
'/login?redirect=' + (redirect_page or request.url), 307
|
|
||||||
)
|
|
||||||
|
|
||||||
# CSRF token check
|
# CSRF token check
|
||||||
if check_csrf_token:
|
if check_csrf_token:
|
||||||
|
@ -224,9 +253,7 @@ def authenticate(redirect_page='', skip_auth_methods=None, check_csrf_token=Fals
|
||||||
return abort(403, 'Invalid or missing csrf_token')
|
return abort(403, 'Invalid or missing csrf_token')
|
||||||
|
|
||||||
if n_users == 0 and 'session' not in skip_methods:
|
if n_users == 0 and 'session' not in skip_methods:
|
||||||
return redirect(
|
return on_auth_fail(has_users=False)
|
||||||
'/register?redirect=' + (redirect_page or request.url), 307
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
('http' not in skip_methods and http_auth_ok)
|
('http' not in skip_methods and http_auth_ok)
|
||||||
|
|
|
@ -2,11 +2,17 @@ import datetime
|
||||||
import enum
|
import enum
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from sqlalchemy import create_engine, Column, Integer, String, DateTime, \
|
from sqlalchemy import (
|
||||||
Enum, ForeignKey
|
create_engine,
|
||||||
|
Column,
|
||||||
|
Integer,
|
||||||
|
String,
|
||||||
|
DateTime,
|
||||||
|
Enum,
|
||||||
|
ForeignKey,
|
||||||
|
)
|
||||||
|
|
||||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
from sqlalchemy.orm import sessionmaker, scoped_session, declarative_base
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
|
||||||
from sqlalchemy.sql.expression import func
|
from sqlalchemy.sql.expression import func
|
||||||
|
|
||||||
from platypush.backend.http.request import HttpRequest
|
from platypush.backend.http.request import HttpRequest
|
||||||
|
@ -44,18 +50,31 @@ class RssUpdates(HttpRequest):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ' + \
|
user_agent = (
|
||||||
'Chrome/62.0.3202.94 Safari/537.36'
|
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) '
|
||||||
|
+ 'Chrome/62.0.3202.94 Safari/537.36'
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, url, title=None, headers=None, params=None, max_entries=None,
|
def __init__(
|
||||||
extract_content=False, digest_format=None, user_agent: str = user_agent,
|
self,
|
||||||
body_style: str = 'font-size: 22px; ' +
|
url,
|
||||||
'font-family: "Merriweather", Georgia, "Times New Roman", Times, serif;',
|
title=None,
|
||||||
title_style: str = 'margin-top: 30px',
|
headers=None,
|
||||||
subtitle_style: str = 'margin-top: 10px; page-break-after: always',
|
params=None,
|
||||||
article_title_style: str = 'page-break-before: always',
|
max_entries=None,
|
||||||
article_link_style: str = 'color: #555; text-decoration: none; border-bottom: 1px dotted',
|
extract_content=False,
|
||||||
article_content_style: str = '', *argv, **kwargs):
|
digest_format=None,
|
||||||
|
user_agent: str = user_agent,
|
||||||
|
body_style: str = 'font-size: 22px; '
|
||||||
|
+ 'font-family: "Merriweather", Georgia, "Times New Roman", Times, serif;',
|
||||||
|
title_style: str = 'margin-top: 30px',
|
||||||
|
subtitle_style: str = 'margin-top: 10px; page-break-after: always',
|
||||||
|
article_title_style: str = 'page-break-before: always',
|
||||||
|
article_link_style: str = 'color: #555; text-decoration: none; border-bottom: 1px dotted',
|
||||||
|
article_content_style: str = '',
|
||||||
|
*argv,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
:param url: URL to the RSS feed to be monitored.
|
:param url: URL to the RSS feed to be monitored.
|
||||||
:param title: Optional title for the feed.
|
:param title: Optional title for the feed.
|
||||||
|
@ -91,7 +110,9 @@ class RssUpdates(HttpRequest):
|
||||||
# If true, then the http.webpage plugin will be used to parse the content
|
# If true, then the http.webpage plugin will be used to parse the content
|
||||||
self.extract_content = extract_content
|
self.extract_content = extract_content
|
||||||
|
|
||||||
self.digest_format = digest_format.lower() if digest_format else None # Supported formats: html, pdf
|
self.digest_format = (
|
||||||
|
digest_format.lower() if digest_format else None
|
||||||
|
) # Supported formats: html, pdf
|
||||||
|
|
||||||
os.makedirs(os.path.expanduser(os.path.dirname(self.dbfile)), exist_ok=True)
|
os.makedirs(os.path.expanduser(os.path.dirname(self.dbfile)), exist_ok=True)
|
||||||
|
|
||||||
|
@ -119,7 +140,11 @@ class RssUpdates(HttpRequest):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_latest_update(session, source_id):
|
def _get_latest_update(session, source_id):
|
||||||
return session.query(func.max(FeedEntry.published)).filter_by(source_id=source_id).scalar()
|
return (
|
||||||
|
session.query(func.max(FeedEntry.published))
|
||||||
|
.filter_by(source_id=source_id)
|
||||||
|
.scalar()
|
||||||
|
)
|
||||||
|
|
||||||
def _parse_entry_content(self, link):
|
def _parse_entry_content(self, link):
|
||||||
self.logger.info('Extracting content from {}'.format(link))
|
self.logger.info('Extracting content from {}'.format(link))
|
||||||
|
@ -130,14 +155,20 @@ class RssUpdates(HttpRequest):
|
||||||
errors = response.errors
|
errors = response.errors
|
||||||
|
|
||||||
if not output:
|
if not output:
|
||||||
self.logger.warning('Mercury parser error: {}'.format(errors or '[unknown error]'))
|
self.logger.warning(
|
||||||
|
'Mercury parser error: {}'.format(errors or '[unknown error]')
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
return output.get('content')
|
return output.get('content')
|
||||||
|
|
||||||
def get_new_items(self, response):
|
def get_new_items(self, response):
|
||||||
import feedparser
|
import feedparser
|
||||||
engine = create_engine('sqlite:///{}'.format(self.dbfile), connect_args={'check_same_thread': False})
|
|
||||||
|
engine = create_engine(
|
||||||
|
'sqlite:///{}'.format(self.dbfile),
|
||||||
|
connect_args={'check_same_thread': False},
|
||||||
|
)
|
||||||
|
|
||||||
Base.metadata.create_all(engine)
|
Base.metadata.create_all(engine)
|
||||||
Session.configure(bind=engine)
|
Session.configure(bind=engine)
|
||||||
|
@ -157,12 +188,16 @@ class RssUpdates(HttpRequest):
|
||||||
|
|
||||||
content = u'''
|
content = u'''
|
||||||
<h1 style="{title_style}">{title}</h1>
|
<h1 style="{title_style}">{title}</h1>
|
||||||
<h2 style="{subtitle_style}">Feeds digest generated on {creation_date}</h2>'''.\
|
<h2 style="{subtitle_style}">Feeds digest generated on {creation_date}</h2>'''.format(
|
||||||
format(title_style=self.title_style, title=self.title, subtitle_style=self.subtitle_style,
|
title_style=self.title_style,
|
||||||
creation_date=datetime.datetime.now().strftime('%d %B %Y, %H:%M'))
|
title=self.title,
|
||||||
|
subtitle_style=self.subtitle_style,
|
||||||
|
creation_date=datetime.datetime.now().strftime('%d %B %Y, %H:%M'),
|
||||||
|
)
|
||||||
|
|
||||||
self.logger.info('Parsed {:d} items from RSS feed <{}>'
|
self.logger.info(
|
||||||
.format(len(feed.entries), self.url))
|
'Parsed {:d} items from RSS feed <{}>'.format(len(feed.entries), self.url)
|
||||||
|
)
|
||||||
|
|
||||||
for entry in feed.entries:
|
for entry in feed.entries:
|
||||||
if not entry.published_parsed:
|
if not entry.published_parsed:
|
||||||
|
@ -171,9 +206,10 @@ class RssUpdates(HttpRequest):
|
||||||
try:
|
try:
|
||||||
entry_timestamp = datetime.datetime(*entry.published_parsed[:6])
|
entry_timestamp = datetime.datetime(*entry.published_parsed[:6])
|
||||||
|
|
||||||
if latest_update is None \
|
if latest_update is None or entry_timestamp > latest_update:
|
||||||
or entry_timestamp > latest_update:
|
self.logger.info(
|
||||||
self.logger.info('Processed new item from RSS feed <{}>'.format(self.url))
|
'Processed new item from RSS feed <{}>'.format(self.url)
|
||||||
|
)
|
||||||
entry.summary = entry.summary if hasattr(entry, 'summary') else None
|
entry.summary = entry.summary if hasattr(entry, 'summary') else None
|
||||||
|
|
||||||
if self.extract_content:
|
if self.extract_content:
|
||||||
|
@ -188,9 +224,13 @@ class RssUpdates(HttpRequest):
|
||||||
<a href="{link}" target="_blank" style="{article_link_style}">{title}</a>
|
<a href="{link}" target="_blank" style="{article_link_style}">{title}</a>
|
||||||
</h1>
|
</h1>
|
||||||
<div class="_parsed-content" style="{article_content_style}">{content}</div>'''.format(
|
<div class="_parsed-content" style="{article_content_style}">{content}</div>'''.format(
|
||||||
article_title_style=self.article_title_style, article_link_style=self.article_link_style,
|
article_title_style=self.article_title_style,
|
||||||
article_content_style=self.article_content_style, link=entry.link, title=entry.title,
|
article_link_style=self.article_link_style,
|
||||||
content=entry.content)
|
article_content_style=self.article_content_style,
|
||||||
|
link=entry.link,
|
||||||
|
title=entry.title,
|
||||||
|
content=entry.content,
|
||||||
|
)
|
||||||
|
|
||||||
e = {
|
e = {
|
||||||
'entry_id': entry.id,
|
'entry_id': entry.id,
|
||||||
|
@ -207,21 +247,32 @@ class RssUpdates(HttpRequest):
|
||||||
if self.max_entries and len(entries) > self.max_entries:
|
if self.max_entries and len(entries) > self.max_entries:
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.warning('Exception encountered while parsing RSS ' +
|
self.logger.warning(
|
||||||
'RSS feed {}: {}'.format(entry.link, str(e)))
|
'Exception encountered while parsing RSS '
|
||||||
|
+ f'RSS feed {entry.link}: {e}'
|
||||||
|
)
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
|
|
||||||
source_record.last_updated_at = parse_start_time
|
source_record.last_updated_at = parse_start_time
|
||||||
digest_filename = None
|
digest_filename = None
|
||||||
|
|
||||||
if entries:
|
if entries:
|
||||||
self.logger.info('Parsed {} new entries from the RSS feed {}'.format(
|
self.logger.info(
|
||||||
len(entries), self.title))
|
'Parsed {} new entries from the RSS feed {}'.format(
|
||||||
|
len(entries), self.title
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if self.digest_format:
|
if self.digest_format:
|
||||||
digest_filename = os.path.join(self.workdir, 'cache', '{}_{}.{}'.format(
|
digest_filename = os.path.join(
|
||||||
datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
|
self.workdir,
|
||||||
self.title, self.digest_format))
|
'cache',
|
||||||
|
'{}_{}.{}'.format(
|
||||||
|
datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
|
||||||
|
self.title,
|
||||||
|
self.digest_format,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(digest_filename), exist_ok=True)
|
os.makedirs(os.path.dirname(digest_filename), exist_ok=True)
|
||||||
|
|
||||||
|
@ -233,12 +284,15 @@ class RssUpdates(HttpRequest):
|
||||||
</head>
|
</head>
|
||||||
<body style="{body_style}">{content}</body>
|
<body style="{body_style}">{content}</body>
|
||||||
</html>
|
</html>
|
||||||
'''.format(title=self.title, body_style=self.body_style, content=content)
|
'''.format(
|
||||||
|
title=self.title, body_style=self.body_style, content=content
|
||||||
|
)
|
||||||
|
|
||||||
with open(digest_filename, 'w', encoding='utf-8') as f:
|
with open(digest_filename, 'w', encoding='utf-8') as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
elif self.digest_format == 'pdf':
|
elif self.digest_format == 'pdf':
|
||||||
from weasyprint import HTML, CSS
|
from weasyprint import HTML, CSS
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from weasyprint.fonts import FontConfiguration
|
from weasyprint.fonts import FontConfiguration
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -246,37 +300,47 @@ class RssUpdates(HttpRequest):
|
||||||
|
|
||||||
body_style = 'body { ' + self.body_style + ' }'
|
body_style = 'body { ' + self.body_style + ' }'
|
||||||
font_config = FontConfiguration()
|
font_config = FontConfiguration()
|
||||||
css = [CSS('https://fonts.googleapis.com/css?family=Merriweather'),
|
css = [
|
||||||
CSS(string=body_style, font_config=font_config)]
|
CSS('https://fonts.googleapis.com/css?family=Merriweather'),
|
||||||
|
CSS(string=body_style, font_config=font_config),
|
||||||
|
]
|
||||||
|
|
||||||
HTML(string=content).write_pdf(digest_filename, stylesheets=css)
|
HTML(string=content).write_pdf(digest_filename, stylesheets=css)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('Unsupported format: {}. Supported formats: ' +
|
raise RuntimeError(
|
||||||
'html or pdf'.format(self.digest_format))
|
f'Unsupported format: {self.digest_format}. Supported formats: html, pdf'
|
||||||
|
)
|
||||||
|
|
||||||
digest_entry = FeedDigest(source_id=source_record.id,
|
digest_entry = FeedDigest(
|
||||||
format=self.digest_format,
|
source_id=source_record.id,
|
||||||
filename=digest_filename)
|
format=self.digest_format,
|
||||||
|
filename=digest_filename,
|
||||||
|
)
|
||||||
|
|
||||||
session.add(digest_entry)
|
session.add(digest_entry)
|
||||||
self.logger.info('{} digest ready: {}'.format(self.digest_format, digest_filename))
|
self.logger.info(
|
||||||
|
'{} digest ready: {}'.format(self.digest_format, digest_filename)
|
||||||
|
)
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
self.logger.info('Parsing RSS feed {}: completed'.format(self.title))
|
self.logger.info('Parsing RSS feed {}: completed'.format(self.title))
|
||||||
|
|
||||||
return NewFeedEvent(request=dict(self), response=entries,
|
return NewFeedEvent(
|
||||||
source_id=source_record.id,
|
request=dict(self),
|
||||||
source_title=source_record.title,
|
response=entries,
|
||||||
title=self.title,
|
source_id=source_record.id,
|
||||||
digest_format=self.digest_format,
|
source_title=source_record.title,
|
||||||
digest_filename=digest_filename)
|
title=self.title,
|
||||||
|
digest_format=self.digest_format,
|
||||||
|
digest_filename=digest_filename,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FeedSource(Base):
|
class FeedSource(Base):
|
||||||
""" Models the FeedSource table, containing RSS sources to be parsed """
|
"""Models the FeedSource table, containing RSS sources to be parsed"""
|
||||||
|
|
||||||
__tablename__ = 'FeedSource'
|
__tablename__ = 'FeedSource'
|
||||||
__table_args__ = ({'sqlite_autoincrement': True})
|
__table_args__ = {'sqlite_autoincrement': True}
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
title = Column(String)
|
title = Column(String)
|
||||||
|
@ -285,10 +349,10 @@ class FeedSource(Base):
|
||||||
|
|
||||||
|
|
||||||
class FeedEntry(Base):
|
class FeedEntry(Base):
|
||||||
""" Models the FeedEntry table, which contains RSS entries """
|
"""Models the FeedEntry table, which contains RSS entries"""
|
||||||
|
|
||||||
__tablename__ = 'FeedEntry'
|
__tablename__ = 'FeedEntry'
|
||||||
__table_args__ = ({'sqlite_autoincrement': True})
|
__table_args__ = {'sqlite_autoincrement': True}
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
entry_id = Column(String)
|
entry_id = Column(String)
|
||||||
|
@ -301,15 +365,15 @@ class FeedEntry(Base):
|
||||||
|
|
||||||
|
|
||||||
class FeedDigest(Base):
|
class FeedDigest(Base):
|
||||||
""" Models the FeedDigest table, containing feed digests either in HTML
|
"""Models the FeedDigest table, containing feed digests either in HTML
|
||||||
or PDF format """
|
or PDF format"""
|
||||||
|
|
||||||
class DigestFormat(enum.Enum):
|
class DigestFormat(enum.Enum):
|
||||||
html = 1
|
html = 1
|
||||||
pdf = 2
|
pdf = 2
|
||||||
|
|
||||||
__tablename__ = 'FeedDigest'
|
__tablename__ = 'FeedDigest'
|
||||||
__table_args__ = ({'sqlite_autoincrement': True})
|
__table_args__ = {'sqlite_autoincrement': True}
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
source_id = Column(Integer, ForeignKey('FeedSource.id'), nullable=False)
|
source_id = Column(Integer, ForeignKey('FeedSource.id'), nullable=False)
|
||||||
|
@ -317,4 +381,5 @@ class FeedDigest(Base):
|
||||||
filename = Column(String, nullable=False)
|
filename = Column(String, nullable=False)
|
||||||
created_at = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
|
created_at = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
BIN
platypush/backend/http/webapp/dist/fonts/Poppins.ttf
vendored
Normal file
BIN
platypush/backend/http/webapp/dist/fonts/Poppins.ttf
vendored
Normal file
Binary file not shown.
7
platypush/backend/http/webapp/dist/fonts/poppins.css
vendored
Normal file
7
platypush/backend/http/webapp/dist/fonts/poppins.css
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(./Poppins.ttf) format('truetype');
|
||||||
|
}
|
BIN
platypush/backend/http/webapp/dist/icons/smartthings.png
vendored
Normal file
BIN
platypush/backend/http/webapp/dist/icons/smartthings.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4 KiB |
BIN
platypush/backend/http/webapp/dist/img/logo.png
vendored
Normal file
BIN
platypush/backend/http/webapp/dist/img/logo.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
BIN
platypush/backend/http/webapp/dist/img/spinner.gif
vendored
Normal file
BIN
platypush/backend/http/webapp/dist/img/spinner.gif
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
|
@ -1 +1 @@
|
||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>platypush</title><script defer="defer" type="module" src="/static/js/chunk-vendors.5adf1720.js"></script><script defer="defer" type="module" src="/static/js/app.12b17001.js"></script><link href="/static/css/chunk-vendors.5cf89a0c.css" rel="stylesheet"><link href="/static/css/app.5028a669.css" rel="stylesheet"><script defer="defer" src="/static/js/chunk-vendors-legacy.6835b8d0.js" nomodule></script><script defer="defer" src="/static/js/app-legacy.ab28664f.js" nomodule></script></head><body><noscript><strong>We're sorry but platypush doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><link rel="stylesheet" href="/fonts/poppins.css"><title>platypush</title><script defer="defer" type="module" src="/static/js/chunk-vendors.95bedba1.js"></script><script defer="defer" type="module" src="/static/js/app.0ecd5641.js"></script><link href="/static/css/chunk-vendors.0fcd36f0.css" rel="stylesheet"><link href="/static/css/app.3b5b9cec.css" rel="stylesheet"><script defer="defer" src="/static/js/chunk-vendors-legacy.c27e0a41.js" nomodule></script><script defer="defer" src="/static/js/app-legacy.602f8c67.js" nomodule></script></head><body><noscript><strong>We're sorry but platypush doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
1
platypush/backend/http/webapp/dist/static/css/1155.3c072b53.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/1155.3c072b53.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/1406.3c45f7ef.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/1406.3c45f7ef.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/2107.3a08bbb5.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/2107.3a08bbb5.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/2790.8a938bab.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/2790.8a938bab.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/3490.3516cb6e.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/3490.3516cb6e.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/4196.f1fcf8f5.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/4196.f1fcf8f5.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/4658.0aa0c9b4.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/4658.0aa0c9b4.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/4848.ae3af6a6.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/4848.ae3af6a6.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/4981.8830c3ce.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/4981.8830c3ce.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/518.44f63b6e.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/518.44f63b6e.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/5779.b285a776.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/5779.b285a776.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/5810.0aa0c9b4.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/5810.0aa0c9b4.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/5824.f9f7ad29.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/5824.f9f7ad29.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/6162.f3d46bda.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/6162.f3d46bda.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/65.d6cbc229.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/65.d6cbc229.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/6833.c53bec53.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/6833.c53bec53.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/6899.f3d46bda.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/6899.f3d46bda.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/7029.13387da1.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/7029.13387da1.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/747.3c45f7ef.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/747.3c45f7ef.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/7643.e25374a8.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/7643.e25374a8.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/8729.f98d84da.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/8729.f98d84da.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/9892.68b29dbb.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/9892.68b29dbb.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/9978.13387da1.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/9978.13387da1.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
17
platypush/backend/http/webapp/dist/static/css/app.3b5b9cec.css
vendored
Normal file
17
platypush/backend/http/webapp/dist/static/css/app.3b5b9cec.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
platypush/backend/http/webapp/dist/static/img/logo.5b906db6.png
vendored
Normal file
BIN
platypush/backend/http/webapp/dist/static/img/logo.5b906db6.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
BIN
platypush/backend/http/webapp/dist/static/img/spinner.c0bee445.gif
vendored
Normal file
BIN
platypush/backend/http/webapp/dist/static/img/spinner.c0bee445.gif
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
2
platypush/backend/http/webapp/dist/static/js/1155-legacy.3b386edd.js
vendored
Normal file
2
platypush/backend/http/webapp/dist/static/js/1155-legacy.3b386edd.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/js/1155-legacy.3b386edd.js.map
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/js/1155-legacy.3b386edd.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
platypush/backend/http/webapp/dist/static/js/1155.ae99e2b9.js
vendored
Normal file
2
platypush/backend/http/webapp/dist/static/js/1155.ae99e2b9.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/js/1155.ae99e2b9.js.map
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/js/1155.ae99e2b9.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
platypush/backend/http/webapp/dist/static/js/1406-legacy.49afea8a.js
vendored
Normal file
2
platypush/backend/http/webapp/dist/static/js/1406-legacy.49afea8a.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/js/1406-legacy.49afea8a.js.map
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/js/1406-legacy.49afea8a.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,2 +1,2 @@
|
||||||
"use strict";(self["webpackChunkplatypush"]=self["webpackChunkplatypush"]||[]).push([[1595],{1595:function(e,t,n){n.r(t),n.d(t,{default:function(){return f}});var s=n(6252),o=n(3577),i={class:"date-time"},a=["textContent"],r=["textContent"];function u(e,t,n,u,h,w){return(0,s.wg)(),(0,s.iD)("div",i,[w._showDate?((0,s.wg)(),(0,s.iD)("div",{key:0,class:"date",textContent:(0,o.zw)(e.formatDate(e.now))},null,8,a)):(0,s.kq)("",!0),w._showTime?((0,s.wg)(),(0,s.iD)("div",{key:1,class:"time",textContent:(0,o.zw)(e.formatTime(e.now,w._showSeconds))},null,8,r)):(0,s.kq)("",!0)])}var h=n(2628),w={name:"DateTime",mixins:[h.Z],props:{showDate:{required:!1,default:!0},showTime:{required:!1,default:!0},showSeconds:{required:!1,default:!0}},computed:{_showTime:function(){return this.parseBoolean(this.showTime)},_showDate:function(){return this.parseBoolean(this.showDate)},_showSeconds:function(){return this.parseBoolean(this.showSeconds)}},data:function(){return{now:new Date}},methods:{refreshTime:function(){this.now=new Date}},mounted:function(){this.refreshTime(),setInterval(this.refreshTime,1e3)}},c=n(3744);const d=(0,c.Z)(w,[["render",u],["__scopeId","data-v-ca42eb9c"]]);var f=d}}]);
|
"use strict";(self["webpackChunkplatypush"]=self["webpackChunkplatypush"]||[]).push([[1595],{1595:function(e,t,n){n.r(t),n.d(t,{default:function(){return f}});var s=n(6252),o=n(3577),i={class:"date-time"},a=["textContent"],r=["textContent"];function u(e,t,n,u,h,w){return(0,s.wg)(),(0,s.iD)("div",i,[w._showDate?((0,s.wg)(),(0,s.iD)("div",{key:0,class:"date",textContent:(0,o.zw)(e.formatDate(e.now))},null,8,a)):(0,s.kq)("",!0),w._showTime?((0,s.wg)(),(0,s.iD)("div",{key:1,class:"time",textContent:(0,o.zw)(e.formatTime(e.now,w._showSeconds))},null,8,r)):(0,s.kq)("",!0)])}var h=n(6813),w={name:"DateTime",mixins:[h.Z],props:{showDate:{required:!1,default:!0},showTime:{required:!1,default:!0},showSeconds:{required:!1,default:!0}},computed:{_showTime:function(){return this.parseBoolean(this.showTime)},_showDate:function(){return this.parseBoolean(this.showDate)},_showSeconds:function(){return this.parseBoolean(this.showSeconds)}},data:function(){return{now:new Date}},methods:{refreshTime:function(){this.now=new Date}},mounted:function(){this.refreshTime(),setInterval(this.refreshTime,1e3)}},c=n(3744);const d=(0,c.Z)(w,[["render",u],["__scopeId","data-v-ca42eb9c"]]);var f=d}}]);
|
||||||
//# sourceMappingURL=1595-legacy.ddcdc704.js.map
|
//# sourceMappingURL=1595-legacy.69aea4ae.js.map
|
1
platypush/backend/http/webapp/dist/static/js/1595-legacy.69aea4ae.js.map
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/js/1595-legacy.69aea4ae.js.map
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"static/js/1595-legacy.69aea4ae.js","mappings":"0LACOA,MAAM,a,8EAAX,QAGM,MAHN,EAGM,CAF6C,EAAAC,YAAA,WAAjD,QAA8D,O,MAAzDD,MAAM,O,aAAO,QAAwB,EAAN,WAAC,EAAAE,OAArC,2BAC+D,EAAAC,YAAA,WAA/D,QAA4E,O,MAAvEH,MAAM,O,aAAO,QAAsC,EAApB,WAAC,EAAAE,IAAK,EAAAE,gBAA1C,4B,eAQJ,GACEC,KAAM,WACNC,OAAQ,CAACC,EAAA,GACTC,MAAO,CAELC,SAAU,CACRC,UAAU,EACVC,SAAS,GAIXC,SAAU,CACRF,UAAU,EACVC,SAAS,GAIXE,YAAa,CACXH,UAAU,EACVC,SAAS,IAIbG,SAAU,CACRX,UADQ,WAEN,OAAOY,KAAKC,aAAaD,KAAKH,SAC/B,EAEDX,UALQ,WAMN,OAAOc,KAAKC,aAAaD,KAAKN,SAC/B,EAEDL,aATQ,WAUN,OAAOW,KAAKC,aAAaD,KAAKF,YAC/B,GAGHI,KAAM,WACJ,MAAO,CACLf,IAAK,IAAIgB,KAEZ,EAEDC,QAAS,CACPC,YADO,WAELL,KAAKb,IAAM,IAAIgB,IAChB,GAGHG,QAAS,WACPN,KAAKK,cACLE,YAAYP,KAAKK,YAAa,IAC/B,G,UCxDH,MAAMG,GAA2B,OAAgB,EAAQ,CAAC,CAAC,SAASC,GAAQ,CAAC,YAAY,qBAEzF,O","sources":["webpack://platypush/./src/components/widgets/DateTime/Index.vue","webpack://platypush/./src/components/widgets/DateTime/Index.vue?dfd6"],"sourcesContent":["<template>\n <div class=\"date-time\">\n <div class=\"date\" v-text=\"formatDate(now)\" v-if=\"_showDate\" />\n <div class=\"time\" v-text=\"formatTime(now, _showSeconds)\" v-if=\"_showTime\" />\n </div>\n</template>\n\n<script>\nimport Utils from \"@/Utils\";\n\n// Widget to show date and time\nexport default {\n name: 'DateTime',\n mixins: [Utils],\n props: {\n // If false then don't display the date.\n showDate: {\n required: false,\n default: true,\n },\n\n // If false then don't display the time.\n showTime: {\n required: false,\n default: true,\n },\n\n // If false then don't display the seconds.\n showSeconds: {\n required: false,\n default: true,\n },\n },\n\n computed: {\n _showTime() {\n return this.parseBoolean(this.showTime)\n },\n\n _showDate() {\n return this.parseBoolean(this.showDate)\n },\n\n _showSeconds() {\n return this.parseBoolean(this.showSeconds)\n },\n },\n\n data: function() {\n return {\n now: new Date(),\n };\n },\n\n methods: {\n refreshTime() {\n this.now = new Date()\n },\n },\n\n mounted: function() {\n this.refreshTime()\n setInterval(this.refreshTime, 1000)\n },\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.date-time {\n .date {\n font-size: 1.3em;\n }\n\n .time {\n font-size: 2em;\n }\n}\n</style>\n","import { render } from \"./Index.vue?vue&type=template&id=ca42eb9c&scoped=true\"\nimport script from \"./Index.vue?vue&type=script&lang=js\"\nexport * from \"./Index.vue?vue&type=script&lang=js\"\n\nimport \"./Index.vue?vue&type=style&index=0&id=ca42eb9c&lang=scss&scoped=true\"\n\nimport exportComponent from \"/home/blacklight/git_tree/platypush/platypush/backend/http/webapp/node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render],['__scopeId',\"data-v-ca42eb9c\"]])\n\nexport default __exports__"],"names":["class","_showDate","now","_showTime","_showSeconds","name","mixins","Utils","props","showDate","required","default","showTime","showSeconds","computed","this","parseBoolean","data","Date","methods","refreshTime","mounted","setInterval","__exports__","render"],"sourceRoot":""}
|
|
@ -1 +0,0 @@
|
||||||
{"version":3,"file":"static/js/1595-legacy.ddcdc704.js","mappings":"0LACOA,MAAM,a,8EAAX,QAGM,MAHN,EAGM,CAF6C,EAAAC,YAAA,WAAjD,QAA8D,O,MAAzDD,MAAM,O,aAAO,QAAwB,EAAN,WAAC,EAAAE,OAArC,2BAC+D,EAAAC,YAAA,WAA/D,QAA4E,O,MAAvEH,MAAM,O,aAAO,QAAsC,EAApB,WAAC,EAAAE,IAAK,EAAAE,gBAA1C,6B,cAQJ,GACEC,KAAM,WACNC,OAAQ,CAACC,EAAA,GACTC,MAAO,CAELC,SAAU,CACRC,UAAU,EACVC,SAAS,GAIXC,SAAU,CACRF,UAAU,EACVC,SAAS,GAIXE,YAAa,CACXH,UAAU,EACVC,SAAS,IAIbG,SAAU,CACRX,UADQ,WAEN,OAAOY,KAAKC,aAAaD,KAAKH,WAGhCX,UALQ,WAMN,OAAOc,KAAKC,aAAaD,KAAKN,WAGhCL,aATQ,WAUN,OAAOW,KAAKC,aAAaD,KAAKF,eAIlCI,KAAM,WACJ,MAAO,CACLf,IAAK,IAAIgB,OAIbC,QAAS,CACPC,YADO,WAELL,KAAKb,IAAM,IAAIgB,OAInBG,QAAS,WACPN,KAAKK,cACLE,YAAYP,KAAKK,YAAa,O,UCvDlC,MAAMG,GAA2B,OAAgB,EAAQ,CAAC,CAAC,SAASC,GAAQ,CAAC,YAAY,qBAEzF","sources":["webpack://platypush/./src/components/widgets/DateTime/Index.vue","webpack://platypush/./src/components/widgets/DateTime/Index.vue?dfd6"],"sourcesContent":["<template>\n <div class=\"date-time\">\n <div class=\"date\" v-text=\"formatDate(now)\" v-if=\"_showDate\" />\n <div class=\"time\" v-text=\"formatTime(now, _showSeconds)\" v-if=\"_showTime\" />\n </div>\n</template>\n\n<script>\nimport Utils from \"@/Utils\";\n\n// Widget to show date and time\nexport default {\n name: 'DateTime',\n mixins: [Utils],\n props: {\n // If false then don't display the date.\n showDate: {\n required: false,\n default: true,\n },\n\n // If false then don't display the time.\n showTime: {\n required: false,\n default: true,\n },\n\n // If false then don't display the seconds.\n showSeconds: {\n required: false,\n default: true,\n },\n },\n\n computed: {\n _showTime() {\n return this.parseBoolean(this.showTime)\n },\n\n _showDate() {\n return this.parseBoolean(this.showDate)\n },\n\n _showSeconds() {\n return this.parseBoolean(this.showSeconds)\n },\n },\n\n data: function() {\n return {\n now: new Date(),\n };\n },\n\n methods: {\n refreshTime() {\n this.now = new Date()\n },\n },\n\n mounted: function() {\n this.refreshTime()\n setInterval(this.refreshTime, 1000)\n },\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.date-time {\n .date {\n font-size: 1.3em;\n }\n\n .time {\n font-size: 2em;\n }\n}\n</style>\n","import { render } from \"./Index.vue?vue&type=template&id=ca42eb9c&scoped=true\"\nimport script from \"./Index.vue?vue&type=script&lang=js\"\nexport * from \"./Index.vue?vue&type=script&lang=js\"\n\nimport \"./Index.vue?vue&type=style&index=0&id=ca42eb9c&lang=scss&scoped=true\"\n\nimport exportComponent from \"/home/blacklight/git_tree/platypush/platypush/backend/http/webapp/node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render],['__scopeId',\"data-v-ca42eb9c\"]])\n\nexport default __exports__"],"names":["class","_showDate","now","_showTime","_showSeconds","name","mixins","Utils","props","showDate","required","default","showTime","showSeconds","computed","this","parseBoolean","data","Date","methods","refreshTime","mounted","setInterval","__exports__","render"],"sourceRoot":""}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue