Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
81b859aca9 | |||
c5c6fb97e4 | |||
c682ca2893 | |||
827841f9e6 | |||
932dd9264b | |||
44d87a8ffb | |||
b1f6c6cdd5 | |||
6b6d1a27d5 | |||
d5f40e43ed | |||
e8ece417da | |||
f85e94b1e1 | |||
a4ae52471a | |||
c4b47c86d5 | |||
045897fc7b | |||
cbac5ee395 | |||
e5cdab0fc1 | |||
575fa7e60e | |||
337bd3a900 | |||
82cba2e2c3 | |||
f76ac1e8fe | |||
631b00db0b | |||
bb97034d14 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -989,7 +989,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orario-scolastico-itet"
|
name = "orario-scolastico-itet"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"askama",
|
"askama",
|
||||||
"axum",
|
"axum",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "orario-scolastico-itet"
|
name = "orario-scolastico-itet"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
373
LICENSE
Normal file
373
LICENSE
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
68
README.md
Normal file
68
README.md
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# Orario Scolastico
|
||||||
|
|
||||||
|
Orario Scolastico is a web application that allows students to view their school
|
||||||
|
timetable in a more readable way. The application works by downloading the PDF
|
||||||
|
file containing the timetable from the school website and then displaying it in
|
||||||
|
a more user-friendly format.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### If you want to use Docker Compose
|
||||||
|
|
||||||
|
- [Git](https://git-scm.com/downloads) (optional)
|
||||||
|
- [Docker Compose](https://docs.docker.com/compose/install/)
|
||||||
|
|
||||||
|
### If you don't want to use Docker Compose
|
||||||
|
|
||||||
|
- [Git](https://git-scm.com/downloads) (optional)
|
||||||
|
- [Cargo](https://doc.rust-lang.org/stable/cargo/getting-started/installation.html)
|
||||||
|
- [Python](https://www.python.org/downloads/)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
If you want to self-host the project, you can follow the steps below:
|
||||||
|
|
||||||
|
### With Docker Compose
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.riefolo.me/mariano/orario-scolastico-itet.git
|
||||||
|
cd orario-scolastico-itet
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Without Docker Compose
|
||||||
|
|
||||||
|
If you prefer to build the project without Docker, you can follow the steps below:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.riefolo.me/mariano/orario-scolastico-itet.git
|
||||||
|
cd orario-scolastico-itet
|
||||||
|
cargo build --release
|
||||||
|
./target/release/orario-scolastico-itet
|
||||||
|
```
|
||||||
|
|
||||||
|
Consider that this may not work if you are not on a Linux machine.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Pull requests are welcome. For major changes, please open an issue first
|
||||||
|
to discuss what you would like to change.
|
||||||
|
|
||||||
|
Please make sure to update tests as appropriate.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### Does this project DoS the school website?
|
||||||
|
|
||||||
|
The project features a cache system that prevents it from making too much
|
||||||
|
requests. The list of PDFs is only fetched every 30 minutes only if a user asks for
|
||||||
|
it, and the pdf files are only fetched once per file.
|
||||||
|
|
||||||
|
### Can I use this project with my school?
|
||||||
|
|
||||||
|
At the moment, the project is only compatible with the
|
||||||
|
[ITET Cassandro Fermi Nervi](https://cassandroferminervi.edu.it/) institute in Italy.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Orario Scolastico is released under the [Mozilla Public License 2.0](LICENSE)
|
@ -5,7 +5,7 @@ use axum::{
|
|||||||
routing::get,
|
routing::get,
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
use csv::StringRecord;
|
use csv::{Reader, StringRecord};
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use scraper::{Html, Selector};
|
use scraper::{Html, Selector};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@ -27,7 +27,7 @@ pub fn get_routes() -> Router {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_pdf_links() -> Result<Vec<String>, reqwest::Error> {
|
async fn get_pdf() -> Result<HashMap<String, Vec<String>>, reqwest::Error> {
|
||||||
if let Ok(metadata) = fs::metadata("pdf.json") {
|
if let Ok(metadata) = fs::metadata("pdf.json") {
|
||||||
let last_modified = metadata.modified().unwrap();
|
let last_modified = metadata.modified().unwrap();
|
||||||
let now = std::time::SystemTime::now();
|
let now = std::time::SystemTime::now();
|
||||||
@ -44,11 +44,18 @@ async fn get_pdf_links() -> Result<Vec<String>, reqwest::Error> {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let dom = Html::parse_document(&response);
|
let dom = Html::parse_document(&response);
|
||||||
let selector = Selector::parse(".wp-block-list > li > a").unwrap();
|
let selector = Selector::parse("a:has(> strong)[href*=pdf]").unwrap();
|
||||||
let mut result = Vec::new();
|
let mut result: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
|
|
||||||
for element in dom.select(&selector) {
|
for element in dom.select(&selector) {
|
||||||
result.push(element.attr("href").unwrap().to_owned());
|
result
|
||||||
|
.entry(String::from("links"))
|
||||||
|
.or_default()
|
||||||
|
.push(element.attr("href").unwrap().to_owned());
|
||||||
|
result
|
||||||
|
.entry(String::from("names"))
|
||||||
|
.or_default()
|
||||||
|
.push(element.text().collect());
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = serde_json::to_string(&result).unwrap();
|
let data = serde_json::to_string(&result).unwrap();
|
||||||
@ -58,18 +65,18 @@ async fn get_pdf_links() -> Result<Vec<String>, reqwest::Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn pdf() -> impl IntoResponse {
|
pub async fn pdf() -> impl IntoResponse {
|
||||||
match get_pdf_links().await {
|
match get_pdf().await {
|
||||||
Ok(x) => (StatusCode::OK, Json(json!({ "links": x}))),
|
Ok(x) => (StatusCode::OK, Json(json!(x["names"]))),
|
||||||
Err(e) => (StatusCode::OK, Json(json!({ "error": e.to_string()}))),
|
Err(e) => (StatusCode::OK, Json(json!({ "error": e.to_string()}))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn download_pdf(id: u8) -> Result<String, Error> {
|
async fn get_csv(id: u8) -> Result<String, Error> {
|
||||||
let links = get_pdf_links().await.unwrap();
|
let links = get_pdf().await.unwrap();
|
||||||
if id as usize >= links.len() {
|
if id as usize >= links.len() {
|
||||||
return Err(failure::err_msg("Invalid ID"));
|
return Err(failure::err_msg("Invalid ID"));
|
||||||
}
|
}
|
||||||
let url = &links[id as usize];
|
let url = &links["links"][id as usize];
|
||||||
|
|
||||||
let mut hasher = Sha1::new();
|
let mut hasher = Sha1::new();
|
||||||
hasher.update(url.as_bytes());
|
hasher.update(url.as_bytes());
|
||||||
@ -82,7 +89,7 @@ async fn download_pdf(id: u8) -> Result<String, Error> {
|
|||||||
let pdf_path = format!("pdf/{}.pdf", filename);
|
let pdf_path = format!("pdf/{}.pdf", filename);
|
||||||
let csv_path = format!("csv/{}.csv", filename);
|
let csv_path = format!("csv/{}.csv", filename);
|
||||||
|
|
||||||
if fs::metadata(&pdf_path).is_err() {
|
if fs::metadata(&csv_path).is_err() {
|
||||||
let response = reqwest::get(url).await?;
|
let response = reqwest::get(url).await?;
|
||||||
let body = response.bytes().await?;
|
let body = response.bytes().await?;
|
||||||
std::fs::write(&pdf_path, body)?;
|
std::fs::write(&pdf_path, body)?;
|
||||||
@ -92,7 +99,10 @@ async fn download_pdf(id: u8) -> Result<String, Error> {
|
|||||||
.arg(&pdf_path)
|
.arg(&pdf_path)
|
||||||
.arg(&csv_path)
|
.arg(&csv_path)
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("Failed to generate csv file");
|
.expect("Failed to generate csv file")
|
||||||
|
.wait()?;
|
||||||
|
|
||||||
|
fs::remove_file(&pdf_path)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(csv_path)
|
Ok(csv_path)
|
||||||
@ -105,12 +115,12 @@ pub struct FilterByTeacherQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
teacher: u8,
|
teacher: Option<u8>,
|
||||||
weekdays: HashMap<String, Range<usize>>,
|
weekdays: HashMap<String, Range<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_teacher(Query(params): Query<FilterByTeacherQuery>) -> impl IntoResponse {
|
pub async fn get_teacher(Query(params): Query<FilterByTeacherQuery>) -> impl IntoResponse {
|
||||||
let filename = match download_pdf(params.id).await {
|
let filename = match get_csv(params.id).await {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return (
|
return (
|
||||||
@ -122,11 +132,11 @@ pub async fn get_teacher(Query(params): Query<FilterByTeacherQuery>) -> impl Int
|
|||||||
let csv_content = fs::read_to_string(&filename).unwrap();
|
let csv_content = fs::read_to_string(&filename).unwrap();
|
||||||
|
|
||||||
let mut rdr = csv::Reader::from_reader(csv_content.as_bytes());
|
let mut rdr = csv::Reader::from_reader(csv_content.as_bytes());
|
||||||
let header = get_header(rdr.headers().unwrap());
|
let header = get_header_wrapper(&mut rdr);
|
||||||
|
|
||||||
for record in rdr.records() {
|
for record in rdr.records() {
|
||||||
let record = record.unwrap();
|
let record = record.unwrap();
|
||||||
if record[header.teacher as usize] == params.professore {
|
if record[header.teacher.unwrap() as usize] == params.professore {
|
||||||
let mut result: HashMap<String, Vec<&str>> = HashMap::new();
|
let mut result: HashMap<String, Vec<&str>> = HashMap::new();
|
||||||
for (i, cell) in record.iter().enumerate() {
|
for (i, cell) in record.iter().enumerate() {
|
||||||
for (j, range) in header.weekdays.clone() {
|
for (j, range) in header.weekdays.clone() {
|
||||||
@ -152,7 +162,7 @@ pub struct FilterByClassQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_class(Query(params): Query<FilterByClassQuery>) -> impl IntoResponse {
|
pub async fn get_class(Query(params): Query<FilterByClassQuery>) -> impl IntoResponse {
|
||||||
let filename = match download_pdf(params.id).await {
|
let filename = match get_csv(params.id).await {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return (
|
return (
|
||||||
@ -164,7 +174,7 @@ pub async fn get_class(Query(params): Query<FilterByClassQuery>) -> impl IntoRes
|
|||||||
let csv_content = fs::read_to_string(&filename).unwrap();
|
let csv_content = fs::read_to_string(&filename).unwrap();
|
||||||
|
|
||||||
let mut rdr = csv::Reader::from_reader(csv_content.as_bytes());
|
let mut rdr = csv::Reader::from_reader(csv_content.as_bytes());
|
||||||
let header = get_header(rdr.headers().unwrap());
|
let header = get_header_wrapper(&mut rdr);
|
||||||
|
|
||||||
let mut result: HashMap<String, Vec<String>> = HashMap::new();
|
let mut result: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
|
|
||||||
@ -182,7 +192,8 @@ pub async fn get_class(Query(params): Query<FilterByClassQuery>) -> impl IntoRes
|
|||||||
v.resize(range.end - range.start, String::new());
|
v.resize(range.end - range.start, String::new());
|
||||||
v
|
v
|
||||||
});
|
});
|
||||||
value[i - range.start] = record[header.teacher as usize].to_string();
|
value[i - range.start] =
|
||||||
|
record[header.teacher.unwrap() as usize].to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,9 +210,13 @@ pub async fn get_class(Query(params): Query<FilterByClassQuery>) -> impl IntoRes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_header(record: &StringRecord) -> Header {
|
pub fn get_header(record: &StringRecord, header: Option<Header>) -> Header {
|
||||||
let mut docente: u8 = 0;
|
let mut teacher: Option<u8> = None;
|
||||||
let mut weekdays = HashMap::new();
|
let mut weekdays = HashMap::new();
|
||||||
|
if let Some(h) = header {
|
||||||
|
teacher = h.teacher;
|
||||||
|
weekdays = h.weekdays;
|
||||||
|
}
|
||||||
|
|
||||||
for (i, field) in record.iter().enumerate() {
|
for (i, field) in record.iter().enumerate() {
|
||||||
let next_index = record
|
let next_index = record
|
||||||
@ -210,29 +225,36 @@ pub fn get_header(record: &StringRecord) -> Header {
|
|||||||
.position(|x| !x.is_empty())
|
.position(|x| !x.is_empty())
|
||||||
.unwrap_or(record.len());
|
.unwrap_or(record.len());
|
||||||
|
|
||||||
let field = field.replace("ì", "i").replace(" ", "").to_uppercase();
|
let field = field.replace(" ", "").to_uppercase();
|
||||||
|
|
||||||
if field == "DOCENTE" {
|
if field == "DOCENTE" {
|
||||||
docente = i as u8;
|
teacher = Some(i as u8);
|
||||||
} else if field == "LUNEDI" {
|
} else if field.starts_with("LUN") {
|
||||||
weekdays.insert("Lunedì".to_string(), i..next_index + i + 1);
|
weekdays.insert("Lunedì".to_string(), i..next_index + i + 1);
|
||||||
} else if field == "MARTEDI" {
|
} else if field.starts_with("MAR") {
|
||||||
weekdays.insert("Martedì".to_string(), i..next_index + i + 1);
|
weekdays.insert("Martedì".to_string(), i..next_index + i + 1);
|
||||||
} else if field == "MERCOLEDI" {
|
} else if field.starts_with("MER") {
|
||||||
weekdays.insert("Mercoledì".to_string(), i..next_index + i + 1);
|
weekdays.insert("Mercoledì".to_string(), i..next_index + i + 1);
|
||||||
} else if field == "GIOVEDI" {
|
} else if field.starts_with("GIO") {
|
||||||
weekdays.insert("Giovedì".to_string(), i..next_index + i + 1);
|
weekdays.insert("Giovedì".to_string(), i..next_index + i + 1);
|
||||||
} else if field == "VENERDI" {
|
} else if field.starts_with("VEN") {
|
||||||
weekdays.insert("Venerdì".to_string(), i..next_index + i + 1);
|
weekdays.insert("Venerdì".to_string(), i..next_index + i + 1);
|
||||||
} else if field == "SABATO" {
|
} else if field.starts_with("SAB") {
|
||||||
weekdays.insert("Sabato".to_string(), i..next_index + i + 1);
|
weekdays.insert("Sabato".to_string(), i..next_index + i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Header {
|
Header { teacher, weekdays }
|
||||||
teacher: docente,
|
|
||||||
weekdays,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_header_wrapper(rdr: &mut Reader<&[u8]>) -> Header {
|
||||||
|
let mut header = get_header(rdr.headers().unwrap(), None);
|
||||||
|
let mut records = rdr.records();
|
||||||
|
while header.teacher.is_none() {
|
||||||
|
let record = records.next().unwrap().unwrap();
|
||||||
|
header = get_header(&record, Some(header));
|
||||||
|
}
|
||||||
|
header
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@ -241,7 +263,7 @@ pub struct GetTeachersQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_teachers(Query(params): Query<GetTeachersQuery>) -> impl IntoResponse {
|
pub async fn get_teachers(Query(params): Query<GetTeachersQuery>) -> impl IntoResponse {
|
||||||
let filename = match download_pdf(params.id).await {
|
let filename = match get_csv(params.id).await {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return (
|
return (
|
||||||
@ -253,7 +275,7 @@ pub async fn get_teachers(Query(params): Query<GetTeachersQuery>) -> impl IntoRe
|
|||||||
|
|
||||||
let csv_content = fs::read_to_string(&filename).unwrap();
|
let csv_content = fs::read_to_string(&filename).unwrap();
|
||||||
let mut rdr = csv::Reader::from_reader(csv_content.as_bytes());
|
let mut rdr = csv::Reader::from_reader(csv_content.as_bytes());
|
||||||
let header = get_header(rdr.headers().unwrap());
|
let header = get_header_wrapper(&mut rdr);
|
||||||
|
|
||||||
let mut teachers = Vec::new();
|
let mut teachers = Vec::new();
|
||||||
|
|
||||||
@ -263,7 +285,7 @@ pub async fn get_teachers(Query(params): Query<GetTeachersQuery>) -> impl IntoRe
|
|||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(teacher) = record.get(header.teacher as usize) {
|
if let Some(teacher) = record.get(header.teacher.unwrap() as usize) {
|
||||||
if !teacher.is_empty() {
|
if !teacher.is_empty() {
|
||||||
teachers.push(teacher.to_owned());
|
teachers.push(teacher.to_owned());
|
||||||
}
|
}
|
||||||
@ -292,7 +314,7 @@ fn is_valid_class(class: &str) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_classes(Query(params): Query<GetClassesQuery>) -> impl IntoResponse {
|
pub async fn get_classes(Query(params): Query<GetClassesQuery>) -> impl IntoResponse {
|
||||||
let filename = match download_pdf(params.id).await {
|
let filename = match get_csv(params.id).await {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return (
|
return (
|
||||||
@ -304,7 +326,8 @@ pub async fn get_classes(Query(params): Query<GetClassesQuery>) -> impl IntoResp
|
|||||||
let csv_content = fs::read_to_string(&filename).unwrap();
|
let csv_content = fs::read_to_string(&filename).unwrap();
|
||||||
|
|
||||||
let mut rdr = csv::Reader::from_reader(csv_content.as_bytes());
|
let mut rdr = csv::Reader::from_reader(csv_content.as_bytes());
|
||||||
let header = get_header(rdr.headers().unwrap());
|
let header = get_header_wrapper(&mut rdr);
|
||||||
|
println!("weekdays: {:?}", header.weekdays);
|
||||||
|
|
||||||
let mut classes: Vec<HashMap<char, HashSet<String>>> = vec![HashMap::new(); 5];
|
let mut classes: Vec<HashMap<char, HashSet<String>>> = vec![HashMap::new(); 5];
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ pub fn get_routes() -> axum::Router {
|
|||||||
axum::Router::new()
|
axum::Router::new()
|
||||||
.route("/", get(index))
|
.route("/", get(index))
|
||||||
.nest_service("/static", ServeDir::new("static"))
|
.nest_service("/static", ServeDir::new("static"))
|
||||||
|
.fallback(not_found)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
@ -21,3 +22,13 @@ async fn index() -> impl IntoResponse {
|
|||||||
let html = template.render().unwrap();
|
let html = template.render().unwrap();
|
||||||
(StatusCode::OK, Html(html).into_response())
|
(StatusCode::OK, Html(html).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "404.html")]
|
||||||
|
struct NotFoundTemplate {}
|
||||||
|
|
||||||
|
async fn not_found() -> impl IntoResponse {
|
||||||
|
let template = NotFoundTemplate {};
|
||||||
|
let html = template.render().unwrap();
|
||||||
|
(StatusCode::NOT_FOUND, Html(html).into_response())
|
||||||
|
}
|
||||||
|
21
static/app.webmanifest
Normal file
21
static/app.webmanifest
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"id": "/",
|
||||||
|
"start_url": "/",
|
||||||
|
"scope": "/",
|
||||||
|
"name": "Orario Scolastico (ITET Cassandro Fermi Nervi)",
|
||||||
|
"short_name": "Orario ITET",
|
||||||
|
"display": "standalone",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "icon_x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "maskable_icon_x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
static/favicon.png
Normal file
BIN
static/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 475 KiB |
BIN
static/icon_x512.png
Normal file
BIN
static/icon_x512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 237 KiB |
@ -1,21 +1,24 @@
|
|||||||
function load_links() {
|
function load_pdfs() {
|
||||||
fetch("/api/pdf")
|
fetch("/api/pdf")
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
data.links.forEach((el) => {
|
if (data === undefined || data.length === 0) {
|
||||||
const select = document.getElementById("links");
|
show_error(null, "Nessun orario trovato");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data.forEach((el) => {
|
||||||
|
const select = document.getElementById("pdf");
|
||||||
const option = document.createElement("option");
|
const option = document.createElement("option");
|
||||||
option.value = i;
|
option.value = i;
|
||||||
option.text = el.split("/").pop().split(".pdf")[0];
|
option.text = el;
|
||||||
select.appendChild(option);
|
select.appendChild(option);
|
||||||
i++;
|
i++;
|
||||||
});
|
});
|
||||||
document.getElementById("links-label").removeAttribute("aria-busy");
|
document.getElementById("pdf-label").removeAttribute("aria-busy");
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
alert("Errore nel caricamento dei dati, riprova più tardi.");
|
show_error(error);
|
||||||
console.error("Error:", error);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,14 +33,14 @@ function load_teachers(id) {
|
|||||||
data.forEach((el) => {
|
data.forEach((el) => {
|
||||||
const option = document.createElement("option");
|
const option = document.createElement("option");
|
||||||
option.value = el;
|
option.value = el;
|
||||||
|
option.text = el;
|
||||||
list.appendChild(option);
|
list.appendChild(option);
|
||||||
i++;
|
i++;
|
||||||
});
|
});
|
||||||
document.getElementById("teacher-label").removeAttribute("aria-busy");
|
document.getElementById("teacher-label").removeAttribute("aria-busy");
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
alert("Errore nel caricamento dei dati, riprova più tardi.");
|
show_error(error);
|
||||||
console.error("Error:", error);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,16 +65,19 @@ function load_classes(id) {
|
|||||||
document.getElementById("class-label").removeAttribute("aria-busy");
|
document.getElementById("class-label").removeAttribute("aria-busy");
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
alert("Errore nel caricamento dei dati, riprova più tardi.");
|
show_error(error);
|
||||||
console.error("Error:", error);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function make_teacher_table(id, teacher) {
|
function make_teacher_table(id, teacher) {
|
||||||
|
document.getElementById("table-title").innerText =
|
||||||
|
`Orario del docente ${teacher}`;
|
||||||
make_table(`/api/professore?id=${id}&professore=${teacher}`);
|
make_table(`/api/professore?id=${id}&professore=${teacher}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function make_student_table(id, student_class) {
|
function make_student_table(id, student_class) {
|
||||||
|
document.getElementById("table-title").innerText =
|
||||||
|
`Orario della classe ${student_class}`;
|
||||||
make_table(`/api/classe?id=${id}&classe=${student_class}`);
|
make_table(`/api/classe?id=${id}&classe=${student_class}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,19 +144,30 @@ function make_table(url) {
|
|||||||
document.getElementById("table").removeAttribute("aria-busy");
|
document.getElementById("table").removeAttribute("aria-busy");
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
alert("Errore nel caricamento dei dati, riprova più tardi.");
|
show_error(error);
|
||||||
console.error("Error:", error);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function select_pdf() {
|
function select_pdf() {
|
||||||
Cookies.set("id", document.getElementById("links").value);
|
Cookies.set("id", document.getElementById("pdf").value);
|
||||||
|
reset_forms();
|
||||||
|
document.getElementById("type").value = "none";
|
||||||
|
document.getElementById("form-teacher").setAttribute("hidden", "");
|
||||||
|
document.getElementById("form-classes").setAttribute("hidden", "");
|
||||||
document.getElementById("form-type").removeAttribute("hidden");
|
document.getElementById("form-type").removeAttribute("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reset_forms() {
|
||||||
|
document.getElementById("year").value = "none";
|
||||||
|
document.getElementById("section").value = "none";
|
||||||
|
document.getElementById("major").value = "none";
|
||||||
|
document.getElementById("teacher").value = "";
|
||||||
|
}
|
||||||
|
|
||||||
function select_type() {
|
function select_type() {
|
||||||
let id = document.getElementById("links").value;
|
let id = document.getElementById("pdf").value;
|
||||||
let value = document.getElementById("type").value;
|
let value = document.getElementById("type").value;
|
||||||
|
reset_forms();
|
||||||
if (value === "teacher") {
|
if (value === "teacher") {
|
||||||
load_teachers(id);
|
load_teachers(id);
|
||||||
document.getElementById("form-teacher").removeAttribute("hidden");
|
document.getElementById("form-teacher").removeAttribute("hidden");
|
||||||
@ -200,7 +217,7 @@ function load_major() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function select_teacher() {
|
function select_teacher() {
|
||||||
let teacher = document.getElementById("teacher").value;
|
let teacher = document.getElementById("teachers").value;
|
||||||
let teachers = JSON.parse(sessionStorage.getItem("teachers"));
|
let teachers = JSON.parse(sessionStorage.getItem("teachers"));
|
||||||
if (!teachers.includes(teacher)) {
|
if (!teachers.includes(teacher)) {
|
||||||
return false;
|
return false;
|
||||||
@ -224,6 +241,36 @@ function delete_cookies() {
|
|||||||
Cookies.remove("class");
|
Cookies.remove("class");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function show_error(e, message) {
|
||||||
|
let default_error = "Errore nel caricamento dei dati, riprova più tardi";
|
||||||
|
console.error(e ?? default_error);
|
||||||
|
document.getElementById("error").textContent = message ?? default_error;
|
||||||
|
document.getElementById("error-dialog").setAttribute("open", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function filter_teachers() {
|
||||||
|
let input = document.getElementById("teacher");
|
||||||
|
let filter = input.value.toUpperCase();
|
||||||
|
let select = document.getElementById("teachers");
|
||||||
|
let option = select.getElementsByTagName("option");
|
||||||
|
let found_first = false;
|
||||||
|
for (let i = 0; i < option.length; i++) {
|
||||||
|
let txtValue = option[i].textContent;
|
||||||
|
if (txtValue.toUpperCase().indexOf(filter) > -1) {
|
||||||
|
option[i].style.display = "";
|
||||||
|
if (!found_first) {
|
||||||
|
select.selectedIndex = i;
|
||||||
|
found_first = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
option[i].style.display = "none";
|
||||||
|
if (option[i].hasAttribute("selected")) {
|
||||||
|
option[i].removeAttribute("selected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Cookies.get("id") && Cookies.get("type")) {
|
if (Cookies.get("id") && Cookies.get("type")) {
|
||||||
if (Cookies.get("type") === "teacher" && Cookies.get("teacher")) {
|
if (Cookies.get("type") === "teacher" && Cookies.get("teacher")) {
|
||||||
make_teacher_table(Cookies.get("id"), Cookies.get("teacher"));
|
make_teacher_table(Cookies.get("id"), Cookies.get("teacher"));
|
||||||
@ -234,5 +281,5 @@ if (Cookies.get("id") && Cookies.get("type")) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
document.getElementById("forms").removeAttribute("hidden");
|
document.getElementById("forms").removeAttribute("hidden");
|
||||||
load_links();
|
load_pdfs();
|
||||||
}
|
}
|
||||||
|
BIN
static/maskable_icon_x512.png
Normal file
BIN
static/maskable_icon_x512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 124 KiB |
11
templates/404.html
Normal file
11
templates/404.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{% extends "base.html" %} {% block title %}Pagina non trovata{% endblock %} {%
|
||||||
|
block head %}
|
||||||
|
<style>
|
||||||
|
main {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %} {% block content %}
|
||||||
|
<h1>404 Pagina non trovata</h1>
|
||||||
|
<p>La pagina che stai cercando non esiste.</p>
|
||||||
|
{% endblock %}
|
29
templates/base.html
Normal file
29
templates/base.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>{% block title %}{% endblock %}</title>
|
||||||
|
<link
|
||||||
|
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<link rel="icon" type="image/png" href="/static/favicon.png" />
|
||||||
|
<link rel="manifest" href="/static/app.webmanifest" />
|
||||||
|
<script async src="https://cdn.jsdelivr.net/npm/pwacompat" crossorigin="anonymous"></script>
|
||||||
|
{% block head %}{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="container">{% block content %}{% endblock %}</main>
|
||||||
|
<footer class="container">
|
||||||
|
<p>
|
||||||
|
Codice rilasciato sotto licenza
|
||||||
|
<a
|
||||||
|
href="https://git.riefolo.me/mariano/orario-scolastico-itet/src/branch/master/LICENSE"
|
||||||
|
target="_blank"
|
||||||
|
>MPL 2.0</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,86 +1,94 @@
|
|||||||
<!doctype html>
|
{% extends "base.html" %} {% block title %}Orario Scolastico{% endblock %} {%
|
||||||
<html lang="it">
|
block head %}
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title>Orario scolastico</title>
|
|
||||||
<link
|
|
||||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
|
|
||||||
rel="stylesheet"
|
|
||||||
/>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js"></script>
|
||||||
<script src="/static/index.js" defer></script>
|
<script src="/static/index.js" defer></script>
|
||||||
</head>
|
<style>
|
||||||
<body>
|
@media (max-width: 1279px) {
|
||||||
<main class="container">
|
table thead tr {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr {
|
||||||
|
counter-set: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr td {
|
||||||
|
counter-increment: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td::before {
|
||||||
|
content: counter(column) " ora: ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#table-title {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %} {%block content %}
|
||||||
<noscript>
|
<noscript>
|
||||||
<h1>
|
<h1>Per utilizzare questa applicazione è necessario abilitare JavaScript.</h1>
|
||||||
Per utilizzare questa applicazione è necessario abilitare JavaScript.
|
|
||||||
</h1>
|
|
||||||
</noscript>
|
</noscript>
|
||||||
<div id="forms" hidden>
|
<div id="forms" hidden>
|
||||||
<form id="form-pdf">
|
<form id="form-pdf">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label id="links-label" aria-busy="true"
|
<label id="pdf-label" aria-busy="true">Seleziona il calendario</label>
|
||||||
>Quale orario vuoi usare?</label
|
<select id="pdf" onchange="select_pdf()" required>
|
||||||
>
|
<option selected disabled value="none">Seleziona un'opzione</option>
|
||||||
<select id="links" onchange="select_pdf()" required>
|
|
||||||
<option selected disabled value="none">
|
|
||||||
Seleziona un orario
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
<form id="form-type" hidden>
|
<form id="form-type" hidden>
|
||||||
<formfield>
|
<formfield>
|
||||||
<label>Sei un professore o uno studente?</label>
|
<label>Seleziona cosa visualizzare</label>
|
||||||
<select id="type" onchange="select_type()">
|
<select id="type" onchange="select_type()">
|
||||||
<option selected disabled value="none">
|
<option selected disabled value="none">Seleziona un'opzione</option>
|
||||||
Seleziona una categoria
|
<option value="teacher">Orario docente</option>
|
||||||
</option>
|
<option value="student">Orario classe</option>
|
||||||
<option value="teacher">Professore</option>
|
|
||||||
<option value="student">Studente</option>
|
|
||||||
</select>
|
</select>
|
||||||
</formfield>
|
</formfield>
|
||||||
</form>
|
</form>
|
||||||
<form id="form-teacher" onsubmit="return select_teacher()" hidden>
|
<form id="form-teacher" onsubmit="return select_teacher()" hidden>
|
||||||
<formfield>
|
<formfield>
|
||||||
<label id="teacher-label" aria-busy="true"
|
<label id="teacher-label" aria-busy="true">Seleziona docente</label>
|
||||||
>Seleziona il tuo cognome</label
|
<input
|
||||||
>
|
id="teacher"
|
||||||
<input id="teacher" type="text" list="teachers" />
|
type="text"
|
||||||
<datalist id="teachers"></datalist>
|
oninput="filter_teachers()"
|
||||||
|
placeholder="Filtra"
|
||||||
|
/>
|
||||||
|
<select id="teachers"></select>
|
||||||
</formfield>
|
</formfield>
|
||||||
<input type="submit" value="Continua" />
|
<input type="submit" value="Continua" />
|
||||||
</form>
|
</form>
|
||||||
<form id="form-classes" hidden>
|
<form id="form-classes" hidden>
|
||||||
<formfield>
|
<formfield>
|
||||||
<label id="class-label" aria-busy="true"
|
<label id="class-label" aria-busy="true">Seleziona la tua classe</label>
|
||||||
>Seleziona la tua classe</label
|
|
||||||
>
|
|
||||||
<select id="year" onchange="load_section()">
|
<select id="year" onchange="load_section()">
|
||||||
<option selected disabled value="none">Seleziona l'anno</option>
|
<option selected disabled value="none">Seleziona l'anno</option>
|
||||||
</select>
|
</select>
|
||||||
<select id="section" onchange="load_major()">
|
<select id="section" onchange="load_major()">
|
||||||
<option selected disabled value="none">
|
<option selected disabled value="none">Seleziona la sezione</option>
|
||||||
Seleziona la sezione
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
<select id="major" onchange="select_class()">
|
<select id="major" onchange="select_class()">
|
||||||
<option selected disabled value="none">
|
<option selected disabled value="none">Seleziona l'indirizzo</option>
|
||||||
Seleziona l'indirizzo
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
<input type="submit" value="Continua" />
|
<input type="submit" value="Continua" />
|
||||||
</formfield>
|
</formfield>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div id="dashboard" hidden>
|
<div id="dashboard" hidden>
|
||||||
|
<h1 id="table-title"></h1>
|
||||||
<table id="table" class="striped" aria-busy="true"></table>
|
<table id="table" class="striped" aria-busy="true"></table>
|
||||||
<form>
|
<form>
|
||||||
<input type="submit" onclick="delete_cookies()" value="Reimposta" />
|
<input type="submit" onclick="delete_cookies()" value="Reimposta" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
<dialog id="error-dialog">
|
||||||
</body>
|
<p id="error">Errore: non è stato possibile caricare l'orario.</p>
|
||||||
</html>
|
</dialog>
|
||||||
|
{% endblock %}
|
||||||
|
Loading…
Reference in New Issue
Block a user