Commit d85bfe58 authored by Florent Chehab's avatar Florent Chehab

Merge branch 'cleaning_for_alpha' into 'master'

Cleaning for alpha

See merge request chehabfl/outgoing_rex!39
parents fc561ea9 40532924
...@@ -17,7 +17,7 @@ build_frontend: generate_frontend_files ...@@ -17,7 +17,7 @@ build_frontend: generate_frontend_files
npm run build npm run build
test_backend: generate_backend test_backend: generate_backend
pytest pytest general/ frontend/ backend/
test_backend_server: test_backend_server:
pytest -n 4 general/ frontend/ backend/ --cov-report html pytest -n 4 general/ frontend/ backend/ --cov-report html
......
from .loading_scripts import LoadGroups
from .loading_scripts import LoadAdminUser from .loading_scripts import LoadAdminUser
from .loading_scripts import LoadCurrencies from .loading_scripts import LoadCurrencies
from .loading_scripts import LoadCountries from .loading_scripts import LoadCountries
...@@ -9,6 +10,7 @@ import reversion ...@@ -9,6 +10,7 @@ import reversion
def load_all(): def load_all():
with reversion.create_revision(): with reversion.create_revision():
LoadGroups()
admin = LoadAdminUser().get() admin = LoadAdminUser().get()
LoadCurrencies(admin).load() LoadCurrencies(admin).load()
LoadCountries(admin).load() LoadCountries(admin).load()
......
from .loadGroups import LoadGroups # noqa: F401
from .loadAdminUser import LoadAdminUser # noqa: F401 from .loadAdminUser import LoadAdminUser # noqa: F401
from .loadCountries import LoadCountries # noqa: F401 from .loadCountries import LoadCountries # noqa: F401
from .loadUniversities import LoadUniversities # noqa: F401 from .loadUniversities import LoadUniversities # noqa: F401
......
from django.contrib.auth.models import Group
class LoadGroups(object):
def __init__(self):
Group.objects.get_or_create(name='Moderators')
Group.objects.get_or_create(name='DRI')
...@@ -3,7 +3,8 @@ from backend.models.university import University ...@@ -3,7 +3,8 @@ from backend.models.university import University
from backend.models.university import UniversityDri from backend.models.university import UniversityDri
from backend.models.university import UniversityInfo from backend.models.university import UniversityInfo
from backend.models.university import UniversitySemestersDates from backend.models.university import UniversitySemestersDates
from backend.models.university import UniversityScholarship from backend.models.country import CountryScholarship
from backend.models.country import Country
from backend.models.university import UniversityTaggedItem from backend.models.university import UniversityTaggedItem
from backend.models.currency import Currency from backend.models.currency import Currency
...@@ -21,9 +22,13 @@ class LoadUniversityEx(LoadGeneric): ...@@ -21,9 +22,13 @@ class LoadUniversityEx(LoadGeneric):
EPFL = University.objects.get(acronym='EPFL') EPFL = University.objects.get(acronym='EPFL')
CHF = Currency.objects.get(pk='CHF') CHF = Currency.objects.get(pk='CHF')
ACCOMMODATION_TAG = Tag.objects.get(name='accommodation') ACCOMMODATION_TAG = Tag.objects.get(name='accommodation')
SWITZERLAND = Country.objects.get(pk="CH")
univ_dri_1 = UniversityDri( univ_dri_1 = UniversityDri(
comment="Attention les cours de master sont 100% en anglais") title="Cours en anglais",
importance_level='+',
comment="Les cours de master en computer science sont 100% en anglais",
)
univ_dri_1.save() univ_dri_1.save()
univ_dri_1.universities.add(EPFL) univ_dri_1.universities.add(EPFL)
self.add_info_and_save(univ_dri_1, self.admin) self.add_info_and_save(univ_dri_1, self.admin)
...@@ -37,19 +42,24 @@ class LoadUniversityEx(LoadGeneric): ...@@ -37,19 +42,24 @@ class LoadUniversityEx(LoadGeneric):
university=EPFL) university=EPFL)
usd.autumn_begin = datetime.strptime("17/09/2018", '%d/%m/%Y') usd.autumn_begin = datetime.strptime("17/09/2018", '%d/%m/%Y')
usd.autumn_end = datetime.strptime("29/01/2019", '%d/%m/%Y') usd.autumn_end = datetime.strptime("29/01/2019", '%d/%m/%Y')
usd.useful_links = [
{"url": "https://memento.epfl.ch/academic-calendar",
"description": "Site de l'EPFL"}
]
self.add_info_and_save(usd, self.admin) self.add_info_and_save(usd, self.admin)
univ_scholarship_1 = UniversityScholarship( country_scholarship = CountryScholarship(
type="Swiss European...", title="Swiss European Mobility Programme",
type="Bourse du gouvernement suisse",
currency=CHF, currency=CHF,
frequency='s', frequency='s',
amount_min=2200, amount_min=2200,
amount_max=2200, amount_max=2200,
comment="Il n'y a rien à faire pour l'obtenir" comment="Bourse attribuée de manière automatique."
) )
univ_scholarship_1.save() country_scholarship.save()
univ_scholarship_1.universities.add(EPFL) country_scholarship.countries.add(SWITZERLAND)
self.add_info_and_save(univ_scholarship_1, self.admin) self.add_info_and_save(country_scholarship, self.admin)
univ_tag_1 = UniversityTaggedItem( univ_tag_1 = UniversityTaggedItem(
university=EPFL, university=EPFL,
......
...@@ -10,13 +10,19 @@ L'application est « agnostique » au type de base de donnée employée. Alors q ...@@ -10,13 +10,19 @@ L'application est « agnostique » au type de base de donnée employée. Alors q
aujourd'hui stockés sous forme de texte car aucune requête n'aurait besoin de les parcourir ; ainsi, seul des champs par défaut de django sont employés. aujourd'hui stockés sous forme de texte car aucune requête n'aurait besoin de les parcourir ; ainsi, seul des champs par défaut de django sont employés.
#### SQlite #### SQlite
Pour utiliser SQlite comme SGBD, il suffit de commenter le bloc qui concerne Postgresql dans le fichier de configuration visible ici : `./general/settings.py` : Pour utiliser SQlite comme SGBD, il suffit de commenter le bloc qui concerne Postgresql dans le fichier de configuration visible ici : `./general/settings/development.py` :
```python ```python
## POSTGRESQL DATABASES = {
# If bellow fails, your need to add DATABASE_URL and ENV to your virutalenv 'default': {
locals()['DATABASES']['default'] = dj_database_url.config( 'ENGINE': 'django.db.backends.postgresql_psycopg2',
conn_max_age=django_heroku.MAX_CONN_AGE, ssl_require=not dev_env) 'NAME': 'outgoing',
'USER': 'outgoing',
'PASSWORD': 'outgoing',
'HOST': '127.0.0.1',
'PORT': '5432',
}
}
``` ```
et de décommenter celui-ci : et de décommenter celui-ci :
```python ```python
...@@ -60,15 +66,6 @@ host all all 127.0.0.1/32 trust ...@@ -60,15 +66,6 @@ host all all 127.0.0.1/32 trust
Pour un fonctionnement simplifié avec ce projet il faut procéder aux changement suivants. Pour un fonctionnement simplifié avec ce projet il faut procéder aux changement suivants.
_Petite commande à faire au début : nous allons avoir besoin de votre nom d'utilisateur._
```bash
whoami
```
C'est par le résultat de cette commande qu'il faudra que vous remplaciez chaque `<login>` dans les autres commandes.
Changer d'utilisateur pour l'utilisateur classique de `postgresql` :
```bash ```bash
su - postgres su - postgres
``` ```
...@@ -84,19 +81,20 @@ Si vous ne voyez pas `postgres=#` c'est qu'il y a un problème, vérifier votre ...@@ -84,19 +81,20 @@ Si vous ne voyez pas `postgres=#` c'est qu'il y a un problème, vérifier votre
Créer un utilisateur correspondant à votre utilisateur/login classique dans postgre : Créer un utilisateur correspondant à votre utilisateur/login classique dans postgre :
```sql ```sql
CREATE USER <login>; CREATE USER outgoing;
ALTER USER outgoing WITH PASSWORD 'outgoing';
``` ```
Donner lui le droit de créer des bases de données (utile lors des tests en local) : Donner lui le droit de créer des bases de données (utile lors des tests en local) :
```sql ```sql
ALTER USER <login> CREATEDB; ALTER USER outgoing CREATEDB;
``` ```
Créer une base de données qui lui sera associée : Créer une base de données qui lui sera associée :
```sql ```sql
CREATE DATABASE <login>; CREATE DATABASE outgoing;
GRANT ALL on DATABASE <login> to <login>; GRANT ALL on DATABASE outgoing to outgoing;
``` ```
Sortez de `psql` (`\q`) et retrouver votre utilisateur normal (`CRTL` + `D`). Sortez de `psql` (`\q`) et retrouver votre utilisateur normal (`CRTL` + `D`).
...@@ -117,37 +115,19 @@ Afin d'avoir des environnements reproductibles il est **fortement** suggéré d' ...@@ -117,37 +115,19 @@ Afin d'avoir des environnements reproductibles il est **fortement** suggéré d'
(Ce projet est développé sur `python-3.6`) (Ce projet est développé sur `python-3.6`)
```bash ```bash
python3.6 -m venv <chemin-du-venv> python3.6 -m venv ./env
``` ```
_(chemin = "chemin" + "nom-du-venv")_ Nous vous suggérons de prendre `<chemin-du-venv> = ./env`.
Cet environnement virtuel doit être **systématiquement** activé lorsque vous travaillez sur ce projet : Cet environnement virtuel doit être **systématiquement** activé lorsque vous travaillez sur ce projet :
```bash ```bash
source <chemin-du-venv>/bin/activate source ./env/bin/activate
``` ```
Les lignes de votre console doivent alors commencez par `(<nom-du-venv>)`. Les lignes de votre console doivent alors commencez par `(<nom-du-venv>)`.
_Pour le désactiver, faîtes : `deactivate`_. _Pour le désactiver, faîtes : `deactivate`_.
!> Il est essentiel à ce stade de modifier votre environnement virtuel pour ajouter des variables d'environnement spécifiques, **pour que ce projet fonctionne correctement**.
Éditer le fichier `<chemin-du-venv>/bin/activate` :
- À la fin de la fonction `deactivate ()`, avant le `}` de fin, rajouter les deux linges :
```bash
unset ENV
unset DATABASE_URL
```
- À la fin du fichier, ajouter les lignes :
```bash
export DATABASE_URL=postgres://$(whoami)@127.0.0.1:5432/$(whoami)
export ENV=development
```
Cela permet d'ajouter et d'enlever automatiquement des variables d'environnement **essentielles** au fonctionnement du projet.
### git ### git
Il ne vous reste plus qu'à cloner le projet : Il ne vous reste plus qu'à cloner le projet :
...@@ -190,11 +170,11 @@ _Collectez_ les éléments statistiques : ...@@ -190,11 +170,11 @@ _Collectez_ les éléments statistiques :
_Checkez_ le système : _Checkez_ le système :
```nash ```nash
./manage.py check make check_backend
``` ```
```bash ```bash
./manage.py test make test_backend
``` ```
Tout ce qu'il y a jusqu'ici **doit** fonctionner. :smile: Tout ce qu'il y a jusqu'ici **doit** fonctionner. :smile:
...@@ -211,4 +191,4 @@ Si vous n'avez jamais travailler avec Django, un tutoriel [s'impose](https://tut ...@@ -211,4 +191,4 @@ Si vous n'avez jamais travailler avec Django, un tutoriel [s'impose](https://tut
## Déploiement _externe_ ## Déploiement _externe_
À ce jour le déploiement externe est réalisé sur la plateforme proposée par [Heroku](https://www.heroku.com) à l'adresse : [http://heroku-badge.herokuapp.com/](http://heroku-badge.herokuapp.com/). À ce jour, le déploiement est réalisé sur un petit vps [outgoing-utc.floflo.ch](outgoing-utc.floflo.ch)
\ No newline at end of file \ No newline at end of file
import React from 'react'; import React from 'react';
import MyComponent from '../MyComponent' import MyComponent from '../MyComponent'
import {connect} from "react-redux"; import { connect } from "react-redux";
import { Map, TileLayer, LayersControl, LayerGroup } from 'react-leaflet'; import { Map, TileLayer, LayersControl, LayerGroup } from 'react-leaflet';
import UnivMarkers from './UnivMakers'; import UnivMarkers from './UnivMakers';
...@@ -8,107 +8,124 @@ import UnivMarkers from './UnivMakers'; ...@@ -8,107 +8,124 @@ import UnivMarkers from './UnivMakers';
import { saveMainMapPosition } from '../../actions/map' import { saveMainMapPosition } from '../../actions/map'
class UnivMap extends MyComponent { class UnivMap extends MyComponent {
constructor() { constructor() {
super(); super();
this.state = { this.state = {
leaflet_instance: null leaflet_instance: null,
}; height: 800,
} };
}
componentWillUnmount() {
let l = this.state.leaflet_instance;
if (l) {
let selected_layer = "";
if (this.state.selected_layer) {
selected_layer = this.state.selected_layer;
} else {
selected_layer = this.props.map.selected_layer;
}
let center = [l.getCenter().lat, l.getCenter().lng];
this.props.saveMainMap({
zoom: l.getZoom(),
center,
selected_layer
})
}
}
saveLeafletInstance = (l) => {
this.setState(Object.assign({},this.state, {
leaflet_instance: l,
}))
}
saveSelectedLayer = (e) => { updateDimensions() {
this.setState(Object.assign({},this.state, { try {
selected_layer: e.name, const height = window.innerHeight - document.getElementById('MySuperMap').getBoundingClientRect().y;
})) this.setState({ height: Math.round(0.9 * height) })
} }
catch (err) { }
myRender() { }
let stamen_name = "Stamen Watercolor";
let osm_fr_name = "OpenStreetMap France"; componentWillMount() {
let esri_name = "Esri WorldImagery"; this.updateDimensions()
}
return (
<Map center={this.props.map.center} zoom={this.props.map.zoom} style={{ height: "800px" }} whenReady={(e) => this.saveLeafletInstance(e.target)} onBaselayerchange={(e) => this.saveSelectedLayer(e)}> myComponentDidMount() {
<LayersControl position="topright"> window.addEventListener("resize", this.updateDimensions.bind(this))
<LayersControl.BaseLayer name={osm_fr_name} checked={this.props.map.selected_layer == osm_fr_name}> }
<TileLayer
attribution='&copy; Openstreetmap France | &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' componentWillUnmount() {
url="https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png" let l = this.state.leaflet_instance;
maxZoom={20} if (l) {
/> let selected_layer = "";
</LayersControl.BaseLayer> if (this.state.selected_layer) {
selected_layer = this.state.selected_layer;
<LayersControl.BaseLayer name={stamen_name} checked={this.props.map.selected_layer == stamen_name}> } else {
<LayerGroup> selected_layer = this.props.map.selected_layer;
<TileLayer }
attribution='Map tiles by <a href="http://stamen.com">Stamen Design</a>, <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> &mdash; Map data &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
url="https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png" let center = [l.getCenter().lat, l.getCenter().lng];
minZoom={1} this.props.saveMainMap({
maxZoom={18} zoom: l.getZoom(),
subdomains='abcd' center,
/> selected_layer
<TileLayer })
attribution='Map tiles by <a href="http://stamen.com">Stamen Design</a>, <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> &mdash; Map data &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
url="https://stamen-tiles-{s}.a.ssl.fastly.net/toner-labels/{z}/{x}/{y}.png"
minZoom={1}
subdomains='abcd'
maxZoom={18}
/>
</LayerGroup>
</LayersControl.BaseLayer>
<LayersControl.BaseLayer name={esri_name} checked={this.props.map.selected_layer == esri_name}>
<TileLayer
attribution="Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
/>
</LayersControl.BaseLayer>
</LayersControl>
<UnivMarkers />
</Map>
);
} }
window.removeEventListener("resize", this.updateDimensions.bind(this))
}
saveLeafletInstance = (l) => {
this.setState(Object.assign({}, this.state, {
leaflet_instance: l,
}))
}
saveSelectedLayer = (e) => {
this.setState(Object.assign({}, this.state, {
selected_layer: e.name,
}))
}
myRender() {
let stamen_name = "Stamen Watercolor";
let osm_fr_name = "OpenStreetMap France";
let esri_name = "Esri WorldImagery";
return (
<Map id={"MySuperMap"} center={this.props.map.center} zoom={this.props.map.zoom} style={{ height: this.state.height }} whenReady={(e) => this.saveLeafletInstance(e.target)} onBaselayerchange={(e) => this.saveSelectedLayer(e)}>
<LayersControl position="topright">
<LayersControl.BaseLayer name={osm_fr_name} checked={this.props.map.selected_layer == osm_fr_name}>
<TileLayer
attribution='&copy; Openstreetmap France | &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
url="https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png"
maxZoom={20}
/>
</LayersControl.BaseLayer>
<LayersControl.BaseLayer name={stamen_name} checked={this.props.map.selected_layer == stamen_name}>
<LayerGroup>
<TileLayer
attribution='Map tiles by <a href="http://stamen.com">Stamen Design</a>, <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> &mdash; Map data &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
url="https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png"
minZoom={1}
maxZoom={18}
subdomains='abcd'
/>
<TileLayer
attribution='Map tiles by <a href="http://stamen.com">Stamen Design</a>, <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> &mdash; Map data &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
url="https://stamen-tiles-{s}.a.ssl.fastly.net/toner-labels/{z}/{x}/{y}.png"
minZoom={1}
subdomains='abcd'
maxZoom={18}
/>
</LayerGroup>
</LayersControl.BaseLayer>
<LayersControl.BaseLayer name={esri_name} checked={this.props.map.selected_layer == esri_name}>
<TileLayer
attribution="Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
/>
</LayersControl.BaseLayer>
</LayersControl>
<UnivMarkers />
</Map>
);
}
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { return {
map: state.app.mainMap map: state.app.mainMap
}; };
}; };
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return { return {
saveMainMap: (pos) => dispatch(saveMainMapPosition(pos)), saveMainMap: (pos) => dispatch(saveMainMapPosition(pos)),
}; };
}; };
export default connect(mapStateToProps, mapDispatchToProps)(UnivMap); export default connect(mapStateToProps, mapDispatchToProps)(UnivMap);
...@@ -12,23 +12,50 @@ const styles = theme => ({ ...@@ -12,23 +12,50 @@ const styles = theme => ({
}); });
const source = ` const source = `
Les objectifs de ce service sont : # Attention
- 1
- 2 **Ce service est à l'heure actuelle au stade de version _alpha_ afin de montrer certaines fonctionnalités. Les données seront vraisemblablement remises à zéro lors du passage à la phase _beta_ (quand toutes les fonctionnalités seront en place).**
- 3
Si vous trouvez des bugs ou si vous avez des suggestions, merci de les signaler [ici](https://gitlab.utc.fr/chehabfl/outgoing_rex/issues) ou par mail à l'adresse [florent.chehab@etu.utc.fr](mailto:florent.chehab@etu.utc.fr).
Pour rendre plus parlantes certaines fonctionnalités liées à la modération (grandement paramétrable) des informations, durant cette phase _alpha_ vous pouvez rejoindre les différents groupes d'accès tout seul :
- Pour rejoindre le groupe des modérateurs, cliquez [ici](/role_change/moderator/)
- Pour rejoindre le groupe d'accès « DRI », cliquez [ici](/role_change/dri/)
- Pour rejoindre le groupe _classique_, cliquez [ici](/role_change/normal/)
Les données actuellement présente sur la plateforme sont extraites d'un document récapitulant les destinations offertes aux GI il y a quelques semestre de cela ; et elles sont complétés par mes ajouts personnels (voir en particulier pour l'EPFL).
## Fonctionnalités manquantes
Voici les fonctionnalités qui seront rajoutées « prochainement » :
- Possibilité de filtrer les universités sur la page avec la carte ou celle avec la recherche. Les critères seront (ou devraient être) : destinations disponibles à tel semestre, destinations où sont partis des étudiants de telles branches/filières, destinations ouvertes à des étudiants de telles branches/filières, niveau de langue requis, etc.
- Possibilité de faire des listes commentées avec des universités et de les partager (ou non).
- Ajout des autres modules sur les pages des universités.
- Gestion des retours des étudiants sur leurs départs.
- Meilleure compatibilité avec les mobiles.
NB : les images de couverture sur les pages des universités seront aussi différentes ! (actuellement cet élément n'est pas _connecté_ au serveur)
Âge des données de l'UTC : --------
| **Feature** | **Support** | L'objectif est de mettre en place la phase _beta_ d'ici la prochaine session de candidature pour les départs à l'étranger.
| ------ | ----------- |
| Ancien départs | ✔ |
| Départs possibles | ✔ |
| Informatio sur les universités | ✔ |
[Rendez-vous sur le GitLab de l'UTC !](https://gitlab.utc.fr) --------
Les objectifs de ce service sont :
- Regrouper les informations sur les départs à l'étranger réalisés par les étudiants de l'UTC ;
- Les renders accessibles et commensurables.
`; `;
// Âge des données de l'UTC :
// | **Feature** | **Support** |
// | ------ | ----------- |
// | Ancien départs | ✔ |
// | Départs possibles | ✔ |
// | Informatio sur les universités | ✔ |
class PageHome extends React.Component { class PageHome extends React.Component {
render() { render() {
const { classes } = this.props; const { classes } = this.props;
...@@ -36,11 +63,10 @@ class PageHome extends React.Component { ...@@ -36,11 +63,10 @@ class PageHome extends React.Component {
<Paper className={classes.myPaper}> <Paper className={classes.myPaper}>
<Typography variant="display2"> <Typography variant="display2">
Bienvenue sur <i>Outgoing REX</i> Bienvenue sur <em>Outgoing REX</em>
</Typography> </Typography>
<Markdown source={source} /> <Markdown source={source} />
</Paper> </Paper>
); );
} }
......
...@@ -25,11 +25,11 @@ class PageMap extends React.Component { ...@@ -25,11 +25,11 @@ class PageMap extends React.Component {
Exploration Cartographique Exploration Cartographique
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={1}> {/* <Grid item xs={1}>
<UnivMapReloadButton /> <UnivMapReloadButton />
</Grid> </Grid> */}
</Grid> </Grid>
<Filter /> {/* <Filter /> */}
<UnivMap /> <UnivMap />