Vertaa committeja

...

6 commits

Tekijä SHA1 Viesti Päivämäärä
Arnaud Vergnet
b4c1e0fcaf Fix parser crash if info key does not exist 2021-07-16 15:29:20 +02:00
Arnaud Vergnet
0cf69f9ff9 Get washinsa status message 2021-07-14 22:03:49 +02:00
Arnaud Vergnet
f85335adad Update readme with install instructions 2021-05-09 11:46:35 +02:00
Arnaud Vergnet
4f8c054663 Update fb parser 2021-05-09 11:20:33 +02:00
Arnaud Vergnet
33fd3a4668 Prevent duplicate machines 2021-05-09 11:03:23 +02:00
Arnaud Vergnet
091bed7fa5 Fix typo 2020-09-10 18:52:43 +02:00
4 muutettua tiedostoa jossa 197 lisäystä ja 42 poistoa

Näytä tiedosto

@ -1,3 +1,88 @@
# Serveur de l'application de l'Amicale # Serveur pour l'application de l'Amicale (Campus)
Partie serveur de l'application pour l'amicale, publiée sous licence GPLv3. Partie serveur pour [l'application de l'amicale](https://git.etud.insa-toulouse.fr/vergnet/application-amicale), publiée sous licence GPLv3.
Le serveur est programmé avec python 3.6 en utilisant des [venv](https://docs.python.org/3/tutorial/venv.html).
## Structure
Pour des raisons de compatibilité, 2 versions sont en ligne sur le serveur: une dans `publich_html` et une autre dans `public_html/v2`. La première version est à ignorer et à supprimer dans le futur. La v2 est celle actuellement utilisée.
## Installation
Tout d'abord, clonez ce dépot dans le dossier désiré et déplacez vous dedans.
```shell
git clone https://git.etud.insa-toulouse.fr/vergnet/application-amicale-serveur.git
cd application-amicale-serveur
```
Ensuite, créez le venv:
```shell
python3 -m venv tutorial-env
```
Et enfin, installez les dépendances:
```shell
pip install -r requirements.txt
```
## Mettre à jour les dépendances
Ouvrez le fichier `requirements.txt` et écrivez la nouvelle version de la librairie à utiliser.
Ensuite, chargez le venv dans votre terminal:
```shell
source .venv/bin/activate
```
Cette commande permet d'utiliser le python installé dans le venv au lieu de celui du système.
Il ne reste plus qu'à installer les nouvelles versions référencées dans `requirements.txt`:
```shell
pip install -r requirements.txt
```
## Envoyer les mises à jour sur le serveur
Le serveur est synchronisé avec git, il suffit donc de se connecter sur l'espace web, de se déplacer dans le dossier v2 et de récupérer les derniers changements:
```shell
ssh amicale_app@etud.insa-toulouse.fr
cd public_html/v2
git pull
```
Si vous avez modifié les versions des librairies dans `requirements.txt`, pensez à les mettre à jour sur le serveur avec la commande suivante:
```shell
pip install -r requirements.txt
```
## Mises à jour 'BREAKING'
Si une mise à jour casse la compatibilité avec la version actuelle de l'application, il est nécessaire de garder l'ancienne version du logiciel serveur le temps que tout le monde mette l'application à jour (plusieurs mois).
Pour cela, créez un nouveau dossier pour la nouvelle version dans `public_html`. Par exemple, pour passer de la version 2 (installée dans `public_html/v2`), il faut installer la nouvelle version dans le dossier `public_html/v3`.
Pour cela, il faut tout réinstaller dans ce dossier comme suit:
```shell
ssh amicale_app@etud.insa-toulouse.fr
cd public_html
git clone https://git.etud.insa-toulouse.fr/vergnet/application-amicale-serveur.git v<NUMERO_DE_VERSION>
cd v<NUMERO_DE_VERSION>
```
Ensuite, créez le venv:
```shell
python3 -m venv tutorial-env
```
Et enfin, installez les dépendances:
```shell
pip install -r requirements.txt
```
Pensez ensuite à rediriger l'application vers cette nouvelle version.

Näytä tiedosto

@ -1,20 +1,21 @@
import json import json
import facebook_scraper import facebook_scraper
import enum
FILE = 'facebook_data.json' FILE = 'facebook_data.json'
PAGES = ["amicale.deseleves", "campus.insat"] PAGES = ["amicale.deseleves", "campus.insat"]
def scrape_data(page): def scrape_data(page):
post_list = [] post_list = []
for post in facebook_scraper.get_posts(page, pages=3): for post in facebook_scraper.get_posts(page, pages=4):
print(post) print(post)
cleaned_post = { cleaned_post = {
"id": post["post_id"], "id": post["post_id"],
"message": post["post_text"], "message": post["post_text"],
"url": post["post_url"], "url": post["post_url"],
"image": post["image"], "image": post["image"],
"images": post["images"],
"video": post["video"], "video": post["video"],
"link": post["link"], "link": post["link"],
"time": post["time"].timestamp(), "time": post["time"].timestamp(),
@ -27,6 +28,7 @@ def scrape_data(page):
def get_all_data(): def get_all_data():
data = {} data = {}
for page in PAGES: for page in PAGES:
print(" -> " + page)
data[page] = scrape_data(page) data[page] = scrape_data(page)
return data return data

Näytä tiedosto

@ -4,7 +4,7 @@ bs4==0.0.1
certifi==2020.6.20 certifi==2020.6.20
chardet==3.0.4 chardet==3.0.4
cssselect==1.1.0 cssselect==1.1.0
facebook-scraper==0.2.9 facebook-scraper==0.2.34
fake-useragent==0.1.11 fake-useragent==0.1.11
html2text==2020.1.16 html2text==2020.1.16
idna==2.10 idna==2.10

Näytä tiedosto

@ -1,15 +1,19 @@
# Parser made with BeautifulSoup4 # Parser made with BeautifulSoup4
# https://www.crummy.com/software/BeautifulSoup/bs4/doc # https://www.crummy.com/software/BeautifulSoup/bs4/doc
from json import JSONDecodeError
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import urllib.request import urllib.request
from enum import Enum from enum import Enum
import re import re
import json import json
from datetime import datetime
from typing.io import TextIO
''' '''
PAGE STRUCTURE PAGE STRUCTURE
as of june 2020 as of july 2021
A table with a row (tr html tag) for each machine A table with a row (tr html tag) for each machine
Each machine row is composed of 6 columns Each machine row is composed of 6 columns
@ -19,12 +23,20 @@ Each machine row is composed of 6 columns
- 4 - Program (Name of the program or empty) - 4 - Program (Name of the program or empty)
- 5 - Start time (The start time in format HH:MM or empty) - 5 - Start time (The start time in format HH:MM or empty)
- 6 - End time (The end time in format HH:MM or empty) - 6 - End time (The end time in format HH:MM or empty)
Custom message (errors displayed on the website)
Must use the non-raw url to see it.
In the <font> under the <div> of id msg-permanent
example message: Perturbations operateur, laverie non connectee a internet depuis le 12/07/2021 a 19h45
''' '''
DUMP_FILE_INSA = "washinsa_data.json" DUMP_FILE_INSA = "washinsa_data.json"
DUMP_FILE_TRIPODE_B = "tripode_b_data.json" DUMP_FILE_TRIPODE_B = "tripode_b_data.json"
WASHINSA_URL = "https://www.proxiwash.com/weblaverie/component/weblaverie/?view=instancesfiche&format=raw&s=" WASHINSA_RAW_URL = "https://www.proxiwash.com/weblaverie/component/weblaverie/?view=instancesfiche&format=raw&s="
WASHINSA_URL = "https://www.proxiwash.com/weblaverie/ma-laverie-2?s="
DRYER_STRING = "SECHE LINGE" DRYER_STRING = "SECHE LINGE"
# 10 min
CUSTOM_MESSAGE_INTERVAL = 10 * 60 * 1000
class State(Enum): class State(Enum):
@ -49,17 +61,55 @@ STATE_CONVERSION_TABLE = {
TIME_RE = re.compile("^\d\d:\d\d$") TIME_RE = re.compile("^\d\d:\d\d$")
def get_json(code): def get_json(code: str, file: TextIO):
file_json = {
"info": {},
"dryers": [],
"washers": []
}
try:
file_json = json.load(file)
except JSONDecodeError as e:
print("Error reading file " + file.name)
print(e)
if not ("info" in file_json):
file_json["info"] = {}
info = file_json["info"]
if not ("last_checked" in info) or info[
"last_checked"] < datetime.now().timestamp() * 1000 - CUSTOM_MESSAGE_INTERVAL:
print("Updating proxiwash message")
info["message"] = get_message(code)
info["last_checked"] = datetime.now().timestamp() * 1000
parsed_data = get_machines(code)
file_json["dryers"] = parsed_data["dryers"]
file_json["washers"] = parsed_data["washers"]
return file_json
def get_machines(code: str):
soup = BeautifulSoup(download_page(code), 'html.parser') soup = BeautifulSoup(download_page(code), 'html.parser')
rows = get_rows(soup) rows = get_rows(soup)
return get_parsed_data(rows) return get_parsed_data(rows)
def download_page(code): def get_message(code: str):
soup = BeautifulSoup(download_page(code, False), 'html.parser')
msg = soup.find(id="msg-permanent")
if msg:
return soup.find(id="msg-permanent").font.string
return None
def download_page(code: str, raw=True):
""" """
Downloads the page from proxiwash website Downloads the page from proxiwash website
""" """
url = WASHINSA_RAW_URL + code
if not raw:
url = WASHINSA_URL + code url = WASHINSA_URL + code
try: try:
with urllib.request.urlopen(url) as response: with urllib.request.urlopen(url) as response:
return response.read().decode() return response.read().decode()
@ -68,7 +118,7 @@ def download_page(code):
return "" return ""
def get_rows(soup): def get_rows(soup: BeautifulSoup):
""" """
Gets rows corresponding to machines on the page Gets rows corresponding to machines on the page
""" """
@ -181,18 +231,30 @@ def get_machine_remaining_time(row):
return time return time
def is_machine_parsed(dryers, washers, number: int):
for m in dryers:
if m["number"] == number:
return True
for m in washers:
if m["number"] == number:
return True
return False
def get_parsed_data(rows): def get_parsed_data(rows):
""" """
Gets the parsed data from the web page, farmatting it in a easy to use object Gets the parsed data from the web page, formatting it in a easy to use object
""" """
dryers = [] dryers = []
washers = [] washers = []
for row in rows: for row in rows:
machine_number = get_machine_number(row)
if not is_machine_parsed(dryers, washers, machine_number):
state = get_machine_state(row) state = get_machine_state(row)
machine = { machine = {
"number": get_machine_number(row), "number": machine_number,
"state": state.value, "state": state.value,
"maxWeight ": get_machine_weight(row), "maxWeight": get_machine_weight(row),
"startTime": "", "startTime": "",
"endTime": "", "endTime": "",
"donePercent": "", "donePercent": "",
@ -214,18 +276,24 @@ def get_parsed_data(rows):
dryers.append(machine) dryers.append(machine)
else: else:
washers.append(machine) washers.append(machine)
return { return {
"dryers": dryers, "dryers": dryers,
"washers": washers "washers": washers
} }
def write_json(data, f: TextIO):
f.seek(0)
f.truncate(0)
json.dump(data, f)
def main(): def main():
with open(DUMP_FILE_INSA, 'w') as f: dump_data = {}
json.dump(get_json("cf4f39"), f) with open(DUMP_FILE_INSA, 'r+', encoding='utf-8') as f:
with open(DUMP_FILE_TRIPODE_B, 'w') as f: write_json(get_json("cf4f39", f), f)
json.dump(get_json("b310b7"), f) with open(DUMP_FILE_TRIPODE_B, 'r+', encoding='utf-8') as f:
write_json(get_json("b310b7", f), f)
main() main()