Auto stash before merge of "main" and "origin/main"

This commit is contained in:
Matyáš Caras 2022-03-05 16:49:11 +01:00
parent 55d36d9f33
commit 846e377266
11 changed files with 369 additions and 1 deletions

10
.gitignore vendored Normal file
View 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
View 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
View 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) | |

View file

@ -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
View 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

View 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
View file

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

220
lib/src/canteen.dart Normal file
View 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
View 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
View 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
View 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);
});
});
}