Compare commits

...

No commits in common. "main" and "docs" have entirely different histories.
main ... docs

20 changed files with 196 additions and 1016 deletions

View File

@ -1,29 +0,0 @@
---
name: Hlášení kompatibility
about: Pokud chcete nahlásit výsledky vašeho testu kompatibility, použijte tuto předlohu
title: 'Kompatibilita: '
labels: kompatibilita
assignees: hernikplays
---
- Název instituce vlastnící instanci / URL:
- Verze iCanteen:
- Verze knihovny:
- Funkční metody: ([odškrtněte](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#task-lists), co funguje)
- [ ] login
- [ ] ziskejJidelnicek
- [ ] jidelnicekDen
- [ ] objednat
- [ ] doBurzy
- [ ] objednatZBurzy
- [ ] ziskatBurzu
- [ ] ziskejUzivatele
**V případě nefunkčnosti některé z metod vkládejte sem chybové hlášky a váš kód**
*Příklad:*
- *chybová hláška pro `login`*
- *chybová hláška pro `jidelnicekDen`*
- *chybová hláška pro `objednat`*

View File

@ -1,24 +0,0 @@
---
name: Nahlášení chyby
about: Pokud něco nefunguje, použijte tuto předlohu
title: ''
labels: bug
assignees: ''
---
**Popište chybu**
Váš popis, co se stalo
**Kód + chybová hláška**
Váš kód, který jste použil/a.
**Očekávané chování**
Co se mělo stát
**Detaily**
- OS: [např. Windows]
- URL na iCanteen instanci (nebo alespoň verzi):
**Dodatečné informace**
Jiné věci, které bychom měli vědět

View File

@ -1,42 +0,0 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Dart check
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Note: This workflow uses the latest stable version of the Dart SDK.
# You can specify other versions if desired, see documentation here:
# https://github.com/dart-lang/setup-dart/blob/main/README.md
# - uses: dart-lang/setup-dart@v1
- uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603
- name: Install dependencies
run: dart pub get
# Uncomment this step to verify the use of 'dart format' on each commit.
# - name: Verify formatting
# run: dart format --output=none --set-exit-if-changed .
# Consider passing '--fatal-infos' for slightly stricter analysis.
- name: Analyze project source
run: dart analyze
# Your project will need to have tests in test/ and a dependency on
# package:test for this step to succeed. Note that Flutter projects will
# want to change this to 'flutter test'.
#- name: Run tests
# run: dart test

14
.gitignore vendored
View File

@ -1,14 +0,0 @@
# Files and directories created by pub.
.dart_tool/
.packages
# Conventional directory for build outputs.
build/
# Omit committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock
.env*
*.test.dart
node_modules/

View File

@ -1,5 +0,0 @@
.env
test
*.test.dart
node_modules
package*.json

View File

@ -1,84 +0,0 @@
## 2.0.0
- Alergeny jsou nyní ve tříde `Alergen`
- Opravena chyba, kdy se HTML alergenů propisovalo do názvu jídla
## 1.1.4
- Opravit info o stavu na burze
## 1.1.3
- Opravit hledání burza URL u jídelen, kde je tlačítko ve tvaru `X ks do burzy`
## 1.1.2
- Opravit negativní čísla v kreditu, účet pro platby by @tpkowastaken in https://github.com/hernikplays/canteenlib/pull/4
## 1.1.1
- Opravit problém s burzou
## 1.1.0-alpha.1
- Experimentální podpora pro SPŠEI Ostrava
- Hezčí kód
- Alergeny
## 1.0.1
- změnit získávání názvu jídla
## 1.0.0
- Stabilizace
## 0.1.0-alpha.17
- Debug informace v `objednatZBurzy`
## 0.1.0-alpha.16
- Opravit zobrazení zda-li jde jídlo objednat, když není objednané žádné jídlo
## 0.1.0-alpha.15
- Úprava nakládání s chybami v `_getRequest`
- `fail` je chyba
## 0.1.0-alpha.14
- Oprava `ziskejBurzu`, kvůli špatnému parsování
## 0.1.0-alpha.13
- Další opravy
- Úprava metod `doBurzy` a `objednat`, aby opravdu mohly vracet aktualizované instance `Jidlo`
## 0.1.0-alpha.12
- Skutečná oprava
## 0.1.0-alpha.11
- Opravit nevkládání URL pro jídlo co má uživatel již v burze
## 0.1.0-alpha.10
- Doufám, že skutečně opraví získávání URL
- Lepší formátování názvu
## 0.1.0-alpha.9
- Vzít změny zpět
## 0.1.0-alpha.8
- Opravit získávání URL z burzy v `jidelnicekDen`
- tridy.dart - Burza: ~~jidlo~~ --> __nazev__
## 0.1.0-alpha.7
- Nastavovat `prihlasen` na `false` v případě chyby i u `ziskejUzivatele`
- Vylepšení dokumentace
- `getFirstSession` je nyní soukromá metoda
[Všechny změny](https://github.com/hernikplays/canteenlib/compare/0.1.0-alpha.6...0.1.0-alpha.7)
## 0.1.0-alpha.6
- `return` místo `throw`
[Všechny změny](https://github.com/hernikplays/canteenlib/compare/0.1.0-alpha.5...0.1.0-alpha.6)
## 0.1.0-alpha.5
- Přechod z `Exception` na `Future.error`
[Všechny změny](https://github.com/hernikplays/canteenlib/compare/0.1.0-alpha.4...0.1.0-alpha.5)
## 0.1.0-alpha.4
- Přidáno získání a objednávání cizích jídel z burzy
- Třída `Jidlo`: ~~cislo~~ 👉 **varianta**
- Nová třída `Burza` pro cizí jídla z burzy
- Více Exceptionů
[Všechny změny](https://github.com/hernikplays/canteenlib/compare/0.1.0-alpha.3...0.1.0-alpha.4)
## 0.1.0-alpha.3
- Kontrolovat správný status kód u GET požadavků
[Všechny změny](https://github.com/hernikplays/canteenlib/compare/0.1.0-alpha.1...0.1.0-alpha.4)
## 0.1.0-alpha.2
- Nevytvářet debugovací soubor
- Místo ziskejKredit používáme ziskejUzivatele (Třída Uzivatel)
- Requesty by měly vyhazovat Exception při chybném požadavku (status kódu)
## 0.1.0-alpha.1
- Aktualizace licence
## 0.1.0-alpha
- Funkční přihlášení
- Funkční zobrazení jídelníčku
- Funkční objednávání jídel z jídelníčku
- Funkční zobrazení kreditu

View File

@ -1,18 +0,0 @@
# Kompatibilita knihovny s instancemi služby iCanteen
V následující tabulce naleznete instance iCanteen, které byly testovány pro jejich funkčnost s touto knihovnou.
Výchozí verze, pro kterou aktuálně je knihovna tvořena, je **2.18.19**
Kantýny, které v adrese obsahují i číslo portu, dokážou být často problémové.
- ❌ - nefunkční nebo netestováno
- ✅ - plně funkční nebo pouze s malými chybami
- ❓ - částečně funkční
| Provozovatel | Verze iCanteen | Funkční | Verze knihovny | Adresa |
|:--------------:|------------------|---------|----------------|---------|
| SŠTE Brno | iCanteen 2.19.13 | ✅ | 2.0.0 | https://stravovani.sstebrno.cz
| SPŠ Třebíč | iCanteen 2.10.25 | ❌ | 0.1.0-alpha | https://icanteen.spst.cz
| SPŠEI Ostrava | iCanteen 2.17.03 | ❌ [zde](https://git.mnau.xyz/hernik/canteenlib/issues/2) | 1.0.1 | https://obedy.spseiostrava.cz:8443/
Pokud chcete přispět s testem, otestujte tuto knihovnu na instanci iCanteen, kde, nejlépe legálně, máte přístup, a nahlašte své poznatky [zde](https://git.mnau.xyz/hernik/canteenlib/issues/new?template=.github%2fISSUE_TEMPLATE%2fhl--en--kompatibility.md)

21
LICENSE
View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 Matyáš Caras
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,48 +1,19 @@
## INFO
__Tato verze knihovny není dále vyvíjena. Vývoj teď probíhá na https://github.com/tpkowastaken/canteenlib__
# Vítejte!
## O knihovně
Experimentální **neoficiální** webscrape knihovna pro komunikaci se systémem [iCanteen](https://www.z-ware.cz/internetove-objednavky). **Knihovna je aktuálně nestabilní! Používejte na vlastní riziko!**
Toto je oficiální, člověkem čitelná, dokumentace pro Dart knihovnu [canteenlib](https://pub.dev/packages/canteenlib)
[![wakatime](https://wakatime.com/badge/user/17178fab-a33c-430f-a764-7b3f26c7b966/project/82873d93-5b79-4978-a5f6-612e21641817.svg)](https://wakatime.com/badge/user/17178fab-a33c-430f-a764-7b3f26c7b966/project/82873d93-5b79-4978-a5f6-612e21641817) [![Pub Version (including pre-releases)](https://img.shields.io/pub/v/canteenlib?color=lightblue&include_prereleases&label=latest%20version)](https://pub.dev/packages/canteenlib)
## Jdeme na to?
## Funkční funkce(*)
- získání jídelníčku na aktuální den (s cenami)
- Objednání / zrušení objednávek
- Nabídnutí jídla do burzy / zrušení
- Získání a objednání cizího jídla z burzy
Quick start vám ukáže, jak rychle začít používat tuto knihovnu
## To do
- Kompatibilita se staršími verzemi iCanteen
{% content-ref url="quick-start.md" %}
[quick-start.md](quick-start.md)
{% endcontent-ref %}
Příklad používání [zde](https://git.mnau.xyz/hernik/canteenlib/src/branch/main/example/canteenlib_example.dart)
## Má to hlubší význam?
*\* Knihovna nemusí fungovat na všech instancích systému iCanteen, proto žádám každého, kdo může a je uživatelem iCanteen, aby otestoval funkčnost této knihovny a případné problémy [nahlásil](https://git.mnau.xyz/hernik/canteenlib/issues/new)*
Většina tříd a metod je popsána v referenci k API.
### Otestované instance iCanteen
[zde](https://git.mnau.xyz/hernik/canteenlib/src/branch/main/COMPATIBILITY.md)
## Licence
```
MIT License
Copyright (c) 2022 Matyáš Caras
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
{% content-ref url="reference/prehled-trid.md" %}
[prehled-trid.md](reference/prehled-trid.md)
{% endcontent-ref %}

9
SUMMARY.md Normal file
View File

@ -0,0 +1,9 @@
# Table of contents
* [Vítejte!](README.md)
* [Quick Start](quick-start.md)
## Reference
* [Příklady](reference/priklady.md)
* [Přehled tříd](reference/prehled-trid.md)

View File

@ -1,30 +0,0 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View File

@ -1,15 +0,0 @@
import 'package:canteenlib/canteenlib.dart';
void main(List<String> args) async {
Canteen c = Canteen(
"https://kantyna.neco.cz"); // vytvořit instanci kantýny, všechna komunikace probíhá skrz ni
try {
await c.login("uzivatel", "heslo"); // přihlásit se
var jidelnicek = await c.jidelnicekDen(den: DateTime.parse("2022-04-04"));
print((await c.ziskejUzivatele()).kredit);
var objednano = await c.objednat(jidelnicek.jidla[0]);
print(objednano.objednano);
} catch (e) {
print("Při získávání informací nastala chyba: $e");
}
}

View File

@ -1,5 +0,0 @@
/// Hlavní knihovna
library canteenlib;
export 'src/canteen.dart';
export 'src/tridy.dart';

View File

@ -1,516 +0,0 @@
import 'package:http/http.dart' as http;
import 'tridy.dart';
/*
MIT License
Copyright (c) 2022 Matyáš Caras and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/// Reprezentuje kantýnu
///
/// **Všechny metody v případě chyby vrací [Future] s chybovou hláškou.**
class Canteen {
/// Adresa kantýny
String url;
/// Sušenky potřebné pro komunikaci
Map<String, String> cookies = {"JSESSIONID": "", "XSRF-TOKEN": ""};
/// Je uživatel přihlášen?
bool prihlasen = false;
Canteen(this.url);
/// Vrátí informace o uživateli ve formě instance [Uzivatel]
Future<Uzivatel> ziskejUzivatele() async {
if (!prihlasen) return Future.error("Uživatel není přihlášen");
var r = await _getRequest("/web/setting");
if (r.contains("přihlášení uživatele")) {
prihlasen = false;
return Future.error("Uživatel není přihlášen");
}
var kreditMatch = double.tryParse(
RegExp(r' +<span id="Kredit" .+?>(.+?)(?=&)')
.firstMatch(r)!
.group(1)!
.replaceAll(",", ".")
.replaceAll(RegExp(r"[^\w.-]"), ""));
var jmenoMatch = RegExp(r'(?<=jméno: <b>).+?(?=<\/b)').firstMatch(r);
var prijmeniMatch = RegExp(r'(?<=příjmení: <b>).+?(?=<\/b)').firstMatch(r);
var kategorieMatch =
RegExp(r'(?<=kategorie: <b>).+?(?=<\/b)').firstMatch(r);
var ucetMatch = RegExp(r'účet pro platby do jídelny:\s*<b>(\d+/\d+)</b>')
.firstMatch(r)
?.group(1)
?.replaceAll(RegExp(r'<\/?b>'), ''); //odstranit html tag <b>
var varMatch =
RegExp(r'(?<=variabilní symbol: <b>).+?(?=<\/b)').firstMatch(r);
var specMatch =
RegExp(r'(?<=specifický symbol: <b>).+?(?=<\/b)').firstMatch(r);
var jmeno = jmenoMatch?.group(0) ?? "";
var prijmeni = prijmeniMatch?.group(0) ?? "";
var kategorie = kategorieMatch?.group(0) ?? "";
var ucet = ucetMatch ?? "";
var varSymbol = varMatch?.group(0) ?? "";
var specSymbol = specMatch?.group(0) ?? "";
var kredit = kreditMatch ?? 0.0;
return Uzivatel(
jmeno: jmeno,
prijmeni: prijmeni,
kategorie: kategorie,
ucetProPlatby: ucet,
varSymbol: varSymbol,
specSymbol: specSymbol,
kredit: kredit);
}
Future<void> _getFirstSession() async {
if (url.endsWith("/")) {
url = url.substring(0, url.length - 1);
} // odstranit lomítko
var res = await http.get(Uri.parse(url));
_parseCookies(res.headers['set-cookie']!);
}
/// Převede cookie řetězec z požadavku do mapy
void _parseCookies(String cookieString) {
Map<String, String> cookies = this.cookies;
var regCookie = RegExp(r'([A-Z\-]+=.+?(?=;))|(remember-me=.+?)(?=;)')
.allMatches(cookieString)
.toList();
for (var cook in regCookie) {
var c = cook.group(0).toString().split("=");
cookies[c[0]] = c[1];
}
}
/// Přihlášení do iCanteen
///
/// Vstup:
///
/// - `user` - uživatelské jméno | [String]
/// - `password` - heslo | [String]
///
/// Výstup:
/// - [bool] ve [Future], v případě přihlášení `true`, v případě špatného hesla `false`
Future<bool> login(String user, String password) async {
if (cookies["JSESSIONID"] == "" || cookies["XSRF-TOKEN"] == "") {
await _getFirstSession();
}
var res =
await http.post(Uri.parse("$url/j_spring_security_check"), headers: {
"Cookie":
"JSESSIONID=${cookies["JSESSIONID"]!}; XSRF-TOKEN=${cookies["XSRF-TOKEN"]!};",
"Content-Type": "application/x-www-form-urlencoded",
}, body: {
"j_username": user,
"j_password": password,
"terminal": "false",
"_csrf": cookies["XSRF-TOKEN"],
"_spring_security_remember_me": "on",
"targetUrl":
"/faces/secured/main.jsp?terminal=false&status=true&printer=&keyboard="
});
if (res.headers['set-cookie']!.contains("remember-me=;")) {
return false; // špatné heslo
}
if (res.statusCode != 302) {
return Future.error("Chyba: ${res.body}");
}
_parseCookies(res.headers['set-cookie']!);
prihlasen = true;
return true;
}
/// Builder pro GET request
Future<String> _getRequest(String path) async {
var r = await http.get(Uri.parse(url + path), headers: {
"Cookie":
"JSESSIONID=${cookies["JSESSIONID"]!}; XSRF-TOKEN=${cookies["XSRF-TOKEN"]!}${cookies.containsKey("remember-me") ? "; ${cookies["remember-me"]!};" : ";"}",
});
if (r.statusCode != 200 ||
r.body.contains("fail") ||
r.body.contains("Chyba")) {
return Future.error("Chyba: ${r.body}");
}
if (r.body.contains("přihlášení uživatele")) {
prihlasen = false;
return Future.error("Uživatel není přihlášen");
}
if (r.headers.containsKey("set-cookie")) {
_parseCookies(r.headers["set-cookie"]!);
}
return r.body;
}
/// Získá jídelníček bez cen
///
/// Výstup:
/// - [List] s [Jidelnicek], který neobsahuje ceny
///
/// __Lze použít bez přihlášení__
Future<List<Jidelnicek>> ziskejJidelnicek() async {
var res = await _getRequest("/");
var reg = RegExp(
r'((?=<div class="jidelnicekDen">).+?(?=<div class="jidelnicekDen">))|((?=<div class="jidelnicekDen">).*<\/span>)',
dotAll: true)
.allMatches(res)
.toList();
List<Jidelnicek> jidelnicek = [];
for (var t in reg) {
// projedeme každý den individuálně
var j = t.group(0).toString(); // převedeme text na něco přehlednějšího
var den = DateTime.parse(RegExp(r'(?<=day-).+?(?=")', dotAll: true)
.firstMatch(j)!
.group(0)
.toString());
var jidlaDenne = RegExp(
r'(?=<div class="container">).+?<\/div>.+?(?=<\/div>)',
dotAll: true)
.allMatches(j)
.toList(); // získáme jednotlivá jídla pro den / VERZE 2.18
if (jidlaDenne.isEmpty) {
jidlaDenne = RegExp(
r'(?=<div style="padding: 2 0 2 20">).+?(?=<\/div>)',
dotAll: true)
.allMatches(j)
.toList(); // získáme jednotlivá jídla pro den / VERZE 2.10
}
List<Jidlo> jidla = [];
for (var jidloNaDen in jidlaDenne) {
// projedeme vsechna jidla
var s = jidloNaDen.group(0)!.replaceAll(
RegExp(
r'[a-zA-ZěščřžýáíéÉÍÁÝŽŘČŠĚŤŇťň.,:] [a-zA-ZěščřžýáíéÉÍÁÝŽŘČŠĚŤŇťň.,:]'),
''); // odstraní dvojté mezery mezi písmeny
var vydejna = RegExp(r'(?<=<span style="color: #1b75bb;">).+?(?=<)')
.firstMatch(s); // název výdejny / verze 2.18
vydejna ??= RegExp(
// TODO: Lepší systém pro podporu různých verzí iCanteen
r'(?<=<span class="smallBoldTitle" style="color: #1b75bb;">).+?(?=<)')
.firstMatch(s); // název výdejny / verze 2.10
var hlavni = RegExp(
r' {20}(([a-zA-ZěščřžýáíéÉÍÁÝŽŘČŠĚŤŇťň.,:\/]+ )+[a-zA-ZěščřžýáíéÉÍÁÝŽŘČŠĚŤŇťň.,:\/]+)',
dotAll: true)
.firstMatch(s)!
.group(1)
.toString(); // Jídlo
jidla.add(Jidlo(
nazev: hlavni,
objednano: false,
varianta: vydejna!.group(0).toString(),
lzeObjednat: false,
den: den,
naBurze: false));
}
jidelnicek.add(Jidelnicek(den, jidla));
}
return jidelnicek;
}
/// Získá jídlo pro daný den
///
/// __Vyžaduje přihlášení pomocí [login]__
///
/// Vstup:
/// - `den` - *volitelné*, určuje pro jaký den chceme získat jídelníček | [DateTime]
///
/// Výstup:
/// - [Jidelnicek] obsahující detaily, které vidí přihlášený uživatel
Future<Jidelnicek> jidelnicekDen({DateTime? den}) async {
if (!prihlasen) {
return Future.error("Uživatel není přihlášen");
}
den ??= DateTime.now();
String res;
try {
res = await _getRequest(
"/faces/secured/main.jsp?day=${den.year}-${(den.month < 10) ? "0${den.month}" : den.month}-${(den.day < 10) ? "0${den.day}" : den.day}&terminal=false&printer=false&keyboard=false");
} catch (e) {
return Future.error(e);
}
var obedDen = DateTime.parse(RegExp(r'(?<=day-).+?(?=")', dotAll: true)
.firstMatch(res)!
.group(0)
.toString());
var jidla = <Jidlo>[];
var jidelnicek = RegExp(
r'(?<=<div class="jidWrapLeft">).+?((fa-clock)|(fa-ban))',
dotAll: true)
.allMatches(res)
.toList();
for (var obed in jidelnicek) {
// formátování do třídy
var o = obed
.group(0)
.toString()
.replaceAll(RegExp(r'( )+|([^>a-z]\n)'), '');
var objednano = o.contains("Máte objednáno");
var lzeObjednat = !(o.contains("nelze zrušit") ||
o.contains("nelze objednat") ||
o.contains("nelze změnit"));
var cenaMatch =
RegExp(r'((?<=Cena objednaného jídla">).+?(?=&))').firstMatch(o);
cenaMatch ??=
RegExp(r'(?<=Cena při objednání jídla:&nbsp;).+?(?=&)').firstMatch(o);
cenaMatch ??=
RegExp(r'(?<=Cena při objednání jídla">).+?(?=&)').firstMatch(o);
var cena =
double.parse(cenaMatch!.group(0).toString().replaceAll(",", "."));
var jidlaProDen =
RegExp(r'<div class="jidWrapCenter.+?>(.+?)(?=<\/div>)', dotAll: true)
.firstMatch(o)!
.group(1)
.toString()
.replaceAll(' ,', ",")
.replaceAll(" <br>", "")
.replaceAll("\n", "");
var alergenyList =
RegExp(r"""<span(?: |\n).+?title="(.+?)".+?>(\d{1,2})""")
.allMatches(jidlaProDen)
.toList();
var alergeny = alergenyList.map<Alergen>((e) {
var jmeno = RegExp(r'<b>(.+?)<\/b>')
.firstMatch(e.group(1).toString())!
.group(1);
var popis =
RegExp(r'<\/b> - (.+)').firstMatch(e.group(1).toString())?.group(1);
var kod = int.parse(e.group(2).toString());
return Alergen(nazev: jmeno!, kod: kod, popis: popis);
}).toList();
var vydejna = RegExp(
r'(?<=<span class="smallBoldTitle button-link-align">).+?(?=<)')
.firstMatch(o)!
.group(0)
.toString();
String? orderUrl;
String? burzaUrl;
if (lzeObjednat) {
// pokud lze objednat, nastavíme adresu pro objednání
var match = RegExp(r"(?<=ajaxOrder\(this, ').+?(?=')").firstMatch(o);
if (match != null) {
orderUrl = match.group(0)!.replaceAll("amp;", "");
}
} else {
// jinak nastavíme URL pro burzu
var match = RegExp(
r"""db\/dbProcessOrder\.jsp.+?type=((plusburza)|(minusburza)|(multiburza)).+?(?=')""")
.firstMatch(o);
if (match != null) {
burzaUrl = match.group(0)!.replaceAll("amp;", "");
}
}
var jidloJmeno = RegExp(r'(.+?)(?=<sub>)')
.firstMatch(jidlaProDen)!
.group(1)
.toString();
jidla.add(
Jidlo(
nazev: jidloJmeno.replaceAll(
r' (?=[^a-zA-ZěščřžýáíéĚŠČŘŽÝÁÍÉŤŇťň])', ''),
objednano: objednano,
varianta: vydejna,
lzeObjednat: lzeObjednat,
cena: cena,
orderUrl: orderUrl,
den: obedDen,
burzaUrl: burzaUrl,
naBurze:
(burzaUrl == null) ? false : burzaUrl.contains("minusburza"),
alergeny: alergeny),
);
// KONEC formátování do třídy
}
return Jidelnicek(obedDen, jidla);
}
/// Objedná vybrané jídlo
///
/// Vstup:
/// - `j` - Jídlo, které chceme objednat | [Jidlo]
///
/// Výstup:
/// - Aktualizovaná instance [Jidlo] tohoto jídla
Future<Jidlo> objednat(Jidlo j) async {
if (!prihlasen) {
return Future.error("Uživatel není přihlášen");
}
if (!j.lzeObjednat || j.orderUrl == null || j.orderUrl!.isEmpty) {
return Future.error(
"Jídlo nelze objednat nebo nemá adresu pro objednání");
}
try {
await _getRequest("/faces/secured/${j.orderUrl!}"); // provést operaci
} catch (e) {
return Future.error(e);
}
var novy = (await jidelnicekDen(den: j.den))
.jidla
.where(
(element) => element.nazev == j.nazev,
)
.toList()[0];
return novy; // vrátit novou instanci
}
/// Uloží vaše jídlo z/do burzy
///
/// Vstup:
/// - `j` - Jídlo, které chceme dát/vzít do/z burzy | [Jidlo]
///
/// Výstup:
/// - Aktualizovaná instance [Jidlo] tohoto jídla NEBO [Future] jako chyba
Future<Jidlo> doBurzy(Jidlo j, {int amount = 1}) async {
if (!prihlasen) {
return Future.error("Uživatel není přihlášen");
}
if (j.burzaUrl == null || j.burzaUrl!.isEmpty) {
return Future.error(
"Jídlo nelze uložit do burzy nebo nemá adresu pro uložení");
}
if (amount < 1 && j.burzaUrl!.endsWith("amount=")) {
return Future.error("Nemůžeš dát do burzy méně než jeden kus");
}
var finalUrl =
(j.burzaUrl!.endsWith("amount=")) ? "${j.burzaUrl}$amount" : j.burzaUrl;
try {
await _getRequest("/faces/secured/$finalUrl"); // provést operaci
} catch (e) {
return Future.error(e);
}
var novy = (await jidelnicekDen(den: j.den))
.jidla
.where(
(element) => element.nazev == j.nazev,
)
.toList()[0];
return novy; // vrátit upravenou instanci
}
/// Získá aktuální jídla v burze
///
/// Výstup:
/// - List instancí [Burza], každá obsahuje informace o jídle v burze
Future<List<Burza>> ziskatBurzu() async {
if (!prihlasen) return Future.error("Uživatel není přihlášen");
List<Burza> burza = [];
String res;
try {
res = await _getRequest("/faces/secured/burza.jsp");
} catch (e) {
return Future.error(e);
}
var dostupnaJidla =
RegExp(r'(?<=<tr class="mouseOutRow">).+?(?=<\/tr>)', dotAll: true)
.allMatches(res); // vyfiltrujeme jednotlivá jídla
if (dostupnaJidla.isNotEmpty) {
for (var burzaMatch in dostupnaJidla) {
var bu = burzaMatch.group(0)!;
var data = RegExp(
r'((?<=<td>).+?(?=<))|(?<=<td align="left">).+?(?=<)|((?<=<td align="right">).+?(?=<))',
dotAll: true)
.allMatches(bu)
.toList();
// Získat datum
var datumRaw = RegExp(r'\d\d\.\d\d\.\d{4}')
.firstMatch(data[1].group(0)!)!
.group(0)!
.split(".");
var datum =
DateTime.parse("${datumRaw[2]}-${datumRaw[1]}-${datumRaw[0]}");
// Získat variantu
var varianta = data[0].group(0)!;
// Získat název jídla
var nazev = data[2].group(0)!.replaceAll(RegExp(r'\n| '), "");
// Získat počet kusů
var pocet = int.parse(data[4].group(0)!.replaceAll(" ks", ""));
var url = RegExp(r"(?<=')db.+?(?=')")
.firstMatch(bu)!
.group(0)!
.replaceAll("&amp;", "&");
var jidlo = Burza(
den: datum,
varianta: varianta,
nazev: nazev,
pocet: pocet,
url: url);
burza.add(jidlo);
}
}
return burza;
}
/// Objedná jídlo z burzy pomocí URL z instance třídy Burza
///
/// Vstup:
/// - `b` - Jídlo __z burzy__, které chceme objednat | [Burza]
///
/// Výstup:
/// - [bool], `true`, pokud bylo jídlo úspěšně objednáno z burzy, jinak `Exception`
Future<bool> objednatZBurzy(Burza b) async {
if (!prihlasen) return Future.error("Uživatel není přihlášen");
try {
await _getRequest("/faces/secured/${b.url!}");
} catch (e) {
return Future.error(e.toString());
}
return true;
}
}

View File

@ -1,126 +0,0 @@
/// Reprezentuje jedno jídlo z jídelníčku
class Jidlo {
/// Název jídla
String nazev;
/// Objednal si uživatel toto jídlo?
bool objednano;
/// Název varianty
String varianta;
/// Cena
double? cena;
///Lze objednat?
bool lzeObjednat;
/// Je jídlo aktuálně na burze?
bool naBurze;
/// Den, který je jídlo vydáváno
DateTime den;
/// Seznam alergenů
///
/// Pokud se žádný nepodařilo najít, vrací prázdný seznam
List<Alergen> alergeny;
/// URL pro požadavek na objednání jídla
final String? orderUrl;
/// URL pro vložení jídla na burzu
final String? burzaUrl;
Jidlo(
{required this.nazev,
required this.objednano,
required this.varianta,
required this.den,
this.alergeny = const [],
this.cena,
required this.lzeObjednat,
this.orderUrl,
this.burzaUrl,
required this.naBurze});
}
/// Popisuje alergen v jídelníčku
class Alergen {
final int kod;
final String nazev;
final String? popis;
const Alergen({required this.nazev, required this.kod, this.popis});
}
/// Reprezentuje cizí jídlo na burze
class Burza {
/// Den, který je jídlo vydáváno
DateTime den;
/// URL pro objednání
final String? url;
/// Název jídla
String nazev;
/// Varianta
String? varianta;
/// Počet kusů tohoto jídla dostupného na burze
int pocet;
Burza(
{required this.den,
required this.url,
required this.nazev,
required this.pocet,
this.varianta});
}
/// Reprezentuje jídelníček pro jeden den
class Jidelnicek {
/// Den, pro který je jídelníček zveřejněn
DateTime den;
/// Seznam jídel
List<Jidlo> jidla;
Jidelnicek(this.den, this.jidla);
}
/// Reprezentuje informace o přihlášeném uživateli
class Uzivatel {
/// Uživatelské jméno
String? uzivatelskeJmeno;
/// Jméno, jak je uvedené v základních údajích o uživateli
String? jmeno;
/// Příjmení, jak je uvedené v základních údajích o uživateli
String? prijmeni;
/// Kategorie uživatele
String? kategorie;
/// Účet jídelny pro zasílání plateb
String? ucetProPlatby;
/// Variabilní symbol
String? varSymbol;
/// Specifický symbol
String? specSymbol;
/// Aktuální stav kreditu
double kredit;
Uzivatel(
{this.uzivatelskeJmeno,
this.jmeno,
this.prijmeni,
this.kategorie,
this.ucetProPlatby,
this.varSymbol,
this.kredit = 0.0,
this.specSymbol});
}

View File

@ -1,15 +0,0 @@
name: canteenlib
description: Library for communication with the czech canteen food ordering system iCanteen
version: 2.0.0
repository: 'https://git.mnau.xyz/hernik/canteenlib'
issue_tracker: 'https://git.mnau.xyz/hernik/canteenlib/issues'
environment:
sdk: '>=2.16.1 <4.0.0'
dev_dependencies:
dotenv: ^4.0.1
lints: ^2.0.0
test: ^1.16.0
dependencies:
http: ^0.13.4

44
quick-start.md Normal file
View File

@ -0,0 +1,44 @@
---
description: Jak začít s používáním API
---
# Quick Start
{% hint style="danger" %}
Využívání balíku je na vlastní nebezpečí, neručíme za škody způsobené používáním!
{% endhint %}
## Instalace balíku
Knihovna je hostována na [pub.dev](https://pub.dev/packages/canteenlib), instalace se provede jednoduše pomocí `dart pub add canteenlib`, pokud používáte Flutter pak `flutter pub add canteenlib`
## Vytvořit instanci
Základem je vytvoření instance třídy [`Canteen`](reference/prehled-trid.md#canteen), která obsahuje všechny metody pro komunikaci s iCanteen. Jediný parametr této třídy je URL k vašemu kýženému iCanteen.
```dart
import 'package:canteenlib/canteenlib.dart';
// ...
Canteen c = Canteen("https://kantyna.neco.cz");
// ...
```
## Přihlášení
Přihlášení za váš účet provedete pomocí metody `login`, parametry jsou uživatelské jméno a heslo.
{% hint style="info" %}
Knihovna používá hlavně [asynchronní funkce](https://dart.dev/codelabs/async-await), které vrací Future, doporučujeme používat uvnitř jiné asynchronní funkce s `await`.
{% endhint %}
```dart
// ...
Canteen c = Canteen("https://kantyna.neco.cz");
var l = await c.login("jmeno","heslo")
```
Metoda vrací `bool` nebo chybu. Pokud se nelze přihlásit pomocí jména nebo hesla, vrací metoda `false`, v případě jiné chyby `Future.error` a při úspěšném přihlášení `true`.
## Dělejte co potřebujete
Nyní byste měli být připravení na to, abyste posílali ostatní požadavky. Prohlédněte si [referenci](reference/prehled-trid.md), [podrobnou dokumentaci](https://pub.dev/documentation/canteenlib/latest/canteenlib/canteenlib-library.html) nebo [příklady](reference/priklady.md) pro pomoc s pokračováním.

129
reference/prehled-trid.md Normal file
View File

@ -0,0 +1,129 @@
# Přehled tříd
Zde jsou zdokumentované všechny třídy, které lze uvnitř knihovny najít.
## Burza
Reprezentuje jedno jídlo na burze, které není vaše, respektive uživatele, který je přihlášený.
### Vlastnosti
#### den
[`DateTime`](https://api.dart.dev/stable/2.17.1/dart-core/DateTime-class.html) - Den, který je jídlo vydáváno
#### nazev
`String` - Název jídla
#### pocet
`int` - Počet kusů tohoto jídla v burze
#### url
`String?` - URL pro objednání jídla
#### varianta
`String?` - Druh varianty
## Canteen
Reprezentuje kantýnu / instanci iCanteen.
Slouží pro uchovávání metod, pomocí kterých se komunikuje s instancí.
### Vlastnosti
#### cookies
[`Map`](https://api.dart.dev/stable/2.17.1/dart-core/Map-class.html)`<String, String>` - Obsahuje všechny sušenky, které jsou vyžadovany pro úspěšnou komunikaci, např. pro identifikaci uživatele
#### prihlasen
`bool` - Slouží k informování, zda-li je uživatel přihlášen
#### url
`String` - URL adresa instance kantýny
### Metody
{% hint style="danger" %}
Všechny metody vrací Future, pro získání hodnoty je nutné použít `.then` nebo `await`.
{% endhint %}
#### doBurzy
Slouží pro uložení jídla uživatele do burzy
##### Parametry
- [`Jidlo`](#jidlo) - Jídlo uživatele, které chce přesunout do burzy
##### Vrací
- [`Jidlo`](#jidlo) - Původní instance upravená o změněné parametry
#### jidelnicekDen
Slouží pro získání jídelníčku pro určitý den
##### Parametry
- `den` - [`DateTime`](https://api.dart.dev/stable/2.17.1/dart-core/DateTime-class.html)`?` *(volitelný)* - určuje pro který den chceme získat jídelníček; není-li zadán, je použito dnešní datum
##### Vrací
- [`Jidelnicek`](#jidelnicek) - Jídelníček pro daný den
#### login
Slouží pro autorizaci a přihlášení uživatele
##### Parametry
- `String` - uživatelské jméno
- `String` - heslo
##### Vrací
- `bool` - `true` v případě přihlášení, jinak `false`
#### objednat
Objedná jídlo zadané v parametru
##### Parametry
- [`Jidlo`](#jidlo) - Jídlo, které chce uživatel objednat
##### Vrací
- [`Jidlo`](#jidlo) - Instance upravená o změněné parametry
#### objednatZBurzy
Objedná jídlo z burzy uvedené v parametru
##### Parametry
- [`Burza`](#burza) - Cizí jídlo z burzy, které chce uživatel objednat
##### Vrací
- `bool` - `true` v případě, že bylo jídlo úspěšně objednáno
#### ziskatBurzu
Získá aktuální jídla v burze, která může uživatel objednat. (iCanteen ve výchozím stavu nezobrazuje jídla v burze pro dny, kdy má uživatel objednáno)
##### Vrací
- [`List`](https://api.dart.dev/stable/2.17.1/dart-core/List-class.html)[`Burza`](#burza) - Seznam jídel v burze
#### ziskejJidelnicek
Získá aktuální holý jídelníček (více dnů), jelikož bere z hlavní stránky, **není nutné přihlášení**
##### Vrací
- [`List`](https://api.dart.dev/stable/2.17.1/dart-core/List-class.html)[`Jidelnicek`](#jidelnicek) - Jídelníčky pro dny, které jsou zobrazené na hlavní stránce
#### ziskejUzivatele
Vrátí údaje o uživateli
##### Vrací
- [`Uzivatel`](#uzivatel) - Instance třídy obsahující všechny údaje, jsou-li vyplněné
## Jidelnicek
Třídá reprezentující jídelníček pro určitý den v týdnu
### Vlastnosti
####
#### den
[`DateTime`](https://api.dart.dev/stable/2.17.1/dart-core/DateTime-class.html) - Den, pro který jídelníček platí
#### jidla
[`List`](https://api.dart.dev/stable/2.17.1/dart-core/List-class.html)[`Jidlo`](#jidlo) - Seznam jídel v tomto jídelníčku
## Jidlo
Reprezentuje jedno určité jídlo v jídelníčku
### Vlastnosti
#### burzaUrl
`String?` - URL pro vložení jídla na burzu, je-li už objednáno
#### cena
`double` - Cena za jídlo
#### den
[`DateTime`](https://api.dart.dev/stable/2.17.1/dart-core/DateTime-class.html) - Den, který je jídlo vydáváno
#### lzeObjednat
`bool` - Udává, zda-li jde jídlo objednat
#### naBurze
`bool` - Udává, zda-li je jídlo aktuálně na burze
#### nazev
`String` - Název jídla
#### objednano
`bool` - Udává, zda-li si uživatel jídlo objednal nebo ne
#### orderUrl
`String?` - URL pro objednání/zrušení objednání jídla
#### varianta
`String` - Název varianty
## Uzivatel
Uchovává informace o přihlášeném uživateli
### Vlastnosti
#### jmeno
`String?` - Jméno, jak je uvedené v základních údajích o uživateli
#### kategorie
`String?` - Kategorie uživatele
#### Kredit
`double` - Aktuální stav kreditu
#### prijmeni
`String?` Příjmení, jak je uvedené v základních údajích o uživateli
#### specSymbol
`String?` - Specifický symbol
#### ucetProPlatby
`String?` - Účet jídelny pro zasílání plateb
#### uzivatelskeJmeno
`String?` - Uživatelské jméno
#### varSymbol
`String?` - Variabilní symbol

2
reference/priklady.md Normal file
View File

@ -0,0 +1,2 @@
# Příklady
TODO

View File

@ -1,31 +0,0 @@
import 'package:canteenlib/canteenlib.dart';
import 'package:test/test.dart';
import 'package:dotenv/dotenv.dart';
void main() {
group('A group of tests', () {
var env = DotEnv(includePlatformEnvironment: true)..load();
Canteen c = Canteen(env["ADDRESS"]!);
test('Log-in test', () {
c.login(env["USER"]!, env["PASS"]!).then((r) => expect(r, true));
});
test('First Test', () {
c.login(env["USER"]!, env["PASS"]!).then((r) {
c.jidelnicekDen().then((t) {
expect(DateTime.now().day, t.den.day);
});
});
});
test('Neprázdný jídelníček', () {
c.login(env["USER"]!, env["PASS"]!).then((r) {
c.jidelnicekDen(den: DateTime.now().add(Duration(days: 5))).then((t) {
print(t.jidla[0].nazev);
expect(t.jidla[0].nazev.isNotEmpty, true);
});
});
});
});
}