Auto stash before merge of "main" and "origin/main"
This commit is contained in:
parent
55d36d9f33
commit
846e377266
11 changed files with 369 additions and 1 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
# 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
|
4
CHANGELOG.md
Normal file
4
CHANGELOG.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
## 0.1.0-alpha
|
||||
|
||||
- První verze, funkčnost omezená
|
||||
- Metody pro zobrazení jídelníčku
|
12
COMPATIBILITY.md
Normal file
12
COMPATIBILITY.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
# 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.
|
||||
|
||||
- ❌ - 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 |
|
||||
|:--------------:|------------------|---------|----------------|
|
||||
| SŠTE Brno | iCanteen 2.18.19 | ❓ | 0.1.0-alpha |
|
||||
| SPŠ Třebíč | iCanteen 2.10.25 | ❓ | 0.1.0-alpha |
|
||||
| Výsledky testů | nahlašujte | [zde](https://github.com/hernikplays/canteenlib/issues/new) | |
|
11
README.md
11
README.md
|
@ -1 +1,10 @@
|
|||
canteenlib
|
||||
## O knihovně
|
||||
Experimentální **neoficiální** webscrape knihovna pro komunikaci se systémem [iCanteen](https://www.z-ware.cz/internetove-objednavky)
|
||||
|
||||
## Funkční funkce(*)
|
||||
- získání jídelníčku na aktuální den (s cenami)
|
||||
|
||||
*\* 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://github.com/hernikplays/canteenlib/issues)*
|
||||
|
||||
### Otestované instance iCanteen
|
||||
[zde]()
|
30
analysis_options.yaml
Normal file
30
analysis_options.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
# 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
|
10
example/canteenlib_example.dart
Normal file
10
example/canteenlib_example.dart
Normal file
|
@ -0,0 +1,10 @@
|
|||
import 'package:canteenlib/canteenlib.dart';
|
||||
|
||||
void main() {
|
||||
var canteen = Canteen("http://icanteen.vasedomena.neco");
|
||||
canteen.login("user", "password").then((_) {
|
||||
canteen.jidelnicekDen().then((jidelnicek) {
|
||||
print(jidelnicek.jidla[0].hlavni);
|
||||
});
|
||||
});
|
||||
}
|
5
lib/canteenlib.dart
Normal file
5
lib/canteenlib.dart
Normal file
|
@ -0,0 +1,5 @@
|
|||
/// Hlavní knihovna
|
||||
library canteenlib;
|
||||
|
||||
export 'src/canteen.dart';
|
||||
export 'src/jidlo.dart';
|
220
lib/src/canteen.dart
Normal file
220
lib/src/canteen.dart
Normal file
|
@ -0,0 +1,220 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'jidlo.dart';
|
||||
|
||||
/*
|
||||
(C) 2022 Matyáš Caras and contributors
|
||||
This library is released under the GNU LGPLv3 license.
|
||||
If you have not received the license with this library, you can view it here http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
class Canteen {
|
||||
final String url;
|
||||
Map<String, String> cookies = {"JSESSIONID": "", "XSRF-TOKEN": ""};
|
||||
Canteen(this.url);
|
||||
|
||||
Future<void> getFirstSession() async {
|
||||
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
|
||||
///
|
||||
/// `user` - uživatelské jméno
|
||||
/// `password` - heslo
|
||||
///
|
||||
/// TODO: Házet chyby
|
||||
Future<void> 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="
|
||||
});
|
||||
_parseCookies(res.headers['set-cookie']!);
|
||||
if (res.statusCode != 302) {
|
||||
print(res.body);
|
||||
print("ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.headers.containsKey("set-cookie")) {
|
||||
_parseCookies(r.headers["set-cookie"]!);
|
||||
}
|
||||
return r.body;
|
||||
}
|
||||
|
||||
/// Získá jídelníček bez cen
|
||||
/// **nevrací** ceny, ale umožňuje získat jídelníček 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() /*.replaceAll(RegExp(r'( )+|([^>a-z]\n)'),
|
||||
'')*/
|
||||
; // 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 cislo = RegExp(r'(?<=<span style="color: #1b75bb;">).+?(?=<)')
|
||||
.firstMatch(s); // název výdejny / verze 2.18
|
||||
cislo ??= RegExp(
|
||||
r'(?<=<span class="smallBoldTitle" style="color: #1b75bb;">).+?(?=<)')
|
||||
.firstMatch(s); // název výdejny / verze 2.10
|
||||
File("dva.txt").writeAsStringSync(s);
|
||||
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,
|
||||
cislo: cislo!.group(0).toString(),
|
||||
lzeObjednat: false));
|
||||
}
|
||||
jidelnicek.add(Jidelnicek(den, jidla));
|
||||
}
|
||||
return jidelnicek;
|
||||
}
|
||||
|
||||
/// Získá jídlo pro daný den
|
||||
/// Vyžaduje přihlášení pomocí [login]
|
||||
Future<Jidelnicek> jidelnicekDen() async {
|
||||
var res = await _getRequest(
|
||||
"/faces/secured/main.jsp?terminal=false&android=false&keyboard=false&printer=false");
|
||||
var den = DateTime.parse(RegExp(r'(?<=day-).+?(?=")', dotAll: true)
|
||||
.firstMatch(res!)!
|
||||
.group(0)
|
||||
.toString());
|
||||
var jidla = <Jidlo>[];
|
||||
var jidelnicek =
|
||||
RegExp(r'(?<=<div class="jidWrapLeft">).+?(?=<br>)', 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"));
|
||||
var cenaMatch =
|
||||
RegExp(r'(?<=Cena objednaného jídla">).+?(?=&)').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'(?<=Polévka: ).+')
|
||||
.firstMatch(o)!
|
||||
.group(0)
|
||||
.toString()
|
||||
.split(" / ");
|
||||
var vydejna = RegExp(
|
||||
r'(?<=<span class="smallBoldTitle" style="color: #1b75bb;">).+?(?=<)')
|
||||
.firstMatch(o)!
|
||||
.group(0)
|
||||
.toString();
|
||||
String? orderUrl;
|
||||
if (lzeObjednat) {
|
||||
// pokud lze objednat, nastavíme adresu pro objednání
|
||||
orderUrl = RegExp(r"(?<=ajaxOrder\(this, ').+?(?=')")
|
||||
.firstMatch(o)!
|
||||
.group(0)!
|
||||
.replaceAll("amp;", "");
|
||||
}
|
||||
jidla.add(Jidlo(
|
||||
nazev: jidlaProDen[1]
|
||||
.replaceAll(r' (?=[^a-zA-ZěščřžýáíéĚŠČŘŽÝÁÍÉŤŇťň])', ''),
|
||||
objednano: objednano,
|
||||
cislo: vydejna,
|
||||
lzeObjednat: lzeObjednat,
|
||||
cena: cena,
|
||||
orderUrl: orderUrl));
|
||||
// KONEC formátování do třídy
|
||||
}
|
||||
|
||||
return Jidelnicek(den, jidla);
|
||||
}
|
||||
|
||||
Future<bool> objednat(Jidlo j) async {
|
||||
if (!j.lzeObjednat || j.orderUrl == null || j.orderUrl!.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
var res = await _getRequest(j.orderUrl!);
|
||||
print(res);
|
||||
return true;
|
||||
}
|
||||
}
|
35
lib/src/jidlo.dart
Normal file
35
lib/src/jidlo.dart
Normal file
|
@ -0,0 +1,35 @@
|
|||
/// 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 výdejny
|
||||
String cislo;
|
||||
|
||||
/// Cena
|
||||
double? cena;
|
||||
|
||||
///Lze objednat?
|
||||
bool lzeObjednat;
|
||||
final String? orderUrl;
|
||||
Jidlo(
|
||||
{required this.nazev,
|
||||
required this.objednano,
|
||||
required this.cislo,
|
||||
this.cena,
|
||||
required this.lzeObjednat,
|
||||
this.orderUrl});
|
||||
}
|
||||
|
||||
/// Reprezentuje jídelníček pro jeden dan
|
||||
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);
|
||||
}
|
17
pubspec.yaml
Normal file
17
pubspec.yaml
Normal file
|
@ -0,0 +1,17 @@
|
|||
name: canteenlib
|
||||
description: Knihovna pro komunikaci se stravovacím systémem iCanteen
|
||||
version: 0.1.0-alpha
|
||||
# homepage: https://www.example.com
|
||||
|
||||
environment:
|
||||
sdk: '>=2.16.1 <3.0.0'
|
||||
|
||||
|
||||
# dependencies:
|
||||
# path: ^1.8.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^1.0.0
|
||||
test: ^1.16.0
|
||||
dependencies:
|
||||
http: ^0.13.4
|
16
test/canteenlib_test.dart
Normal file
16
test/canteenlib_test.dart
Normal file
|
@ -0,0 +1,16 @@
|
|||
import 'package:canteenlib/canteenlib.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('A group of tests', () {
|
||||
final awesome = Awesome();
|
||||
|
||||
setUp(() {
|
||||
// Additional setup goes here.
|
||||
});
|
||||
|
||||
test('First Test', () {
|
||||
expect(awesome.isAwesome, isTrue);
|
||||
});
|
||||
});
|
||||
}
|
Reference in a new issue