项目:painting 主题
- 项目初始版本 - 项目默认主题 painting
44
.github/workflows/build-and-run.yml
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
name: Build and Run
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build-and-run:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: 1. Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 2. Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: 3. Install
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
|
||||
- name: 4. Run
|
||||
run: |
|
||||
python ./main.py &
|
||||
sleep 7
|
||||
|
||||
- name: 5. Build
|
||||
run: |
|
||||
wget http://localhost:7777
|
||||
|
||||
- name: 6. Log
|
||||
run: |
|
||||
cat endofyear.log
|
||||
|
||||
- name: 7. Push static HTML
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./static
|
||||
publish_branch: html
|
24
.github/workflows/docker-publish.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
name: Publish Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
run: |
|
||||
docker build -t sevenwate/endofyear:latest .
|
||||
docker push sevenwate/endofyear:latest
|
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/.idea
|
||||
*.log
|
||||
/Pipfile
|
||||
/Pipfile.lock
|
11
Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM python:3.11.0
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ADD . /app
|
||||
|
||||
RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && pip install -r requirements.txt
|
||||
|
||||
EXPOSE 7777
|
||||
|
||||
CMD [ "python", "./main.py" ]
|
625
License
Normal file
@ -0,0 +1,625 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||
document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for software and
|
||||
other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed to take
|
||||
away your freedom to share and change the works. By contrast, the GNU General
|
||||
Public License is intended to guarantee your freedom to share and change all
|
||||
versions of a program--to make sure it remains free software for all its users.
|
||||
We, the Free Software Foundation, use the GNU General Public License for most
|
||||
of our software; it applies also to any other work released this way by its
|
||||
authors. You can apply it to your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not price. Our
|
||||
General Public Licenses are designed to make sure that you have the freedom
|
||||
to distribute copies of free software (and charge for them if you wish), that
|
||||
you receive source code or can get it if you want it, that you can change
|
||||
the software or use pieces of it in new free programs, and that you know you
|
||||
can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you these rights
|
||||
or asking you to surrender the rights. Therefore, you have certain responsibilities
|
||||
if you distribute copies of the software, or if you modify it: responsibilities
|
||||
to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether gratis or
|
||||
for a fee, you must pass on to the recipients the same freedoms that you received.
|
||||
You must make sure that they, too, receive or can get the source code. And
|
||||
you must show them these terms so they know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps: (1) assert
|
||||
copyright on the software, and (2) offer you this License giving you legal
|
||||
permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains that
|
||||
there is no warranty for this free software. For both users' and authors'
|
||||
sake, the GPL requires that modified versions be marked as changed, so that
|
||||
their problems will not be attributed erroneously to authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run modified
|
||||
versions of the software inside them, although the manufacturer can do so.
|
||||
This is fundamentally incompatible with the aim of protecting users' freedom
|
||||
to change the software. The systematic pattern of such abuse occurs in the
|
||||
area of products for individuals to use, which is precisely where it is most
|
||||
unacceptable. Therefore, we have designed this version of the GPL to prohibit
|
||||
the practice for those products. If such problems arise substantially in other
|
||||
domains, we stand ready to extend this provision to those domains in future
|
||||
versions of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents. States
|
||||
should not allow patents to restrict development and use of software on general-purpose
|
||||
computers, but in those that do, we wish to avoid the special danger that
|
||||
patents applied to a free program could make it effectively proprietary. To
|
||||
prevent this, the GPL assures that patents cannot be used to render the program
|
||||
non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification
|
||||
follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of works,
|
||||
such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this License.
|
||||
Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals
|
||||
or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work in
|
||||
a fashion requiring copyright permission, other than the making of an exact
|
||||
copy. The resulting work is called a "modified version" of the earlier work
|
||||
or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based on the
|
||||
Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without permission,
|
||||
would make you directly or secondarily liable for infringement under applicable
|
||||
copyright law, except executing it on a computer or modifying a private copy.
|
||||
Propagation includes copying, distribution (with or without modification),
|
||||
making available to the public, and in some countries other activities as
|
||||
well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other parties
|
||||
to make or receive copies. Mere interaction with a user through a computer
|
||||
network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices" to the
|
||||
extent that it includes a convenient and prominently visible feature that
|
||||
(1) displays an appropriate copyright notice, and (2) tells the user that
|
||||
there is no warranty for the work (except to the extent that warranties are
|
||||
provided), that licensees may convey the work under this License, and how
|
||||
to view a copy of this License. If the interface presents a list of user commands
|
||||
or options, such as a menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work for making
|
||||
modifications to it. "Object code" means any non-source form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official standard
|
||||
defined by a recognized standards body, or, in the case of interfaces specified
|
||||
for a particular programming language, one that is widely used among developers
|
||||
working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other than
|
||||
the work as a whole, that (a) is included in the normal form of packaging
|
||||
a Major Component, but which is not part of that Major Component, and (b)
|
||||
serves only to enable use of the work with that Major Component, or to implement
|
||||
a Standard Interface for which an implementation is available to the public
|
||||
in source code form. A "Major Component", in this context, means a major essential
|
||||
component (kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to produce
|
||||
the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all the source
|
||||
code needed to generate, install, and (for an executable work) run the object
|
||||
code and to modify the work, including scripts to control those activities.
|
||||
However, it does not include the work's System Libraries, or general-purpose
|
||||
tools or generally available free programs which are used unmodified in performing
|
||||
those activities but which are not part of the work. For example, Corresponding
|
||||
Source includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically linked
|
||||
subprograms that the work is specifically designed to require, such as by
|
||||
intimate data communication or control flow between those subprograms and
|
||||
other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users can regenerate
|
||||
automatically from other parts of the Corresponding Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of copyright
|
||||
on the Program, and are irrevocable provided the stated conditions are met.
|
||||
This License explicitly affirms your unlimited permission to run the unmodified
|
||||
Program. The output from running a covered work is covered by this License
|
||||
only if the output, given its content, constitutes a covered work. This License
|
||||
acknowledges your rights of fair use or other equivalent, as provided by copyright
|
||||
law.
|
||||
|
||||
You may make, run and propagate covered works that you do not convey, without
|
||||
conditions so long as your license otherwise remains in force. You may convey
|
||||
covered works to others for the sole purpose of having them make modifications
|
||||
exclusively for you, or provide you with facilities for running those works,
|
||||
provided that you comply with the terms of this License in conveying all material
|
||||
for which you do not control copyright. Those thus making or running the covered
|
||||
works for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of your copyrighted
|
||||
material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under the conditions
|
||||
stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological measure
|
||||
under any applicable law fulfilling obligations under article 11 of the WIPO
|
||||
copyright treaty adopted on 20 December 1996, or similar laws prohibiting
|
||||
or restricting circumvention of such measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid circumvention
|
||||
of technological measures to the extent such circumvention is effected by
|
||||
exercising rights under this License with respect to the covered work, and
|
||||
you disclaim any intention to limit operation or modification of the work
|
||||
as a means of enforcing, against the work's users, your or third parties'
|
||||
legal rights to forbid circumvention of technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you receive
|
||||
it, in any medium, provided that you conspicuously and appropriately publish
|
||||
on each copy an appropriate copyright notice; keep intact all notices stating
|
||||
that this License and any non-permissive terms added in accord with section
|
||||
7 apply to the code; keep intact all notices of the absence of any warranty;
|
||||
and give all recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey, and you
|
||||
may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to produce
|
||||
it from the Program, in the form of source code under the terms of section
|
||||
4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified it, and
|
||||
giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is released under
|
||||
this License and any conditions added under section 7. This requirement modifies
|
||||
the requirement in section 4 to "keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this License to anyone
|
||||
who comes into possession of a copy. This License will therefore apply, along
|
||||
with any applicable section 7 additional terms, to the whole of the work,
|
||||
and all its parts, regardless of how they are packaged. This License gives
|
||||
no permission to license the work in any other way, but it does not invalidate
|
||||
such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display Appropriate
|
||||
Legal Notices; however, if the Program has interactive interfaces that do
|
||||
not display Appropriate Legal Notices, your work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent works,
|
||||
which are not by their nature extensions of the covered work, and which are
|
||||
not combined with it such as to form a larger program, in or on a volume of
|
||||
a storage or distribution medium, is called an "aggregate" if the compilation
|
||||
and its resulting copyright are not used to limit the access or legal rights
|
||||
of the compilation's users beyond what the individual works permit. Inclusion
|
||||
of a covered work in an aggregate does not cause this License to apply to
|
||||
the other parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms of sections
|
||||
4 and 5, provided that you also convey the machine-readable Corresponding
|
||||
Source under the terms of this License, in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product (including
|
||||
a physical distribution medium), accompanied by the Corresponding Source fixed
|
||||
on a durable physical medium customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product (including
|
||||
a physical distribution medium), accompanied by a written offer, valid for
|
||||
at least three years and valid for as long as you offer spare parts or customer
|
||||
support for that product model, to give anyone who possesses the object code
|
||||
either (1) a copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical medium customarily
|
||||
used for software interchange, for a price no more than your reasonable cost
|
||||
of physically performing this conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the written
|
||||
offer to provide the Corresponding Source. This alternative is allowed only
|
||||
occasionally and noncommercially, and only if you received the object code
|
||||
with such an offer, in accord with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated place (gratis
|
||||
or for a charge), and offer equivalent access to the Corresponding Source
|
||||
in the same way through the same place at no further charge. You need not
|
||||
require recipients to copy the Corresponding Source along with the object
|
||||
code. If the place to copy the object code is a network server, the Corresponding
|
||||
Source may be on a different server (operated by you or a third party) that
|
||||
supports equivalent copying facilities, provided you maintain clear directions
|
||||
next to the object code saying where to find the Corresponding Source. Regardless
|
||||
of what server hosts the Corresponding Source, you remain obligated to ensure
|
||||
that it is available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided you inform
|
||||
other peers where the object code and Corresponding Source of the work are
|
||||
being offered to the general public at no charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded from
|
||||
the Corresponding Source as a System Library, need not be included in conveying
|
||||
the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any tangible
|
||||
personal property which is normally used for personal, family, or household
|
||||
purposes, or (2) anything designed or sold for incorporation into a dwelling.
|
||||
In determining whether a product is a consumer product, doubtful cases shall
|
||||
be resolved in favor of coverage. For a particular product received by a particular
|
||||
user, "normally used" refers to a typical or common use of that class of product,
|
||||
regardless of the status of the particular user or of the way in which the
|
||||
particular user actually uses, or expects or is expected to use, the product.
|
||||
A product is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent the
|
||||
only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods, procedures,
|
||||
authorization keys, or other information required to install and execute modified
|
||||
versions of a covered work in that User Product from a modified version of
|
||||
its Corresponding Source. The information must suffice to ensure that the
|
||||
continued functioning of the modified object code is in no case prevented
|
||||
or interfered with solely because modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or specifically
|
||||
for use in, a User Product, and the conveying occurs as part of a transaction
|
||||
in which the right of possession and use of the User Product is transferred
|
||||
to the recipient in perpetuity or for a fixed term (regardless of how the
|
||||
transaction is characterized), the Corresponding Source conveyed under this
|
||||
section must be accompanied by the Installation Information. But this requirement
|
||||
does not apply if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has been installed
|
||||
in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a requirement
|
||||
to continue to provide support service, warranty, or updates for a work that
|
||||
has been modified or installed by the recipient, or for the User Product in
|
||||
which it has been modified or installed. Access to a network may be denied
|
||||
when the modification itself materially and adversely affects the operation
|
||||
of the network or violates the rules and protocols for communication across
|
||||
the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, in accord
|
||||
with this section must be in a format that is publicly documented (and with
|
||||
an implementation available to the public in source code form), and must require
|
||||
no special password or key for unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this License
|
||||
by making exceptions from one or more of its conditions. Additional permissions
|
||||
that are applicable to the entire Program shall be treated as though they
|
||||
were included in this License, to the extent that they are valid under applicable
|
||||
law. If additional permissions apply only to part of the Program, that part
|
||||
may be used separately under those permissions, but the entire Program remains
|
||||
governed by this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option remove any
|
||||
additional permissions from that copy, or from any part of it. (Additional
|
||||
permissions may be written to require their own removal in certain cases when
|
||||
you modify the work.) You may place additional permissions on material, added
|
||||
by you to a covered work, for which you have or can give appropriate copyright
|
||||
permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you add
|
||||
to a covered work, you may (if authorized by the copyright holders of that
|
||||
material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the terms of
|
||||
sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or author
|
||||
attributions in that material or in the Appropriate Legal Notices displayed
|
||||
by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or requiring
|
||||
that modified versions of such material be marked in reasonable ways as different
|
||||
from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or authors
|
||||
of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some trade names,
|
||||
trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that material by
|
||||
anyone who conveys the material (or modified versions of it) with contractual
|
||||
assumptions of liability to the recipient, for any liability that these contractual
|
||||
assumptions directly impose on those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further restrictions"
|
||||
within the meaning of section 10. If the Program as you received it, or any
|
||||
part of it, contains a notice stating that it is governed by this License
|
||||
along with a term that is a further restriction, you may remove that term.
|
||||
If a license document contains a further restriction but permits relicensing
|
||||
or conveying under this License, you may add to a covered work material governed
|
||||
by the terms of that license document, provided that the further restriction
|
||||
does not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you must place,
|
||||
in the relevant source files, a statement of the additional terms that apply
|
||||
to those files, or a notice indicating where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the form
|
||||
of a separately written license, or stated as exceptions; the above requirements
|
||||
apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly provided
|
||||
under this License. Any attempt otherwise to propagate or modify it is void,
|
||||
and will automatically terminate your rights under this License (including
|
||||
any patent licenses granted under the third paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your license from
|
||||
a particular copyright holder is reinstated (a) provisionally, unless and
|
||||
until the copyright holder explicitly and finally terminates your license,
|
||||
and (b) permanently, if the copyright holder fails to notify you of the violation
|
||||
by some reasonable means prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is reinstated permanently
|
||||
if the copyright holder notifies you of the violation by some reasonable means,
|
||||
this is the first time you have received notice of violation of this License
|
||||
(for any work) from that copyright holder, and you cure the violation prior
|
||||
to 30 days after your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the licenses
|
||||
of parties who have received copies or rights from you under this License.
|
||||
If your rights have been terminated and not permanently reinstated, you do
|
||||
not qualify to receive new licenses for the same material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or run a copy
|
||||
of the Program. Ancillary propagation of a covered work occurring solely as
|
||||
a consequence of using peer-to-peer transmission to receive a copy likewise
|
||||
does not require acceptance. However, nothing other than this License grants
|
||||
you permission to propagate or modify any covered work. These actions infringe
|
||||
copyright if you do not accept this License. Therefore, by modifying or propagating
|
||||
a covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically receives
|
||||
a license from the original licensors, to run, modify and propagate that work,
|
||||
subject to this License. You are not responsible for enforcing compliance
|
||||
by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an organization,
|
||||
or substantially all assets of one, or subdividing an organization, or merging
|
||||
organizations. If propagation of a covered work results from an entity transaction,
|
||||
each party to that transaction who receives a copy of the work also receives
|
||||
whatever licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the Corresponding
|
||||
Source of the work from the predecessor in interest, if the predecessor has
|
||||
it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the rights
|
||||
granted or affirmed under this License. For example, you may not impose a
|
||||
license fee, royalty, or other charge for exercise of rights granted under
|
||||
this License, and you may not initiate litigation (including a cross-claim
|
||||
or counterclaim in a lawsuit) alleging that any patent claim is infringed
|
||||
by making, using, selling, offering for sale, or importing the Program or
|
||||
any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this License
|
||||
of the Program or a work on which the Program is based. The work thus licensed
|
||||
is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims owned or controlled
|
||||
by the contributor, whether already acquired or hereafter acquired, that would
|
||||
be infringed by some manner, permitted by this License, of making, using,
|
||||
or selling its contributor version, but do not include claims that would be
|
||||
infringed only as a consequence of further modification of the contributor
|
||||
version. For purposes of this definition, "control" includes the right to
|
||||
grant patent sublicenses in a manner consistent with the requirements of this
|
||||
License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free patent
|
||||
license under the contributor's essential patent claims, to make, use, sell,
|
||||
offer for sale, import and otherwise run, modify and propagate the contents
|
||||
of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express agreement
|
||||
or commitment, however denominated, not to enforce a patent (such as an express
|
||||
permission to practice a patent or covenant not to sue for patent infringement).
|
||||
To "grant" such a patent license to a party means to make such an agreement
|
||||
or commitment not to enforce a patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, and the
|
||||
Corresponding Source of the work is not available for anyone to copy, free
|
||||
of charge and under the terms of this License, through a publicly available
|
||||
network server or other readily accessible means, then you must either (1)
|
||||
cause the Corresponding Source to be so available, or (2) arrange to deprive
|
||||
yourself of the benefit of the patent license for this particular work, or
|
||||
(3) arrange, in a manner consistent with the requirements of this License,
|
||||
to extend the patent license to downstream recipients. "Knowingly relying"
|
||||
means you have actual knowledge that, but for the patent license, your conveying
|
||||
the covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that country
|
||||
that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or arrangement,
|
||||
you convey, or propagate by procuring conveyance of, a covered work, and grant
|
||||
a patent license to some of the parties receiving the covered work authorizing
|
||||
them to use, propagate, modify or convey a specific copy of the covered work,
|
||||
then the patent license you grant is automatically extended to all recipients
|
||||
of the covered work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within the scope
|
||||
of its coverage, prohibits the exercise of, or is conditioned on the non-exercise
|
||||
of one or more of the rights that are specifically granted under this License.
|
||||
You may not convey a covered work if you are a party to an arrangement with
|
||||
a third party that is in the business of distributing software, under which
|
||||
you make payment to the third party based on the extent of your activity of
|
||||
conveying the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory patent
|
||||
license (a) in connection with copies of the covered work conveyed by you
|
||||
(or copies made from those copies), or (b) primarily for and in connection
|
||||
with specific products or compilations that contain the covered work, unless
|
||||
you entered into that arrangement, or that patent license was granted, prior
|
||||
to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting any implied
|
||||
license or other defenses to infringement that may otherwise be available
|
||||
to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or otherwise)
|
||||
that contradict the conditions of this License, they do not excuse you from
|
||||
the conditions of this License. If you cannot convey a covered work so as
|
||||
to satisfy simultaneously your obligations under this License and any other
|
||||
pertinent obligations, then as a consequence you may not convey it at all.
|
||||
For example, if you agree to terms that obligate you to collect a royalty
|
||||
for further conveying from those to whom you convey the Program, the only
|
||||
way you could satisfy both those terms and this License would be to refrain
|
||||
entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have permission to
|
||||
link or combine any covered work with a work licensed under version 3 of the
|
||||
GNU Affero General Public License into a single combined work, and to convey
|
||||
the resulting work. The terms of this License will continue to apply to the
|
||||
part which is the covered work, but the special requirements of the GNU Affero
|
||||
General Public License, section 13, concerning interaction through a network
|
||||
will apply to the combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of the
|
||||
GNU General Public License from time to time. Such new versions will be similar
|
||||
in spirit to the present version, but may differ in detail to address new
|
||||
problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program specifies
|
||||
that a certain numbered version of the GNU General Public License "or any
|
||||
later version" applies to it, you have the option of following the terms and
|
||||
conditions either of that numbered version or of any later version published
|
||||
by the Free Software Foundation. If the Program does not specify a version
|
||||
number of the GNU General Public License, you may choose any version ever
|
||||
published by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future versions of
|
||||
the GNU General Public License can be used, that proxy's public statement
|
||||
of acceptance of a version permanently authorizes you to choose that version
|
||||
for the Program.
|
||||
|
||||
Later license versions may give you additional or different permissions. However,
|
||||
no additional obligations are imposed on any author or copyright holder as
|
||||
a result of your choosing to follow a later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
|
||||
LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM
|
||||
PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
|
||||
CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
|
||||
ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM
|
||||
AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
|
||||
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO
|
||||
USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
|
||||
INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
|
||||
PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
|
||||
PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided above cannot
|
||||
be given local legal effect according to their terms, reviewing courts shall
|
||||
apply local law that most closely approximates an absolute waiver of all civil
|
||||
liability in connection with the Program, unless a warranty or assumption
|
||||
of liability accompanies a copy of the Program in return for a fee. END OF
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest possible
|
||||
use to the public, the best way to achieve this is to make it free software
|
||||
which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to attach
|
||||
them to the start of each source file to most effectively state the exclusion
|
||||
of warranty; and each file should have at least the "copyright" line and a
|
||||
pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short notice like
|
||||
this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
|
||||
This is free software, and you are welcome to redistribute it under certain
|
||||
conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands might
|
||||
be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary. For
|
||||
more information on this, and how to apply and follow the GNU GPL, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General Public
|
||||
License instead of this License. But first, please read <https://www.gnu.org/
|
||||
licenses /why-not-lgpl.html>.
|
122
README.md
Normal file
@ -0,0 +1,122 @@
|
||||
# EndOfYear
|
||||
|
||||
EndOfYear 点燃个人博客的年度辉煌!
|
||||
|
||||
![EndOfYear](static/endofyear.jpg)
|
||||
|
||||
## 流程
|
||||
|
||||
EndOfYear 通过 RSS 获取博客文章数据,对文章数据进行统计、分析和整理,最终输出为 HTML,客观地反映了博客一年的写作情况。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor User
|
||||
participant Flask
|
||||
participant Config
|
||||
participant Generator
|
||||
participant Scraper
|
||||
participant Analyzer
|
||||
|
||||
User ->> Flask: Access service
|
||||
Flask ->> Config: Check cache
|
||||
activate Config
|
||||
alt Cache exists
|
||||
Config -->> Flask: Return cached data
|
||||
else Cache does not exist
|
||||
Config ->> Generator: Run data generator
|
||||
activate Generator
|
||||
Generator ->> Scraper: Run data scraping
|
||||
activate Scraper
|
||||
Scraper -->> Generator: Return scraped data
|
||||
deactivate Scraper
|
||||
Generator ->> Analyzer: Run data analysis
|
||||
activate Analyzer
|
||||
Analyzer -->> Generator: Return analyzed data
|
||||
deactivate Analyzer
|
||||
Generator -->> Config: Return organized data
|
||||
deactivate Generator
|
||||
Config -->> Flask: Return data
|
||||
end
|
||||
Flask -->> User: Return HTML page
|
||||
deactivate Config
|
||||
```
|
||||
|
||||
1. 用户访问 Flask 服务。
|
||||
2. Flask 检查缓存是否存在。
|
||||
- 如果缓存存在,Flask直接返回缓存数据。
|
||||
- 如果缓存不存在,继续下一步。
|
||||
3. Config 模块运行数据生成器(Generator)。
|
||||
4. Generator 模块运行数据抓取器(Scraper)来获取RSS数据。
|
||||
5. Scraper 将抓取的数据返回给 Generator。
|
||||
6. Generator 运行数据分析器(Analyzer)对数据进行分析。
|
||||
7. Analyzer 将分析后的数据返回给 Generator。
|
||||
8. Generator 整理结构化数据后将其返回给 Flask,Config 模块。
|
||||
9. Flask 使用整理后的数据渲染 HTML 页面。
|
||||
10. Flask 返回渲染后的 HTML 页面给用户。
|
||||
|
||||
## 用法
|
||||
|
||||
### Github
|
||||
|
||||
1. Fork 项目到个人仓库
|
||||
2. 手动配置仓库的 Workflow permissions 设置为 **Read and write permissions**,否则无法写入 html 分支。
|
||||
1. 导航到 **Settings**(设置)选项卡。
|
||||
2. 在左侧导航栏中,点击 **Actions**(操作)。
|
||||
3. 在 **General**(常规)页面下滑,找到 **Workflow permissions**(工作流权限)。
|
||||
4. 在 **Workflow permissions** 中,选择 **Read and write permissions**(读写权限)。
|
||||
5. 最后点击 **Save**(保存)。
|
||||
3. 在仓库首页打开目录下的 `config.ini` 配置文件,点击右上角工具栏的 **🖋️(钢笔)** 图标,在线编辑文件。
|
||||
- `web`:配置为 `false`。
|
||||
- `rss`:配置为 RSS 地址。
|
||||
|
||||
```ini
|
||||
[default]
|
||||
web = false
|
||||
|
||||
[blog]
|
||||
rss = https://blog.7wate.com/rss.xml
|
||||
data =
|
||||
```
|
||||
|
||||
4. 点击右上角的 **Commit changes** 提交到 `main` 分支,会自动运行 Action。
|
||||
5. 等待 Action 运行成功,将会部署静态网站文件至 `html` 分支。
|
||||
|
||||
6. 开启仓库的 Pages 功能,默认为根目录。
|
||||
7. 访问个人网址,就可以看到啦~
|
||||
|
||||
### Docker
|
||||
|
||||
1. 拉取最新镜像
|
||||
|
||||
```shell
|
||||
docker pull sevewate/endofyear:latest
|
||||
```
|
||||
|
||||
2. 指定 `rss_url` 环境变量,运行 Docker。请将 `https://blog.7wate.com/rss.xml` 替换为自己的 RSS 地址。
|
||||
|
||||
```shell
|
||||
docker run -p 7777:7777 --env rss=https://blog.7wate.com/rss.xml sevewate/endofyear:latest
|
||||
```
|
||||
|
||||
3. 访问网址 `localhost:7777`
|
||||
|
||||
## 路线图
|
||||
|
||||
EndOfYear 目前处于初始阶段,如果您有兴趣,可以为其做出贡献。计划路线如下:
|
||||
|
||||
### V1
|
||||
|
||||
- [ ] 对博客系统的数据源进行全面、规模性的测试。
|
||||
- [ ] 进一步细化数据分析维度和数据颗粒度,精准描绘用户画像。
|
||||
- [ ] 渲染数据的规范,约束主题开发,提高主题的兼容性。
|
||||
|
||||
### V2
|
||||
|
||||
- [ ] 丰富和完善主题。
|
||||
- [ ] 实现轻量化的运行部署,一键运行。
|
||||
- [ ] 探索以插件的方式附加到博客系统的方法。
|
||||
|
||||
|
||||
## 协议
|
||||
|
||||
EndOfYear 采用 GPL 3.0 协议。
|
6
config.ini
Normal file
@ -0,0 +1,6 @@
|
||||
[default]
|
||||
web = true
|
||||
|
||||
[blog]
|
||||
rss =
|
||||
data =
|
767
data/stop_words.txt
Normal file
@ -0,0 +1,767 @@
|
||||
———
|
||||
》),
|
||||
)÷(1-
|
||||
”,
|
||||
)、
|
||||
=(
|
||||
:
|
||||
→
|
||||
℃
|
||||
&
|
||||
*
|
||||
一一
|
||||
~~~~
|
||||
’
|
||||
.
|
||||
『
|
||||
.一
|
||||
./
|
||||
--
|
||||
』
|
||||
=″
|
||||
【
|
||||
[*]
|
||||
}>
|
||||
[⑤]]
|
||||
[①D]
|
||||
c]
|
||||
ng昉
|
||||
*
|
||||
//
|
||||
[
|
||||
]
|
||||
[②e]
|
||||
[②g]
|
||||
={
|
||||
}
|
||||
,也
|
||||
‘
|
||||
A
|
||||
[①⑥]
|
||||
[②B]
|
||||
[①a]
|
||||
[④a]
|
||||
[①③]
|
||||
[③h]
|
||||
③]
|
||||
1.
|
||||
--
|
||||
[②b]
|
||||
’‘
|
||||
×××
|
||||
[①⑧]
|
||||
0:2
|
||||
=[
|
||||
[⑤b]
|
||||
[②c]
|
||||
[④b]
|
||||
[②③]
|
||||
[③a]
|
||||
[④c]
|
||||
[①⑤]
|
||||
[①⑦]
|
||||
[①g]
|
||||
∈[
|
||||
[①⑨]
|
||||
[①④]
|
||||
[①c]
|
||||
[②f]
|
||||
[②⑧]
|
||||
[②①]
|
||||
[①C]
|
||||
[③c]
|
||||
[③g]
|
||||
[②⑤]
|
||||
[②②]
|
||||
一.
|
||||
[①h]
|
||||
.数
|
||||
[]
|
||||
[①B]
|
||||
数/
|
||||
[①i]
|
||||
[③e]
|
||||
[①①]
|
||||
[④d]
|
||||
[④e]
|
||||
[③b]
|
||||
[⑤a]
|
||||
[①A]
|
||||
[②⑧]
|
||||
[②⑦]
|
||||
[①d]
|
||||
[②j]
|
||||
〕〔
|
||||
][
|
||||
://
|
||||
′∈
|
||||
[②④
|
||||
[⑤e]
|
||||
12%
|
||||
b]
|
||||
...
|
||||
...................
|
||||
…………………………………………………③
|
||||
ZXFITL
|
||||
[③F]
|
||||
」
|
||||
[①o]
|
||||
]∧′=[
|
||||
∪φ∈
|
||||
′|
|
||||
{-
|
||||
②c
|
||||
}
|
||||
[③①]
|
||||
R.L.
|
||||
[①E]
|
||||
Ψ
|
||||
-[*]-
|
||||
↑
|
||||
.日
|
||||
[②d]
|
||||
[②
|
||||
[②⑦]
|
||||
[②②]
|
||||
[③e]
|
||||
[①i]
|
||||
[①B]
|
||||
[①h]
|
||||
[①d]
|
||||
[①g]
|
||||
[①②]
|
||||
[②a]
|
||||
f]
|
||||
[⑩]
|
||||
a]
|
||||
[①e]
|
||||
[②h]
|
||||
[②⑥]
|
||||
[③d]
|
||||
[②⑩]
|
||||
e]
|
||||
〉
|
||||
】
|
||||
元/吨
|
||||
[②⑩]
|
||||
2.3%
|
||||
5:0
|
||||
[①]
|
||||
::
|
||||
[②]
|
||||
[③]
|
||||
[④]
|
||||
[⑤]
|
||||
[⑥]
|
||||
[⑦]
|
||||
[⑧]
|
||||
[⑨]
|
||||
……
|
||||
——
|
||||
?
|
||||
、
|
||||
。
|
||||
“
|
||||
”
|
||||
《
|
||||
》
|
||||
!
|
||||
,
|
||||
:
|
||||
;
|
||||
?
|
||||
.
|
||||
,
|
||||
.
|
||||
'
|
||||
?
|
||||
·
|
||||
———
|
||||
──
|
||||
?
|
||||
—
|
||||
<
|
||||
>
|
||||
(
|
||||
)
|
||||
〔
|
||||
〕
|
||||
[
|
||||
]
|
||||
(
|
||||
)
|
||||
-
|
||||
+
|
||||
~
|
||||
×
|
||||
/
|
||||
/
|
||||
①
|
||||
②
|
||||
③
|
||||
④
|
||||
⑤
|
||||
⑥
|
||||
⑦
|
||||
⑧
|
||||
⑨
|
||||
⑩
|
||||
Ⅲ
|
||||
В
|
||||
"
|
||||
;
|
||||
#
|
||||
@
|
||||
γ
|
||||
μ
|
||||
φ
|
||||
φ.
|
||||
×
|
||||
Δ
|
||||
■
|
||||
▲
|
||||
sub
|
||||
exp
|
||||
sup
|
||||
sub
|
||||
Lex
|
||||
#
|
||||
%
|
||||
&
|
||||
'
|
||||
+
|
||||
+ξ
|
||||
++
|
||||
-
|
||||
-β
|
||||
<
|
||||
<±
|
||||
<Δ
|
||||
<λ
|
||||
<φ
|
||||
<<
|
||||
=
|
||||
=
|
||||
=☆
|
||||
=-
|
||||
>
|
||||
>λ
|
||||
_
|
||||
~±
|
||||
~+
|
||||
[⑤f]
|
||||
[⑤d]
|
||||
[②i]
|
||||
≈
|
||||
[②G]
|
||||
[①f]
|
||||
LI
|
||||
㈧
|
||||
[-
|
||||
......
|
||||
〉
|
||||
[③⑩]
|
||||
第二
|
||||
一番
|
||||
一直
|
||||
一个
|
||||
一些
|
||||
许多
|
||||
种
|
||||
有的是
|
||||
也就是说
|
||||
末##末
|
||||
啊
|
||||
阿
|
||||
哎
|
||||
哎呀
|
||||
哎哟
|
||||
唉
|
||||
俺
|
||||
俺们
|
||||
按
|
||||
按照
|
||||
吧
|
||||
吧哒
|
||||
把
|
||||
罢了
|
||||
被
|
||||
本
|
||||
本着
|
||||
比
|
||||
比方
|
||||
比如
|
||||
鄙人
|
||||
彼
|
||||
彼此
|
||||
边
|
||||
别
|
||||
别的
|
||||
别说
|
||||
并
|
||||
并且
|
||||
不比
|
||||
不成
|
||||
不单
|
||||
不但
|
||||
不独
|
||||
不管
|
||||
不光
|
||||
不过
|
||||
不仅
|
||||
不拘
|
||||
不论
|
||||
不怕
|
||||
不然
|
||||
不如
|
||||
不特
|
||||
不惟
|
||||
不问
|
||||
不只
|
||||
朝
|
||||
朝着
|
||||
趁
|
||||
趁着
|
||||
乘
|
||||
冲
|
||||
除
|
||||
除此之外
|
||||
除非
|
||||
除了
|
||||
此
|
||||
此间
|
||||
此外
|
||||
从
|
||||
从而
|
||||
打
|
||||
待
|
||||
但
|
||||
但是
|
||||
当
|
||||
当着
|
||||
到
|
||||
得
|
||||
的
|
||||
的话
|
||||
等
|
||||
等等
|
||||
地
|
||||
第
|
||||
叮咚
|
||||
对
|
||||
对于
|
||||
多
|
||||
多少
|
||||
而
|
||||
而况
|
||||
而且
|
||||
而是
|
||||
而外
|
||||
而言
|
||||
而已
|
||||
尔后
|
||||
反过来
|
||||
反过来说
|
||||
反之
|
||||
非但
|
||||
非徒
|
||||
否则
|
||||
嘎
|
||||
嘎登
|
||||
该
|
||||
赶
|
||||
个
|
||||
各
|
||||
各个
|
||||
各位
|
||||
各种
|
||||
各自
|
||||
给
|
||||
根据
|
||||
跟
|
||||
故
|
||||
故此
|
||||
固然
|
||||
关于
|
||||
管
|
||||
归
|
||||
果然
|
||||
果真
|
||||
过
|
||||
哈
|
||||
哈哈
|
||||
呵
|
||||
和
|
||||
何
|
||||
何处
|
||||
何况
|
||||
何时
|
||||
嘿
|
||||
哼
|
||||
哼唷
|
||||
呼哧
|
||||
乎
|
||||
哗
|
||||
还是
|
||||
还有
|
||||
换句话说
|
||||
换言之
|
||||
或
|
||||
或是
|
||||
或者
|
||||
极了
|
||||
及
|
||||
及其
|
||||
及至
|
||||
即
|
||||
即便
|
||||
即或
|
||||
即令
|
||||
即若
|
||||
即使
|
||||
几
|
||||
几时
|
||||
己
|
||||
既
|
||||
既然
|
||||
既是
|
||||
继而
|
||||
加之
|
||||
假如
|
||||
假若
|
||||
假使
|
||||
鉴于
|
||||
将
|
||||
较
|
||||
较之
|
||||
叫
|
||||
接着
|
||||
结果
|
||||
借
|
||||
紧接着
|
||||
进而
|
||||
尽
|
||||
尽管
|
||||
经
|
||||
经过
|
||||
就
|
||||
就是
|
||||
就是说
|
||||
据
|
||||
具体地说
|
||||
具体说来
|
||||
开始
|
||||
开外
|
||||
靠
|
||||
咳
|
||||
可
|
||||
可见
|
||||
可是
|
||||
可以
|
||||
况且
|
||||
啦
|
||||
来
|
||||
来着
|
||||
离
|
||||
例如
|
||||
哩
|
||||
连
|
||||
连同
|
||||
两者
|
||||
了
|
||||
临
|
||||
另
|
||||
另外
|
||||
另一方面
|
||||
论
|
||||
嘛
|
||||
吗
|
||||
慢说
|
||||
漫说
|
||||
冒
|
||||
么
|
||||
每
|
||||
每当
|
||||
们
|
||||
莫若
|
||||
某
|
||||
某个
|
||||
某些
|
||||
拿
|
||||
哪
|
||||
哪边
|
||||
哪儿
|
||||
哪个
|
||||
哪里
|
||||
哪年
|
||||
哪怕
|
||||
哪天
|
||||
哪些
|
||||
哪样
|
||||
那
|
||||
那边
|
||||
那儿
|
||||
那个
|
||||
那会儿
|
||||
那里
|
||||
那么
|
||||
那么些
|
||||
那么样
|
||||
那时
|
||||
那些
|
||||
那样
|
||||
乃
|
||||
乃至
|
||||
呢
|
||||
能
|
||||
你
|
||||
你们
|
||||
您
|
||||
宁
|
||||
宁可
|
||||
宁肯
|
||||
宁愿
|
||||
哦
|
||||
呕
|
||||
啪达
|
||||
旁人
|
||||
呸
|
||||
凭
|
||||
凭借
|
||||
其
|
||||
其次
|
||||
其二
|
||||
其他
|
||||
其它
|
||||
其一
|
||||
其余
|
||||
其中
|
||||
起
|
||||
起见
|
||||
起见
|
||||
岂但
|
||||
恰恰相反
|
||||
前后
|
||||
前者
|
||||
且
|
||||
然而
|
||||
然后
|
||||
然则
|
||||
让
|
||||
人家
|
||||
任
|
||||
任何
|
||||
任凭
|
||||
如
|
||||
如此
|
||||
如果
|
||||
如何
|
||||
如其
|
||||
如若
|
||||
如上所述
|
||||
若
|
||||
若非
|
||||
若是
|
||||
啥
|
||||
上下
|
||||
尚且
|
||||
设若
|
||||
设使
|
||||
甚而
|
||||
甚么
|
||||
甚至
|
||||
省得
|
||||
时候
|
||||
什么
|
||||
什么样
|
||||
使得
|
||||
是
|
||||
是的
|
||||
首先
|
||||
谁
|
||||
谁知
|
||||
顺
|
||||
顺着
|
||||
似的
|
||||
虽
|
||||
虽然
|
||||
虽说
|
||||
虽则
|
||||
随
|
||||
随着
|
||||
所
|
||||
所以
|
||||
他
|
||||
他们
|
||||
他人
|
||||
它
|
||||
它们
|
||||
她
|
||||
她们
|
||||
倘
|
||||
倘或
|
||||
倘然
|
||||
倘若
|
||||
倘使
|
||||
腾
|
||||
替
|
||||
通过
|
||||
同
|
||||
同时
|
||||
哇
|
||||
万一
|
||||
往
|
||||
望
|
||||
为
|
||||
为何
|
||||
为了
|
||||
为什么
|
||||
为着
|
||||
喂
|
||||
嗡嗡
|
||||
我
|
||||
我们
|
||||
呜
|
||||
呜呼
|
||||
乌乎
|
||||
无论
|
||||
无宁
|
||||
毋宁
|
||||
嘻
|
||||
吓
|
||||
相对而言
|
||||
像
|
||||
向
|
||||
向着
|
||||
嘘
|
||||
呀
|
||||
焉
|
||||
沿
|
||||
沿着
|
||||
要
|
||||
要不
|
||||
要不然
|
||||
要不是
|
||||
要么
|
||||
要是
|
||||
也
|
||||
也罢
|
||||
也好
|
||||
一
|
||||
一般
|
||||
一旦
|
||||
一方面
|
||||
一来
|
||||
一切
|
||||
一样
|
||||
一则
|
||||
依
|
||||
依照
|
||||
矣
|
||||
以
|
||||
以便
|
||||
以及
|
||||
以免
|
||||
以至
|
||||
以至于
|
||||
以致
|
||||
抑或
|
||||
因
|
||||
因此
|
||||
因而
|
||||
因为
|
||||
哟
|
||||
用
|
||||
由
|
||||
由此可见
|
||||
由于
|
||||
有
|
||||
有的
|
||||
有关
|
||||
有些
|
||||
又
|
||||
于
|
||||
于是
|
||||
于是乎
|
||||
与
|
||||
与此同时
|
||||
与否
|
||||
与其
|
||||
越是
|
||||
云云
|
||||
哉
|
||||
再说
|
||||
再者
|
||||
在
|
||||
在下
|
||||
咱
|
||||
咱们
|
||||
则
|
||||
怎
|
||||
怎么
|
||||
怎么办
|
||||
怎么样
|
||||
怎样
|
||||
咋
|
||||
照
|
||||
照着
|
||||
者
|
||||
这
|
||||
这边
|
||||
这儿
|
||||
这个
|
||||
这会儿
|
||||
这就是说
|
||||
这里
|
||||
这么
|
||||
这么点儿
|
||||
这么些
|
||||
这么样
|
||||
这时
|
||||
这些
|
||||
这样
|
||||
正如
|
||||
吱
|
||||
之
|
||||
之类
|
||||
之所以
|
||||
之一
|
||||
只是
|
||||
只限
|
||||
只要
|
||||
只有
|
||||
至
|
||||
至于
|
||||
诸位
|
||||
着
|
||||
着呢
|
||||
自
|
||||
自从
|
||||
自个儿
|
||||
自各儿
|
||||
自己
|
||||
自家
|
||||
自身
|
||||
综上所述
|
||||
总的来看
|
||||
总的来说
|
||||
总的说来
|
||||
总而言之
|
||||
总之
|
||||
纵
|
||||
纵令
|
||||
纵然
|
||||
纵使
|
||||
遵照
|
||||
作为
|
||||
兮
|
||||
呃
|
||||
呗
|
||||
咚
|
||||
咦
|
||||
喏
|
||||
啐
|
||||
喔唷
|
||||
嗬
|
||||
嗯
|
||||
嗳
|
247
data/tech_terms.txt
Normal file
@ -0,0 +1,247 @@
|
||||
Python
|
||||
Java
|
||||
JavaScript
|
||||
C
|
||||
C++
|
||||
C#
|
||||
PHP
|
||||
Ruby
|
||||
Go
|
||||
Swift
|
||||
Kotlin
|
||||
Rust
|
||||
TypeScript
|
||||
HTML
|
||||
CSS
|
||||
SQL
|
||||
NoSQL
|
||||
MongoDB
|
||||
PostgreSQL
|
||||
SQLite
|
||||
Redis
|
||||
Elasticsearch
|
||||
Docker
|
||||
Kubernetes
|
||||
Microservices
|
||||
Angular
|
||||
React
|
||||
Vue
|
||||
Node.js
|
||||
Express
|
||||
Django
|
||||
Flask
|
||||
Spring Boot
|
||||
.NET
|
||||
TensorFlow
|
||||
PyTorch
|
||||
Pandas
|
||||
NumPy
|
||||
Scikit-learn
|
||||
Matplotlib
|
||||
Seaborn
|
||||
Bokeh
|
||||
Jupyter
|
||||
Data Science
|
||||
Machine Learning
|
||||
Deep Learning
|
||||
Artificial Intelligence
|
||||
Natural Language Processing
|
||||
Computer Vision
|
||||
Reinforcement Learning
|
||||
Neural Network
|
||||
Convolutional Neural Network
|
||||
Recurrent Neural Network
|
||||
GANs
|
||||
Blockchain
|
||||
Bitcoin
|
||||
Ethereum
|
||||
Smart Contract
|
||||
Cloud Computing
|
||||
AWS
|
||||
Azure
|
||||
GCP
|
||||
Serverless
|
||||
DevOps
|
||||
Git
|
||||
GitHub
|
||||
Bitbucket
|
||||
Agile
|
||||
Scrum
|
||||
Kanban
|
||||
CI/CD
|
||||
JUnit
|
||||
Selenium
|
||||
REST
|
||||
GraphQL
|
||||
JSON
|
||||
XML
|
||||
YAML
|
||||
API
|
||||
UI
|
||||
UX
|
||||
VR
|
||||
AR
|
||||
IoT
|
||||
Raspberry Pi
|
||||
Arduino
|
||||
Linux
|
||||
Ubuntu
|
||||
CentOS
|
||||
Fedora
|
||||
Debian
|
||||
Windows
|
||||
macOS
|
||||
Android
|
||||
iOS
|
||||
Firmware
|
||||
Cybersecurity
|
||||
Encryption
|
||||
Firewall
|
||||
VPN
|
||||
DDoS
|
||||
Malware
|
||||
Ransomware
|
||||
Phishing
|
||||
Two-factor Authentication
|
||||
Biometrics
|
||||
Virtual Machine
|
||||
Container
|
||||
Orchestration
|
||||
Big Data
|
||||
Hadoop
|
||||
Spark
|
||||
Kafka
|
||||
Flink
|
||||
ETL
|
||||
Data Warehouse
|
||||
Business Intelligence
|
||||
Data Mining
|
||||
Web Scraping
|
||||
Web Development
|
||||
Frontend
|
||||
Backend
|
||||
Full Stack
|
||||
Software Engineering
|
||||
Database
|
||||
Schema
|
||||
Query
|
||||
Indexing
|
||||
Normalization
|
||||
Denormalization
|
||||
Object-Oriented Programming
|
||||
Functional Programming
|
||||
Procedural Programming
|
||||
Scripting
|
||||
Compiler
|
||||
Interpreter
|
||||
IDE
|
||||
Debugging
|
||||
Refactoring
|
||||
Version Control
|
||||
Concurrency
|
||||
Multithreading
|
||||
Synchronization
|
||||
Deadlock
|
||||
Memory Management
|
||||
Garbage Collection
|
||||
Exception Handling
|
||||
PaaS
|
||||
IaaS
|
||||
SaaS
|
||||
Hashing
|
||||
Cryptography
|
||||
Algorithm
|
||||
Data Structure
|
||||
Stack
|
||||
Queue
|
||||
Tree
|
||||
Graph
|
||||
Linked List
|
||||
Array
|
||||
Matrix
|
||||
Sorting
|
||||
Searching
|
||||
BFS
|
||||
DFS
|
||||
Dynamic Programming
|
||||
Greedy Algorithm
|
||||
Divide and Conquer
|
||||
Backtracking
|
||||
Recursion
|
||||
MapReduce
|
||||
Object-Relational Mapping
|
||||
Virtual Reality
|
||||
Augmented Reality
|
||||
Mixed Reality
|
||||
Cyber-Physical System
|
||||
Edge Computing
|
||||
Fog Computing
|
||||
Fifth Generation
|
||||
Quantum Computing
|
||||
Server
|
||||
Client
|
||||
Peer-to-Peer
|
||||
Blockchain
|
||||
Tangle
|
||||
Distributed Ledger
|
||||
Smart Grid
|
||||
Autonomous Vehicle
|
||||
Drone
|
||||
3D Printing
|
||||
Cryptocurrency
|
||||
Token
|
||||
Initial Coin Offering
|
||||
Public Key
|
||||
Private Key
|
||||
Digital Signature
|
||||
Zero-Knowledge Proof
|
||||
Merkle Tree
|
||||
Side Chain
|
||||
Hard Fork
|
||||
Soft Fork
|
||||
Data Lake
|
||||
Data Mart
|
||||
OLAP
|
||||
OLTP
|
||||
CAP Theorem
|
||||
ACID
|
||||
BASE
|
||||
Event-Driven
|
||||
Microfrontend
|
||||
Service Mesh
|
||||
Progressive Web App
|
||||
WebAssembly
|
||||
GraphQL
|
||||
gRPC
|
||||
Thrift
|
||||
Protocol Buffers
|
||||
WebRTC
|
||||
Socket.IO
|
||||
OAuth
|
||||
OpenID
|
||||
LDAP
|
||||
RBAC
|
||||
XSS
|
||||
CSRF
|
||||
SQL Injection
|
||||
Buffer Overflow
|
||||
Shellshock
|
||||
Heartbleed
|
||||
Spectre
|
||||
Meltdown
|
||||
Row Hammer
|
||||
BlueKeep
|
||||
Zero Day
|
||||
White Hat
|
||||
Black Hat
|
||||
Gray Hat
|
||||
Rainbow Table
|
||||
Brute Force
|
||||
Dictionary Attack
|
||||
Man in the Middle
|
||||
Replay Attack
|
||||
Denial of Service
|
||||
SYN Flood
|
||||
Smurf Attack
|
||||
IP Spoofing
|
||||
Phreaking
|
38
main.py
Normal file
@ -0,0 +1,38 @@
|
||||
from flask import Flask, render_template, redirect, url_for
|
||||
from loguru import logger
|
||||
|
||||
from src.config import Config
|
||||
from src.generator import build_data
|
||||
|
||||
app = Flask(__name__)
|
||||
logger.add("endofyear.log")
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def home():
|
||||
# 默认主题 painting
|
||||
return redirect(url_for('painting'))
|
||||
|
||||
|
||||
@app.route('/painting')
|
||||
def painting():
|
||||
if Config("config.ini").web_status:
|
||||
# web 服务
|
||||
# 如果数据存在,直接返回
|
||||
if blog_data := Config("config.ini").blog_data:
|
||||
return render_template('painting.html', data=blog_data, web_status=1)
|
||||
|
||||
# 如果数据不存在,需要生成,并写入配置
|
||||
return render_template('painting.html', data=build_data(), web_status=1)
|
||||
else:
|
||||
# Github 静态
|
||||
# 数据需要生成,并写入静态文件
|
||||
html_data = render_template('painting.html', data=build_data(), web_status=0)
|
||||
with open("static/index.html", "w") as f:
|
||||
f.write(html_data)
|
||||
|
||||
return 'OK'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=7777, debug=True)
|
28
requirements.txt
Normal file
@ -0,0 +1,28 @@
|
||||
beautifulsoup4==4.12.2
|
||||
blinker==1.6.3
|
||||
certifi==2023.7.22
|
||||
charset-normalizer==3.3.1
|
||||
click==8.1.7
|
||||
feedparser==6.0.10
|
||||
Flask==3.0.0
|
||||
idna==3.4
|
||||
itsdangerous==2.1.2
|
||||
jieba==0.42.1
|
||||
Jinja2==3.1.2
|
||||
joblib==1.3.2
|
||||
loguru==0.7.2
|
||||
lunardate==0.2.0
|
||||
MarkupSafe==2.1.3
|
||||
nltk==3.8.1
|
||||
python-dateutil==2.8.2
|
||||
pytz==2023.3.post1
|
||||
regex==2023.10.3
|
||||
requests==2.31.0
|
||||
sgmllib3k==1.0.0
|
||||
six==1.16.0
|
||||
snownlp==0.12.3
|
||||
soupsieve==2.5
|
||||
textblob==0.17.1
|
||||
tqdm==4.66.1
|
||||
urllib3==2.0.7
|
||||
Werkzeug==3.0.1
|
0
src/__init__.py
Normal file
122
src/analyzer.py
Normal file
@ -0,0 +1,122 @@
|
||||
from typing import Any
|
||||
|
||||
import jieba.analyse
|
||||
import pytz
|
||||
from dateutil.parser import parse
|
||||
from loguru import logger
|
||||
from lunardate import LunarDate
|
||||
from snownlp import SnowNLP
|
||||
|
||||
|
||||
# 计算文本内容情感分数
|
||||
def analyze_sentiment(text):
|
||||
"""
|
||||
博客文章情感分计算(有点问题,酌情使用)
|
||||
:param text:文章文本
|
||||
:return:分数
|
||||
"""
|
||||
s = SnowNLP(text)
|
||||
return round(s.sentiments * 100)
|
||||
|
||||
|
||||
def classify_and_extract_keywords(text: str, topK: int, stopwords: str,
|
||||
tech_terms_file: str) -> tuple[None, list[Any]] | tuple[int, Any]:
|
||||
"""
|
||||
博客文章关键字提取
|
||||
:param text:文章文本
|
||||
:param topK:关键字数量,建议20个
|
||||
:param stopwords:停词文本,去掉无意义词组
|
||||
:param tech_terms_file:专业词语,区分文章类目
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
jieba.analyse.set_stop_words(stopwords)
|
||||
keywords = jieba.analyse.extract_tags(text, topK=topK)
|
||||
except ValueError as e:
|
||||
logger.error(f"关键词提取出错:{e}")
|
||||
return None, []
|
||||
except ModuleNotFoundError as e:
|
||||
logger.error(f"关键词提取出错:{e}")
|
||||
return None, []
|
||||
|
||||
with open(tech_terms_file, 'r', encoding='utf-8') as f:
|
||||
tech_terms_set = {line.strip().lower() for line in f}
|
||||
|
||||
for keyword in keywords:
|
||||
if keyword.lower() in tech_terms_set:
|
||||
return 1, keywords
|
||||
|
||||
return 2, keywords
|
||||
|
||||
|
||||
def calculate_weight(time_str: str):
|
||||
"""
|
||||
博客文章特殊日期权重分数计算。
|
||||
- 传统节假日 +10
|
||||
- 节假日 +7
|
||||
- 凌晨 +5
|
||||
- 早上 +4
|
||||
- 下午 +3
|
||||
- 晚上 +2
|
||||
:param time_str: 时间字符串
|
||||
:return:总分数,特殊日期
|
||||
"""
|
||||
dt = parse(time_str)
|
||||
dt = dt.astimezone(pytz.timezone('Asia/Shanghai'))
|
||||
|
||||
weight = 0
|
||||
date_str = ""
|
||||
|
||||
# 农历节日权重计算
|
||||
LUNAR_HOLIDAYS = {
|
||||
(1, 1): '春节',
|
||||
(1, 15): '元宵节',
|
||||
(2, 2): '龙抬头',
|
||||
(5, 5): '端午节',
|
||||
(7, 7): '七夕节',
|
||||
(7, 15): '中元节',
|
||||
(8, 15): '中秋节',
|
||||
(9, 9): '重阳节',
|
||||
(12, 8): '腊八节',
|
||||
(12, 23): '小年',
|
||||
(12, 30): '除夕'
|
||||
}
|
||||
|
||||
lunar_date = LunarDate.fromSolarDate(dt.year, dt.month, dt.day)
|
||||
if (lunar_date.month, lunar_date.day) in LUNAR_HOLIDAYS:
|
||||
weight += 10
|
||||
date_str = LUNAR_HOLIDAYS[(lunar_date.month, lunar_date.day)]
|
||||
|
||||
# 公历节日权重计算
|
||||
SOLAR_HOLIDAYS = {
|
||||
(1, 1): '元旦',
|
||||
(2, 14): '情人节',
|
||||
(3, 8): '国际妇女节',
|
||||
(4, 4): '清明节',
|
||||
(5, 1): '国际劳动节',
|
||||
(10, 1): '国庆节',
|
||||
(12, 13): '南京大屠杀纪念日',
|
||||
(9, 18): '九一八事变纪念日',
|
||||
(12, 7): '南京保卫战胜利纪念日',
|
||||
(8, 15): '抗日战争胜利纪念日'
|
||||
}
|
||||
|
||||
if (dt.month, dt.day) in SOLAR_HOLIDAYS:
|
||||
weight += 7
|
||||
date_str = SOLAR_HOLIDAYS[(dt.month, dt.day)]
|
||||
|
||||
if 22 <= dt.hour or dt.hour < 7:
|
||||
weight += 5
|
||||
elif 7 <= dt.hour < 12:
|
||||
weight += 4
|
||||
elif 12 <= dt.hour < 18:
|
||||
weight += 3
|
||||
elif 18 <= dt.hour < 22:
|
||||
weight += 2
|
||||
else:
|
||||
weight += 0
|
||||
|
||||
if not date_str:
|
||||
date_str = f"{dt.month}月{dt.day}日"
|
||||
|
||||
return weight, date_str
|
114
src/config.py
Normal file
@ -0,0 +1,114 @@
|
||||
import configparser
|
||||
import json
|
||||
import os
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from src.tools import check_website_status
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self, path):
|
||||
"""
|
||||
初始化配置文件 config.ini
|
||||
:param path:文件路径
|
||||
"""
|
||||
if not os.path.isfile(path):
|
||||
logger.error(f"配置文件 {path} 不存在或不是一个文件")
|
||||
raise FileNotFoundError
|
||||
|
||||
self.path = path
|
||||
self.config = configparser.ConfigParser()
|
||||
|
||||
try:
|
||||
self.config.read(self.path)
|
||||
except configparser.ParsingError as e:
|
||||
logger.error(f"解析配置文件 {self.path} 错误: {str(e)}")
|
||||
raise
|
||||
|
||||
except PermissionError as e:
|
||||
logger.error(f"没有权限读取配置文件 {self.path}: {str(e)}")
|
||||
raise
|
||||
|
||||
logger.info(f"配置文件 {self.path} 加载成功")
|
||||
|
||||
@property
|
||||
def rss_url(self):
|
||||
try:
|
||||
url = self.config.get('blog', 'rss', fallback=None)
|
||||
except configparser.NoSectionError:
|
||||
logger.error('未找到 blog 配置项,请检查拼写')
|
||||
return None
|
||||
|
||||
if not url:
|
||||
logger.debug('rss 文件配置值为空,尝试读取环境变量')
|
||||
url = os.environ.get('rss')
|
||||
if url is None:
|
||||
logger.error('rss 文件配置值为空,环境变量为空……')
|
||||
return None
|
||||
|
||||
# 如果网址不可访问,返回 None
|
||||
if not check_website_status(url):
|
||||
logger.error(f"rss URL {url} 不可访问")
|
||||
return None
|
||||
|
||||
return url
|
||||
|
||||
@property
|
||||
def rss_domain(self):
|
||||
rss_url = self.rss_url
|
||||
|
||||
if rss_url is None:
|
||||
return None
|
||||
|
||||
parsed = urlparse(rss_url)
|
||||
domain_parts = parsed.netloc.split('.')
|
||||
|
||||
if len(domain_parts) < 2:
|
||||
logger.error(f"提供的 URL {rss_url} 的域名格式错误")
|
||||
return None
|
||||
|
||||
return '.'.join(domain_parts[-2:])
|
||||
|
||||
@property
|
||||
def blog_data(self):
|
||||
try:
|
||||
data = self.config.get('blog', 'data', fallback=None)
|
||||
except configparser.NoSectionError:
|
||||
logger.error('未找到 section 配置项,请检查拼写')
|
||||
return None
|
||||
|
||||
if not data:
|
||||
logger.error('data 配置值为空')
|
||||
return None
|
||||
|
||||
return json.loads(data)
|
||||
|
||||
@blog_data.setter
|
||||
def blog_data(self, value):
|
||||
if not self.config.has_section('blog'):
|
||||
self.config.add_section('blog')
|
||||
|
||||
self.config.set('blog', 'data', json.dumps(value))
|
||||
|
||||
with open(self.path, 'w') as configfile:
|
||||
self.config.write(configfile)
|
||||
|
||||
@property
|
||||
def web_status(self):
|
||||
try:
|
||||
web_status = self.config.get('default', 'web', fallback=None)
|
||||
except configparser.NoSectionError:
|
||||
logger.error('未找到 web 配置项,请检查拼写')
|
||||
return None
|
||||
|
||||
if web_status is None:
|
||||
logger.error('web 配置值为空')
|
||||
return True
|
||||
|
||||
if web_status == "True" or web_status == "true":
|
||||
return True
|
||||
|
||||
if web_status == "False" or web_status == "false":
|
||||
return False
|
95
src/generator.py
Normal file
@ -0,0 +1,95 @@
|
||||
from collections import Counter
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from .analyzer import analyze_sentiment, calculate_weight, classify_and_extract_keywords
|
||||
from .config import Config
|
||||
from .scraper import Blog
|
||||
|
||||
|
||||
def build_data():
|
||||
"""
|
||||
目前只有一个主题,构建数据部分后期会再进行重构拆分
|
||||
:return: 网页渲染数据
|
||||
"""
|
||||
# 读取配置
|
||||
config = Config("config.ini")
|
||||
|
||||
# 创建博客对象
|
||||
try:
|
||||
my_blog = Blog(config.rss_url)
|
||||
except Exception as e:
|
||||
logger.error(f"Feed 无法创建博客对象: {str(e)}")
|
||||
return None
|
||||
|
||||
logger.debug(my_blog)
|
||||
|
||||
# 构建博客基本数据
|
||||
data = {
|
||||
"blog_name": my_blog.title,
|
||||
"blog_link": my_blog.link,
|
||||
"blog_article_count": my_blog.article_count,
|
||||
"blog_article_word_count": my_blog.article_word_count,
|
||||
}
|
||||
|
||||
if my_blog.life is None:
|
||||
data.update({
|
||||
"blog_life": 0
|
||||
})
|
||||
else:
|
||||
data.update({
|
||||
"blog_life_year": my_blog.life // 365,
|
||||
"blog_life_day": my_blog.life % 365,
|
||||
})
|
||||
|
||||
# 博客文章处理
|
||||
for i, post in enumerate(my_blog.post_lists(), 1):
|
||||
# 情感分
|
||||
post.score = analyze_sentiment(post.content)
|
||||
# 分类, 关键字
|
||||
post.category, post.keys = classify_and_extract_keywords(text=post.content, topK=21,
|
||||
stopwords='data/stop_words.txt',
|
||||
tech_terms_file='data/tech_terms.txt')
|
||||
# 权重, 日子计算
|
||||
post.weight, post.date = calculate_weight(post.time)
|
||||
|
||||
logger.info(f"Post #{i}:")
|
||||
logger.info(post)
|
||||
|
||||
# 博客文章权重计算
|
||||
weights = [post.weight for post in my_blog.post_lists()]
|
||||
max_weight = max(weights)
|
||||
max_item = [post for post in my_blog.post_lists() if post.weight == max_weight][0]
|
||||
|
||||
data.update({
|
||||
"blog_title": max_item.title,
|
||||
"blog_content": max_item.content[0:50],
|
||||
"blog_content_date": max_item.date,
|
||||
})
|
||||
|
||||
# 暂时只有一个主题
|
||||
# 博客关键词计算 5 个
|
||||
all_keys = []
|
||||
for post in my_blog.post_lists():
|
||||
all_keys.extend(post.keys)
|
||||
|
||||
keyword_counts = Counter(all_keys)
|
||||
top_keywords = keyword_counts.most_common(5)
|
||||
data.update({
|
||||
"blog_top_keywords": top_keywords
|
||||
})
|
||||
|
||||
# 博客分类计算
|
||||
categories = [post.category for post in my_blog.post_lists()]
|
||||
cat_counts = Counter(categories)
|
||||
most_common_cat = cat_counts.most_common(1)[0][0]
|
||||
|
||||
data.update({
|
||||
"blog_category": "技术" if most_common_cat == 1 else "生活"
|
||||
})
|
||||
|
||||
# 输出
|
||||
logger.debug(data)
|
||||
# 写入 config.ini 避免重复计算
|
||||
config.blog_data = data
|
||||
return data
|
153
src/scraper.py
Normal file
@ -0,0 +1,153 @@
|
||||
import feedparser
|
||||
from loguru import logger
|
||||
|
||||
from . import tools
|
||||
|
||||
|
||||
class Blog:
|
||||
def __init__(self, url):
|
||||
try:
|
||||
self.feed = feedparser.parse(url)
|
||||
except Exception as e:
|
||||
logger.error(f'解析 RSS feed 时发生错误: {str(e)}')
|
||||
raise
|
||||
self.posts = [Post(entry) for entry in self.feed.entries]
|
||||
|
||||
def _get_feed_field(self, field):
|
||||
"""
|
||||
从 RSS feed 中获取特定字段
|
||||
"""
|
||||
field_value = self.feed.feed.get(field)
|
||||
if field_value is None:
|
||||
logger.warning(f'{field} 字段不存在!')
|
||||
return field_value
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return self._get_feed_field('title')
|
||||
|
||||
@property
|
||||
def link(self):
|
||||
return self._get_feed_field('link')
|
||||
|
||||
@property
|
||||
def life(self):
|
||||
domain = tools.get_domain(self.link)
|
||||
return tools.get_domain_life(domain)
|
||||
|
||||
@property
|
||||
def article_count(self):
|
||||
return len(self.posts)
|
||||
|
||||
@property
|
||||
def article_word_count(self):
|
||||
return sum(post.word_count for post in self.posts)
|
||||
|
||||
def post_lists(self):
|
||||
return self.posts
|
||||
|
||||
def __str__(self):
|
||||
return f"Blog: {self.title}, Life:{self.life}, Count{self.article_count}. Word count:{self.article_word_count}"
|
||||
|
||||
|
||||
class Post:
|
||||
def __init__(self, entry):
|
||||
# 日期权重
|
||||
self._weight = None
|
||||
# 日子
|
||||
self._date = None
|
||||
# 情感分
|
||||
self._score = None
|
||||
# 关键字
|
||||
self._keys = None
|
||||
# 分类
|
||||
self._category = None
|
||||
self.entry = entry
|
||||
|
||||
def _get_entry_field(self, field):
|
||||
"""
|
||||
从 RSS entry 中获取特定字段
|
||||
"""
|
||||
field_value = self.entry.get(field)
|
||||
if field_value is None:
|
||||
pass
|
||||
# logger.warning(f'{field} 字段不存在!')
|
||||
return field_value
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return self._get_entry_field('title')
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
description = self._get_entry_field('description')
|
||||
content = self._get_entry_field('content')
|
||||
if content:
|
||||
content = content[0].get('value', '')
|
||||
|
||||
description = tools.remove_html_tags(description) if description else ""
|
||||
content = tools.remove_html_tags(content) if content else ""
|
||||
|
||||
if len(description) < 128 and content:
|
||||
return content
|
||||
else:
|
||||
return description
|
||||
|
||||
@property
|
||||
def time(self):
|
||||
return self._get_entry_field('published')
|
||||
|
||||
@property
|
||||
def link(self):
|
||||
return self._get_entry_field('link')
|
||||
|
||||
@property
|
||||
def word_count(self):
|
||||
return len(self.content) if self.content else 0
|
||||
|
||||
@property
|
||||
def keys(self):
|
||||
return self._keys
|
||||
|
||||
@keys.setter
|
||||
def keys(self, value):
|
||||
self._keys = value
|
||||
|
||||
@property
|
||||
def score(self):
|
||||
return self._score
|
||||
|
||||
@score.setter
|
||||
def score(self, value):
|
||||
self._score = value
|
||||
|
||||
@property
|
||||
def category(self):
|
||||
return self._category
|
||||
|
||||
@category.setter
|
||||
def category(self, value):
|
||||
self._category = value
|
||||
|
||||
@property
|
||||
def date(self):
|
||||
return self._date
|
||||
|
||||
@date.setter
|
||||
def date(self, value):
|
||||
self._date = value
|
||||
|
||||
@property
|
||||
def weight(self):
|
||||
return self._weight
|
||||
|
||||
@weight.setter
|
||||
def weight(self, value):
|
||||
self._weight = value
|
||||
|
||||
def __str__(self):
|
||||
return (f"Post title={self.title[:20]}..., "
|
||||
f" content={self.content[:20]}..., "
|
||||
f" time={self.time}, "
|
||||
f" link={self.link}, "
|
||||
f" word_count={self.word_count}")
|
99
src/tools.py
Normal file
@ -0,0 +1,99 @@
|
||||
from datetime import datetime
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from loguru import logger
|
||||
|
||||
|
||||
def check_website_status(url):
|
||||
"""
|
||||
检测网站是否可以正常访问,取决于 status == 200
|
||||
:param url:网址
|
||||
:return:True 可以访问,False 不可以。
|
||||
"""
|
||||
try:
|
||||
response = requests.get(url, timeout=5) # Set timeout to 5 seconds
|
||||
if response.status_code == 200:
|
||||
return True
|
||||
else:
|
||||
logger.error(f"{url} 网站无法访问,状态码:{response.status_code}")
|
||||
return False
|
||||
except requests.Timeout as e:
|
||||
logger.error(f"{url} 请求超时,错误:{e}")
|
||||
return False
|
||||
except requests.ConnectionError as e:
|
||||
logger.error(f"{url} 连接错误,错误:{e}")
|
||||
return False
|
||||
except requests.RequestException as e:
|
||||
logger.error(f"{url} 网站无法访问,错误:{e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"{url} 未知错误,错误:{e}")
|
||||
return False
|
||||
|
||||
|
||||
def get_domain(url):
|
||||
"""
|
||||
获取 url 注册域名,二级域名 + 顶级域名
|
||||
:param url:url 地址
|
||||
:return:注册域名
|
||||
"""
|
||||
parsed_uri = urlparse(url)
|
||||
subdomain = parsed_uri.netloc.split('.')[-2] # 获取二级域名部分
|
||||
top_domain = parsed_uri.netloc.split('.')[-1] # 获取顶级域名部分
|
||||
return f"{subdomain}.{top_domain}"
|
||||
|
||||
|
||||
def get_domain_life(url):
|
||||
"""
|
||||
域名注册天数
|
||||
:param url:注册域名
|
||||
:return:天数
|
||||
"""
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
|
||||
}
|
||||
domain_url = f"https://rdap.verisign.com/com/v1/domain/{url}"
|
||||
|
||||
try:
|
||||
response = requests.get(domain_url, headers=headers)
|
||||
response.raise_for_status() # Raises stored HTTPError, if one occurred.
|
||||
|
||||
registration_date = response.json().get('events')[0].get('eventDate')
|
||||
if registration_date is None:
|
||||
logger.error("无效响应,未找到 'eventDate'")
|
||||
return None
|
||||
|
||||
date_format = "%Y-%m-%dT%H:%M:%SZ"
|
||||
|
||||
# 将字符串转换为日期对象
|
||||
your_date = datetime.strptime(registration_date, date_format)
|
||||
|
||||
# 当前日期
|
||||
now = datetime.now() # 使用当前日期
|
||||
|
||||
# 计算天数差
|
||||
delta = now - your_date
|
||||
|
||||
return delta.days
|
||||
|
||||
except requests.exceptions.HTTPError as err:
|
||||
logger.error(f"HTTP 错误: {err}")
|
||||
except requests.exceptions.RequestException as err:
|
||||
logger.error(f"请求错误: {err}")
|
||||
except ValueError as err:
|
||||
logger.error(f"日期解析错误: {err}")
|
||||
except Exception as err:
|
||||
logger.error(f"未预期的错误: {err}")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def remove_html_tags(text):
|
||||
"""
|
||||
移除无用 html 标签
|
||||
:param text:源文本
|
||||
:return:文本
|
||||
"""
|
||||
return BeautifulSoup(text, "html.parser").get_text()
|
BIN
static/endofyear.jpg
Normal file
After Width: | Height: | Size: 123 KiB |
7
static/painting/css/animate.min.css
vendored
Normal file
349
static/painting/css/normalize.css
vendored
Normal file
@ -0,0 +1,349 @@
|
||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the `main` element consistently in IE.
|
||||
*/
|
||||
|
||||
main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background on active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57-
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers.
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE 10+.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10.
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||
*/
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10+.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
227
static/painting/css/painting.css
Normal file
@ -0,0 +1,227 @@
|
||||
@font-face {
|
||||
font-family: 'wenkai';
|
||||
src: url('../font/LXGWWenKaiMonoGB-Bold.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@media screen and (max-width: 320px) {
|
||||
html {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 55vh;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (min-width: 320px) and (max-width: 480px) {
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 55vh;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 480px) and (max-width: 768px) {
|
||||
html {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 55vh;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
html {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 55vh;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
html {
|
||||
font-family: 'wenkai', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
||||
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||
'Noto Color Emoji';
|
||||
|
||||
letter-spacing: 0.25rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
min-width: 375px;
|
||||
height: 100vh;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.container.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
#tab1 {
|
||||
background-image: url('../img/page1.jpg');
|
||||
}
|
||||
|
||||
#tab2 {
|
||||
background-image: url('../img/page2.jpg');
|
||||
}
|
||||
|
||||
.tab2-box {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
padding: 1rem 1rem;
|
||||
font-size: 1.75rem;
|
||||
color: #FFFFFFD9;
|
||||
}
|
||||
|
||||
.tab2-box > p > small {
|
||||
line-height: 3rem;
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
|
||||
#tab3 {
|
||||
background-image: url('../img/page3.jpg');
|
||||
}
|
||||
|
||||
.tab3-box {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
padding: 1rem 1rem;
|
||||
font-size: 1.75rem;
|
||||
color: #ffa940;
|
||||
}
|
||||
|
||||
.tab3-box > p > small {
|
||||
line-height: 3rem;
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
|
||||
#tab4 {
|
||||
background-image: url('../img/page4.jpg');
|
||||
}
|
||||
|
||||
.tab4-box {
|
||||
writing-mode: vertical-lr;
|
||||
position: fixed;
|
||||
top: 2rem;
|
||||
font-size: 1.75rem;
|
||||
color: #FFFFFFD9;
|
||||
}
|
||||
|
||||
#tab4 > .tab4-box > p:last-child {
|
||||
writing-mode: horizontal-tb;
|
||||
margin: 0 0;
|
||||
height: 30vh;
|
||||
width: 13rem;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
|
||||
#tab5 {
|
||||
background-image: url('../img/page5.jpg');
|
||||
}
|
||||
|
||||
.tab5-box {
|
||||
position: relative;
|
||||
top: 2rem;
|
||||
left: 0.75rem;
|
||||
color: #597ef7;
|
||||
}
|
||||
|
||||
#tab5 > .tab5-box > p:nth-child(1) {
|
||||
position: relative;
|
||||
top: -6rem;
|
||||
left: 0.8rem;
|
||||
transform: rotate(-43deg);
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
#tab5 > .tab5-box > p:nth-child(2) {
|
||||
position: relative;
|
||||
top: 3.2rem;
|
||||
left: 7.8rem;
|
||||
transform: rotate(42deg);
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
|
||||
#tab5 > .tab5-box > p:nth-child(3) {
|
||||
position: relative;
|
||||
top: 8rem;
|
||||
left: 2rem;
|
||||
transform: rotate(45deg);
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
#tab5 > .tab5-box > p:nth-child(4) {
|
||||
position: relative;
|
||||
top: -13rem;
|
||||
left: 11.7rem;
|
||||
transform: rotate(-50deg);
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
#tab5 > .tab5-box > p:nth-child(5) {
|
||||
position: relative;
|
||||
top: -5.6rem;
|
||||
left: 9.7rem;
|
||||
transform: rotate(-50deg);
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
#tab5 > .tab5-box > p:nth-child(6) {
|
||||
position: relative;
|
||||
top: 2rem;
|
||||
left: 2rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
#tab5 > .tab5-box > p:nth-child(6) > small {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
#tab5 > .tab5-box > p:nth-child(7) {
|
||||
position: relative;
|
||||
top: 2rem;
|
||||
left: 2rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
#tab5 > .tab5-box > p:nth-child(7) > small {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
|
||||
#tab6 {
|
||||
background-image: url('../img/page6.jpg');
|
||||
}
|
||||
|
||||
#tab6 > .tab6-box {
|
||||
position: fixed;
|
||||
bottom: 7%;
|
||||
padding-left: 2rem;
|
||||
font-size: 1.75rem;
|
||||
color: #FFFFFFD9;
|
||||
}
|
||||
|
||||
#tab7 {
|
||||
background-image: url('../img/page7.jpg');
|
||||
}
|
||||
|
BIN
static/painting/font/LXGWWenKaiMonoGB-Bold.ttf
Normal file
BIN
static/painting/img/page1.jpg
Normal file
After Width: | Height: | Size: 266 KiB |
BIN
static/painting/img/page2.jpg
Normal file
After Width: | Height: | Size: 312 KiB |
BIN
static/painting/img/page3.jpg
Normal file
After Width: | Height: | Size: 426 KiB |
BIN
static/painting/img/page4.jpg
Normal file
After Width: | Height: | Size: 332 KiB |
BIN
static/painting/img/page5.jpg
Normal file
After Width: | Height: | Size: 418 KiB |
BIN
static/painting/img/page6.jpg
Normal file
After Width: | Height: | Size: 448 KiB |
BIN
static/painting/img/page7.jpg
Normal file
After Width: | Height: | Size: 490 KiB |
104
templates/painting.html
Normal file
@ -0,0 +1,104 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>EndOfYear</title>
|
||||
{% if web_status == 1 %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='painting/css/normalize.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='painting/css/animate.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='painting/css/painting.css') }}">
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="painting/css/normalize.css">
|
||||
<link rel="stylesheet" href="painting/css/animate.min.css">
|
||||
<link rel="stylesheet" href="painting/css/painting.css">
|
||||
{% endif %}
|
||||
<script async src="https://umami.7wate.org/script.js" data-website-id="635fec50-bc6c-4ac2-909a-e2a7403438be"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container active animate__animated animate__fadeIn animate__slow" id="tab1"></div>
|
||||
<div class="container animate__animated animate__fadeIn animate__slow" id="tab2">
|
||||
<div class="tab2-box">
|
||||
<p class="animate__animated animate__fadeIn animate__delay-1s">亲爱的{{ data.blog_name }}</p>
|
||||
{% if data.blog_life == 0 %}
|
||||
<p class="animate__animated animate__fadeIn animate__delay-2s">旧事如梦,一年已过</p>
|
||||
<p class="animate__animated animate__fadeIn animate__delay-2s">贰三年、感谢有你!</p>
|
||||
{% else %}
|
||||
<p class="animate__animated animate__fadeIn animate__delay-2s">今天是我们相识的</p>
|
||||
<p class="animate__animated animate__fadeIn animate__delay-3s">第 <small>{{ data.blog_life_year }}</small> 年
|
||||
<small>{{ data.blog_life_day }}</small> 天</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="container animate__animated animate__fadeIn animate__slow" id="tab3">
|
||||
<div class="tab3-box">
|
||||
<p class="animate__animated animate__fadeInUp animate__delay-1s">这一年你写下了</p>
|
||||
<p class="animate__animated animate__fadeInUp animate__delay-2s"><small>{{ data.blog_article_count }}</small>
|
||||
篇博文</p>
|
||||
<p class="animate__animated animate__fadeInUp animate__delay-3s"><small>{{ data.blog_article_word_count
|
||||
}}</small> 个文字</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container animate__animated animate__fadeIn animate__slow" id="tab4">
|
||||
<div class="tab4-box">
|
||||
<p class="animate__animated animate__fadeInDown animate__delay-1s">{{ data.blog_content_date }}那天,你写下了</p>
|
||||
<p class="animate__animated animate__fadeInDown animate__delay-2s">{{ data.blog_title }}</p>
|
||||
<p class="animate__animated animate__fadeInDown animate__delay-3s">{{ data.blog_content }}<small>……</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container animate__animated animate__fadeIn animate__slow" id="tab5">
|
||||
<div class="tab5-box">
|
||||
{% for keyword in data.blog_top_keywords %}
|
||||
<p>{{ keyword[0] }}</p>
|
||||
{% endfor %}
|
||||
<p class="animate__animated animate__fadeInDown animate__delay-1s">这些都是<small>你</small>的</p>
|
||||
<p class="animate__animated animate__fadeInDown animate__delay-2s">专属<small>关键词</small></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container animate__animated animate__fadeIn animate__slow" id="tab6">
|
||||
<div class="tab6-box">
|
||||
<p class="animate__animated animate__fadeInLeft animate__delay-1s">热爱{{ data.blog_category }}的你</p>
|
||||
<p class="animate__animated animate__fadeInLeft animate__delay-2s">一定要继续砥砺前行!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container animate__animated animate__fadeIn animate__slow" id="tab7">
|
||||
<div class="tab7-box">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
console.log("Window inner size: ", window.innerWidth, "x", window.innerHeight);
|
||||
console.log("Window outer size: ", window.outerWidth, "x", window.outerHeight);
|
||||
console.log("Screen size: ", screen.width, "x", screen.height);
|
||||
console.log("Screen available size: ", screen.availWidth, "x", screen.availHeight);
|
||||
var carousel = {
|
||||
currentIndex: 0,
|
||||
tabs: [],
|
||||
|
||||
init: function () {
|
||||
this.tabs = document.getElementsByClassName('container');
|
||||
|
||||
for (let i = 0; i < this.tabs.length; i++) {
|
||||
this.tabs[i].addEventListener('click', () => {
|
||||
this.next();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
next: function () {
|
||||
this.tabs[this.currentIndex].classList.remove('active');
|
||||
this.currentIndex = (this.currentIndex + 1) % this.tabs.length;
|
||||
this.tabs[this.currentIndex].classList.add('active');
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = function () {
|
||||
carousel.init();
|
||||
};
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|