Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

5 changed files with 45 additions and 214 deletions

1
.gitignore vendored
View file

@ -1,6 +1,5 @@
/facebook/token /facebook/token
/washinsa/washinsa_data.json /washinsa/washinsa_data.json
/washinsa/tripode_b_data.json
/facebook/facebook_data.json /facebook/facebook_data.json
/dashboard/dashboard_data.json /dashboard/dashboard_data.json
/menu/menu_data.json /menu/menu_data.json

View file

@ -1,88 +1,3 @@
# Serveur pour l'application de l'Amicale (Campus) # Serveur de l'application de l'Amicale
Partie serveur pour [l'application de l'amicale](https://git.etud.insa-toulouse.fr/vergnet/application-amicale), publiée sous licence GPLv3. Partie serveur de l'application pour l'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.

View file

@ -1,21 +1,20 @@
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=4): for post in facebook_scraper.get_posts(page, pages=3):
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(),
@ -28,7 +27,6 @@ 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

View file

@ -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.34 facebook-scraper==0.2.9
fake-useragent==0.1.11 fake-useragent==0.1.11
html2text==2020.1.16 html2text==2020.1.16
idna==2.10 idna==2.10

View file

@ -1,19 +1,15 @@
# 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 july 2021 as of june 2020
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
@ -23,20 +19,11 @@ 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 = "washinsa_data.json"
DUMP_FILE_TRIPODE_B = "tripode_b_data.json" WASHINSA_URL = "https://www.proxiwash.com/weblaverie/component/weblaverie/?view=instancesfiche&format=raw&s=cf4f39"
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):
@ -61,64 +48,19 @@ STATE_CONVERSION_TABLE = {
TIME_RE = re.compile("^\d\d:\d\d$") TIME_RE = re.compile("^\d\d:\d\d$")
def get_json(code: str, file: TextIO): def download_page():
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')
rows = get_rows(soup)
return get_parsed_data(rows)
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
try: try:
with urllib.request.urlopen(url) as response: with urllib.request.urlopen(WASHINSA_URL) as response:
return response.read().decode() return response.read().decode()
except: except:
print("Error processing following url: " + url) print("Error processing following url: " + WASHINSA_URL)
return "" return ""
def get_rows(soup: BeautifulSoup): def get_rows(soup):
""" """
Gets rows corresponding to machines on the page Gets rows corresponding to machines on the page
""" """
@ -136,13 +78,6 @@ def is_machine_dryer(row):
return DRYER_STRING in row.contents[0].text return DRYER_STRING in row.contents[0].text
def get_machine_weight(row):
"""
Find the maximum weight supported by the machine.
"""
return int(re.search("LINGE (.*?) KG", row.contents[0].text).group(1))
def get_machine_number(row): def get_machine_number(row):
""" """
Gets the current machine number. Gets the current machine number.
@ -231,30 +166,17 @@ 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, formatting it in a easy to use object Gets the parsed data from the web page, farmatting 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": machine_number, "number": get_machine_number(row),
"state": state.value, "state": state.value,
"maxWeight": get_machine_weight(row),
"startTime": "", "startTime": "",
"endTime": "", "endTime": "",
"donePercent": "", "donePercent": "",
@ -276,24 +198,21 @@ 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():
dump_data = {} soup = BeautifulSoup(download_page(), 'html.parser')
with open(DUMP_FILE_INSA, 'r+', encoding='utf-8') as f: rows = get_rows(soup)
write_json(get_json("cf4f39", f), f) with open(DUMP_FILE, 'w') as f:
with open(DUMP_FILE_TRIPODE_B, 'r+', encoding='utf-8') as f: json.dump(get_parsed_data(rows), f)
write_json(get_json("b310b7", f), f)
main() main()