Subversion Repositories eFlore/Applications.cel

Rev

Rev 3771 | Rev 3779 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 3771 Rev 3773
1
<?php
1
<?php
2
 
2
 
3
/*
3
/*
4
	PHP 7
4
	PHP 7
5
 
5
 
-
 
6
	Script used to synchronize PN occ with Flora Data
-
 
7
 
6
	Script used to synchronize PN occ with CEL
8
	Écrit en franglish pour une meilleure compréhension (lol)
7
*/
9
*/
8
 
10
 
9
class PullPlantnet extends Script {
11
class PullPlantnet extends Script {
10
 
12
 
11
	protected $bdd;
13
	protected $bdd;
-
 
14
 
-
 
15
	protected $debug = false;
12
 
16
 
13
	protected $allowedPartners = ['tb', 'pn'];
17
	protected $allowedPartners = ['tb', 'pn'];
14
 
18
 
15
	// Nom du fichier contenant les correspondances d'ID TB/PN
19
	// Nom du fichier contenant les correspondances d'ID TB/PN
16
	protected $correspondingIdsFilename = '';
20
	protected $correspondingIdsFilename = '';
17
 
21
 
18
	// Cache d'informations utilisateurs provenant de l'annuaire TB
22
	// Cache d'informations utilisateurs provenant de l'annuaire TB
19
	protected $userInfos = [];
23
	protected $userInfos = [];
20
 
24
 
21
	// Cache des obs id PN traitées pendant cette run
25
	// Cache des obs id PN traitées pendant cette run
22
	protected $processedObsId = [];
26
	protected $processedObsId = [];
23
 
27
 
24
	// Paramètre de suivi et de pagination de l'API des obs PN
28
	// Paramètre de suivi et de pagination de l'API des obs PN
25
	protected $currentPage = '';
29
	protected $currentPage = '';
26
	protected $currentProject = '';
30
	protected $currentProject = '';
27
	protected $hasMore = false;
31
	protected $hasMore = false;
28
	protected $count = 0;
-
 
29
 
32
 
30
	// Traduction des organes de PN
33
	// Traduction des organes de PN
31
	protected $tagsImageTraduits = [
34
	protected $tagsImageTraduits = [
32
		'flower' => 'fleur',
35
		'flower' => 'fleur',
33
		'fruit' => 'fruit',
36
		'fruit' => 'fruit',
34
		'leaf' => 'feuille',
37
		'leaf' => 'feuille',
35
		'bark' => 'écorce',
38
		'bark' => 'écorce',
36
		'branch' => 'branche',
39
		'branch' => 'branche',
37
		'habit' => 'port',
40
		'habit' => 'port',
38
	];
41
	];
39
 
42
 
40
	// Différents types de lifecle possible pour filtrer les obs
43
	// Différents types de lifecle possible pour filtrer les obs
41
	// Modified renvoi les obs en fonction de leur date de modif
44
	// Modified renvoi les obs en fonction de leur date de modif
42
	// Created, en fonction de la date de création
45
	// Created, en fonction de la date de création
43
	// Deleted, well, you already got it
46
	// Deleted, well, you already got it
44
	protected $lifecycles = [
47
	protected $lifecycles = [
45
		'created',
48
		'created',
46
		'modified',
49
		'modified',
47
		'deleted', // deleted peut être très lent
50
		'deleted', // deleted peut être très lent (pas d'index coté PN pour le moment)
48
	];
51
	];
49
 
52
 
50
	/**
53
	/**
51
	 * Paramêtres par défaut disponibles pour la ligne de commande
54
	 * Paramêtres par défaut disponibles pour la ligne de commande
52
	 * le tableau se construit de la forme suivante :
55
	 * le tableau se construit de la forme suivante :
53
	 * - clé =  nom du paramêtre '-foo'
56
	 * - clé =  nom du paramêtre '-foo'
54
	 * - value = contient un nouveau tableau composé de cette façon :
57
	 * - value = contient un nouveau tableau composé de cette façon :
55
	 *  - booléen: true si le paramêtre est obligatoire
58
	 *  - booléen: true si le paramêtre est obligatoire
56
	 *  - booléen ou var : true si le paramêtre nécessite un valeur à sa suite ou la valeur par défaut
59
	 *  - booléen ou var : true si le paramêtre nécessite un valeur à sa suite ou la valeur par défaut
57
	 *  - string: description du contenu du paramêtre
60
	 *  - string: description du contenu du paramêtre
58
	 * Les paramêtres optionels devraient être déclaré à la fin du tableau.
61
	 * Les paramêtres optionels devraient être déclaré à la fin du tableau.
59
	 * Le dernier parametre du tableau peut avoir la valeur '...',
62
	 * Le dernier parametre du tableau peut avoir la valeur '...',
60
	 * il contiendra alors l'ensemble des paramêtres suivant trouvés sur la ligne de commande.
63
	 * il contiendra alors l'ensemble des paramêtres suivant trouvés sur la ligne de commande.
61
	 * @var array
64
	 * @var array
62
	 */
65
	 */
63
	protected $parametres_autorises = array(
66
	protected $parametres_autorises = array(
64
		'-startDate' => array(false, true, 'Date de début (parsée par strtotime). Ex: "YYYY:MM:DD HH:II:SS"'),
67
		'-startDate' => array(false, true, 'Date de début (parsée par strtotime). Ex: "YYYY:MM:DD HH:II:SS"'),
65
		'-lifecycle' => array(false, true, 'Par défaut : modified ; Au choix parmi : created/modified/deleted (deleted est lent)'),
68
		'-lifecycle' => array(false, true, 'Par défaut : modified ; Au choix parmi : created/modified/deleted (deleted est lent)'),
66
		'-pnObsId' => array(false, true, "ID de l'obs chez PlantNet"),
69
		'-pnObsId' => array(false, true, "ID de l'obs chez PlantNet"),
67
		'-project' => array(false, true, "projectId de l'obs chez PlantNet"),
70
		'-project' => array(false, true, "projectId de l'obs chez PlantNet"),
68
		'-tbObsId' => array(false, true, "ID de l'obs chez Tela"),
71
		'-tbObsId' => array(false, true, "ID de l'obs chez Tela"),
-
 
72
		'-debug' => array(false, false, "Mode débug (ajoute du log) Au choix parmi : debug/log (log est plus verbeux)"),
69
	);
73
	);
70
 
74
 
71
	public function __construct($script_nom, $parametres_cli) {
75
	public function __construct($script_nom, $parametres_cli) {
72
		parent::__construct($script_nom, $parametres_cli);
76
		parent::__construct($script_nom, $parametres_cli);
73
		$this->bdd = new Bdd();
77
		$this->bdd = new Bdd();
74
 
78
 
75
		$this->correspondingIdsFilename = Config::get('correspondingIdsFilename');
79
		$this->correspondingIdsFilename = Config::get('correspondingIdsFilename');
-
 
80
 
-
 
81
		$this->debug = $this->getParametre('debug');
76
	}
82
	}
77
 
83
 
78
	public function executer() {
84
	public function executer() {
79
		$cmd = $this->getParametre('a');
85
		$cmd = $this->getParametre('a');
80
 
86
 
81
		switch ($cmd) {
87
		switch ($cmd) {
82
			case 'updateLatest':
88
			case 'updateLatest':
83
				$this->updateLatest($this->getParametre('startDate'));
89
				$this->updateLatest($this->getParametre('startDate'), $this->getParametre('lifecycle'));
84
				break;
90
				break;
85
			case 'updateOnePN':
91
			case 'updateOnePN':
86
				$this->updateOnePN((int)$this->getParametre('pnObsId'), $this->getParametre('project'));
92
				$this->updateOnePN((int)$this->getParametre('pnObsId'), $this->getParametre('project'));
87
				break;
93
				break;
88
			case 'updateOneTB':
94
			case 'updateOneTB':
89
				$this->updateOneTB($this->getParametre('tbObsId'));
95
				$this->updateOneTB((int)$this->getParametre('tbObsId'));
90
				break;
96
				break;
91
			case 'updateCorrespondingIdsTable':
97
			case 'updateCorrespondingIdsTable':
92
				$this->updateCorrespondingIdsTable();
98
				$this->updateCorrespondingIdsTable();
93
				break;
99
				break;
94
			default:
100
			default:
95
				$msg = "Erreur : la commande '$cmd' n'existe pas!\n"
101
				$msg = "Erreur : la commande '$cmd' n'existe pas!\n"
96
						. ', utilisez plutôt :' . "\n"
102
						. ', utilisez plutôt :' . "\n"
97
						. 'php cli.php PullPlantnet -a updateLatest -startDate "YYYY:MM:DD HH:II:SS" [[-lifecycle [modified]/created/deleted]]' . "\n"
103
						. 'php cli.php PullPlantnet -a updateLatest -startDate "YYYY:MM:DD HH:II:SS" [[-lifecycle [modified]/created/deleted]]' . "\n"
98
						. 'php cli.php PullPlantnet -a updateOnePN -pnObsId "xxxxxx" -project "xxxxxx"' . "\n"
104
						. 'php cli.php PullPlantnet -a updateOnePN -pnObsId "1234567890" -project "xxxxxx"' . "\n"
99
						. 'php cli.php PullPlantnet -a updateOneTB -tbObsId "xxxxxx"' . "\n"
105
						. 'php cli.php PullPlantnet -a updateOneTB -tbObsId "1234567890"' . "\n"
100
						. 'php cli.php PullPlantnet -a updateCorrespondingIdsTable' . "\n"
106
						. 'php cli.php PullPlantnet -a updateCorrespondingIdsTable' . "\n"
101
						;
107
						;
102
				throw new Exception($msg);
108
				throw new Exception($msg);
103
		}
109
		}
104
	}
110
	}
105
 
111
 
106
	/**
112
	/**
107
	 * Considering all modified pn obs since startDate
113
	 * Considering all modified pn obs since startDate
108
	 * One pn project after an other
114
	 * One pn project after an other
109
	 * Inserting obs in tb db (if new and matching user email)
115
	 * Inserting obs in tb db (if new and matching user email)
110
	 */
116
	 */
111
	private function updateLatest(string $startDate = '1917:03:15 00:00:00', string $lifecycle = 'modified') {
117
	private function updateLatest(string $startDate = '1917:03:15 00:00:00', string $lifecycle = 'modified') {
112
		$startDate = strtotime($startDate) * 1000; // we need a microtimestamp
118
		$startDate = strtotime($startDate) * 1000; // we need a microtimestamp
113
 
119
 
114
		$pnProjects = $this->getProjects(); // refresh projects list
120
		$pnProjects = $this->getProjects(); // refresh projects list
115
		foreach ($pnProjects as $project) {
121
		foreach ($pnProjects as $project) {
116
			do {
122
			do {
117
				$observations_PN = $this->getProjectLatest($project, $startDate, $lifecycle);
123
				$observations_PN = $this->getProjectLatest($project, $startDate, $lifecycle);
118
				$this->updateObs($observations_PN);
124
				$this->updateObs($observations_PN);
119
			} while ($this->hasMore);
125
			} while ($this->hasMore);
120
		}
126
		}
121
	}
127
	}
122
 
128
 
123
	private function getProjectLatest(array $project, int $startDate, string $lifecycle): array {
129
	private function getProjectLatest(array $project, int $startDate, string $lifecycle): array {
124
		$this->currentProject = $project;
130
		$this->currentProject = $project;
125
 
131
 
126
		if (!$this->currentPage) {
132
		if (!$this->currentPage) {
127
			echo 'Projet ' . $project['name'] . "\n";
133
			echo 'Projet ' . $project['name'] . "\n";
128
 
134
 
129
			// hop, on appelle le service de PN
135
			// hop, on appelle le service de PN
130
			$url_service = str_replace(
136
			$url_service = str_replace(
131
				['{project}', '{token}', '{startDate}', '{lifecycle}'],
137
				['{project}', '{token}', '{startDate}', '{lifecycle}'],
132
				[$project['id'], Config::get('tokenPlantnet'), $startDate, $lifecycle],
138
				[$project['id'], Config::get('tokenPlantnet'), $startDate, $lifecycle],
133
				Config::get('urlPlantnetBase').Config::get('urlPlantnetLatestChanges')
139
				Config::get('urlPlantnetBase').Config::get('urlPlantnetLatestChanges')
134
			);
140
			);
-
 
141
 
135
			// die(var_dump($url_service));
142
			$this->debug("URL service derniers changements : $url_service");
-
 
143
 
136
			$this->currentPage = $url_service;
144
			$this->currentPage = $url_service;
137
		}
145
		}
138
 
146
 
139
		$ch = curl_init($this->currentPage);
147
		$ch = curl_init($this->currentPage);
140
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
148
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
141
		$reponse = curl_exec($ch);
149
		$reponse = curl_exec($ch);
142
		$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
150
		$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
143
		curl_close($ch);
151
		curl_close($ch);
144
 
152
 
145
		// cool, maintenant on va trier ce qu'on a reçu
153
		// cool, maintenant on va trier ce qu'on a reçu
146
		if (!$reponse) {
154
		if (!$reponse) {
147
			throw new Exception("\nPN ne répond rien à l'adresse {$this->currentPage}\n");
155
			throw new Exception("\nPN ne répond rien à l'adresse {$this->currentPage}\n");
148
		} elseif (200 != $code) {
156
		} elseif (200 != $code) {
149
			// l'api répond avec une 404 quand y'a une date dans le futur ou simplement pas de nouvelle obs...
157
			// l'api répond avec une 404 quand y'a une date dans le futur ou simplement pas de nouvelle obs...
150
			if (404 == $code && strpos($reponse, 'No more results')) {
158
			if (404 == $code && strpos($reponse, 'No more results')) {
-
 
159
				$this->log("Pas d'autres résultats");
151
				$this->hasMore = false;
160
				$this->hasMore = false;
152
				$this->currentPage = '';
161
				$this->currentPage = '';
153
				return [];
162
				return [];
154
			}
163
			}
155
			throw new Exception("\nPN répond avec une $code à l'adresse {$this->currentPage} : $reponse\n");
164
			throw new Exception("\nPN répond avec une $code à l'adresse {$this->currentPage} : $reponse\n");
156
		}
165
		}
157
		$responseJson = json_decode($reponse, true);
166
		$responseJson = json_decode($reponse, true);
158
		$observations_PN = $responseJson['data'] ?? [];
167
		$observations_PN = $responseJson['data'] ?? [];
159
 
168
 
160
		$this->hasMore = $responseJson['hasMore'];
169
		$this->hasMore = $responseJson['hasMore'];
161
		if ($this->hasMore) {
170
		if ($this->hasMore) {
162
			$this->count += count($observations_PN);
-
 
163
			$this->currentPage = Config::get('urlPlantnetBase').$responseJson['next'];
171
			$this->currentPage = Config::get('urlPlantnetBase').$responseJson['next'];
-
 
172
			$this->debug("URL service derniers changements, page suivante : {$this->currentPage}");
164
		} else {
173
		} else {
165
			$this->currentPage = '';
174
			$this->currentPage = '';
166
			$this->count = 0;
-
 
167
		}
175
		}
168
 
176
 
169
		return $observations_PN;
177
		return $observations_PN;
170
	}
178
	}
171
 
179
 
172
	private function getProjects(): array {
180
	private function getProjects(): array {
173
		// get PN projects list
181
		// get PN projects list
174
		$url = str_replace('{token}', Config::get('tokenPlantnet'), Config::get('urlPlantnetBase').Config::get('urlPlantnetProjects'));
182
		$url = str_replace('{token}', Config::get('tokenPlantnet'), Config::get('urlPlantnetBase').Config::get('urlPlantnetProjects'));
-
 
183
 
-
 
184
		$this->debug("URL service liste projets : $url");
175
 
185
 
176
		$ch = curl_init($url);
186
		$ch = curl_init($url);
177
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
187
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
178
		$reponse = curl_exec($ch);
188
		$reponse = curl_exec($ch);
179
		$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
189
		$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
180
		curl_close($ch);
190
		curl_close($ch);
181
 
191
 
182
		if (!$reponse || 200 != $code) {
192
		if (!$reponse || 200 != $code) {
183
			throw new Exception("\nPN répond avec une $code à l'adresse $url : $reponse\n");
193
			throw new Exception("\nPN répond avec une $code à l'adresse $url : $reponse\n");
184
		}
194
		}
185
		$pnProjects = json_decode($reponse, true);
195
		$pnProjects = json_decode($reponse, true);
186
 
196
 
187
		echo 'Liste de projets récupérée : ' . count($pnProjects) . " projets dans la liste\n";
197
		echo 'Liste de projets récupérée : ' . count($pnProjects) . " projets dans la liste\n";
-
 
198
 
-
 
199
		$this->debug("Liste projets : " . json_encode($pnProjects));
188
 
200
 
189
		return $pnProjects;
201
		return $pnProjects;
190
	}
202
	}
191
 
203
 
192
	/**
204
	/**
193
	 * For a given bunch of plantnet obs, post new one threw saisie widget service
205
	 * For a given bunch of plantnet obs, post new one threw saisie widget service
194
	 */
206
	 */
195
	private function updateObs(array $observations_PN) {
207
	private function updateObs(array $observations_PN) {
196
 
208
 
197
		$url_cel_widget_saisie = Config::get('urlCelWidgetSaisie');
209
		$url_cel_widget_saisie = Config::get('urlCelWidgetSaisie');
198
 
210
 
199
		foreach ($observations_PN as $obs) {
211
		foreach ($observations_PN as $obs) {
200
			// est-ce qu'on a déjà traité cette obs ? (oui une même obs peut être dans plusieurs projects)
212
			// est-ce qu'on a déjà traité cette obs ? (oui une même obs peut être dans plusieurs projects)
201
			if (in_array($obs['id'], $this->processedObsId)) {
213
			if (in_array($obs['id'], $this->processedObsId)) {
-
 
214
				$this->log("Obs {$obs['id']} déjà traitée pendant cette run");
202
				continue;
215
				continue;
203
			} else {
216
			} else {
204
				$this->processedObsId[] = $obs['id'];
217
				$this->processedObsId[] = $obs['id'];
-
 
218
				$this->log("Obs {$obs['id']} ajoutée à la liste de cette run");
205
			}
219
			}
206
 
220
 
207
			// on ne teste pas la suppression de l'obs ici, faut le faire après avoir vérifié si on l'a déjà synchro
221
			// on ne teste pas la suppression de l'obs ici, faut le faire après avoir vérifié si on l'a déjà synchro
208
			if (!isset($obs['currentName'])) {
222
			if (!isset($obs['currentName'])) {
-
 
223
				$this->log("Obs {$obs['id']} sans nom de taxon, on zap");
209
				continue; // pas de nom de taxon, obs inutile
224
				continue; // pas de nom de taxon, obs inutile
210
			}
225
			}
211
			if (!isset($obs['geo'])) {
226
			if (!isset($obs['geo'])) {
-
 
227
				$this->log("Obs {$obs['id']} sans geom, on zap");
212
				continue; // pas de position, obs inutile
228
				continue; // pas de position, obs inutile
213
			}
229
			}
214
			if (!isset($obs['dateObs'])) {
230
			if (!isset($obs['dateObs'])) {
-
 
231
				$this->log("Obs {$obs['id']} sans date, on zap");
215
				continue; // pas de date, obs inutile
232
				continue; // pas de date, obs inutile
216
			}
233
			}
217
 
234
 
218
			if (isset($obs['partner']['id']) && 'tela' === $obs['partner']['id']) {
235
			if (isset($obs['partner']['id']) && 'tela' === $obs['partner']['id']) {
219
				// c'est une obs tela
236
				// c'est une obs tela
220
				// si c'est mis à jour récemment coté PN et qu'on l'a pas supprimée chez nous entre temps
237
				// si c'est mis à jour récemment coté PN et qu'on l'a pas supprimée chez nous entre temps
221
					// on update les votes PN
238
					// on update les votes PN
222
					// on update l'identification proposée par PN
239
					// on update l'identification proposée par PN
223
 
240
 
224
				// @TODO
241
				// @TODO
-
 
242
				$this->log("Obs {$obs['id']} venant de Tela, mise à jour pas implémentée, on zap");
225
				continue;
243
				continue;
226
			} elseif (!isset($obs['partner']['id'])) {
244
			} elseif (!isset($obs['partner']['id'])) {
227
				// ce n'est pas une obs d'un partenaire, c'est donc une obs PN
245
				// ce n'est pas une obs d'un partenaire, c'est donc une obs PN
228
				// on récupère l'id utilisateur tela via son mail
246
				// on récupère l'id utilisateur tela via son mail
229
				$email = $obs['author']['email'];
247
				$email = $obs['author']['email'];
230
				$infos_utilisateur = $this->findUserInfo($email);
248
				$infos_utilisateur = $this->findUserInfo($email);
231
				if (!$infos_utilisateur) {
249
				if (!$infos_utilisateur) {
-
 
250
					$this->log("Obs {$obs['id']} email $email ne provient pas d'un telabotaniste");
232
					continue; // c'est pas un telabotaniste
251
					continue; // c'est pas un telabotaniste
233
				}
252
				}
234
				//echo "Synchro d'une obs d'un telabotaniste : $email";
253
				$this->log("Obs {$obs['id']} email $email provient d'un telabotaniste");
235
				// echo json_encode($obs);
254
				$this->log(json_encode($obs));
236
				// die(var_dump($obs));
255
				// die(var_dump($obs));
237
 
256
 
238
				// vérification que l'obs n'a pas déjà été ajoutée à la BdD de Tela
257
				// vérification que l'obs n'a pas déjà été ajoutée à la BdD de Tela
239
				$sql = "SELECT date_maj, id_observation FROM cel_plantnet WHERE id_plantnet = '{$obs['id']}'"
258
				$sql = "SELECT date_maj, id_observation FROM cel_plantnet WHERE id_plantnet = '{$obs['id']}'"
240
						. ' -- ' . __FILE__ . ':' . __LINE__;
259
						. ' -- ' . __FILE__ . ':' . __LINE__;
241
				$res = $this->bdd->recupererTous($sql);
260
				$res = $this->bdd->recupererTous($sql);
242
				// die(var_dump($res));
261
				// die(var_dump($res));
243
				if ($res) {
262
				if ($res) {
244
					// l'obs existe déjà, on vérifie si il faut màj puis on passe à la suite
263
					// l'obs existe déjà, on vérifie si il faut màj puis on passe à la suite
245
					// la date de l'obs est un microtime, donc on coupe les millièmes
264
					// la date de l'obs est un microtime, donc on coupe les millièmes
246
					// die(var_dump((int)($obs['dateUpdated']/1000), strtotime($res[0]['date_maj'])));
265
					// die(var_dump((int)($obs['dateUpdated']/1000), strtotime($res[0]['date_maj'])));
247
					if ((int)($obs['dateUpdated']/1000) > strtotime($res[0]['date_maj'])) {
266
					if ((int)($obs['dateUpdated']/1000) > strtotime($res[0]['date_maj'])) {
248
						echo "Obs déjà en base, mise à jour : ID PN {$obs['id']} ; ID TB {$res[0]['id_observation']} ; utilisateur_tb $email\n";
267
						echo "Obs déjà en base, mise à jour : ID PN {$obs['id']} ; ID TB {$res[0]['id_observation']} ; utilisateur_tb $email\n";
249
						$this->updateFromPN($obs, $res[0]['id_observation']);
268
						$this->updateFromPN($obs, $res[0]['id_observation']);
250
					} else {
269
					} else {
251
						echo "Obs déjà en base, déjà à jour : ID PN {$obs['id']} ; ID TB {$res[0]['id_observation']} ; utilisateur_tb $email\n";
270
						echo "Obs déjà en base, déjà à jour : ID PN {$obs['id']} ; ID TB {$res[0]['id_observation']} ; utilisateur_tb $email\n";
252
					}
271
					}
253
					continue;
272
					continue;
254
				}
273
				}
255
 
274
 
256
				if (isset($obs['deleted']) && (true === $obs['deleted'])) {
275
				if (isset($obs['deleted']) && (true === $obs['deleted'])) {
-
 
276
					$this->log("Obs {$obs['id']} supprimée coté PlantNet");
257
					continue; // obs supprimée chez PN sans être passée par nos serveurs
277
					continue; // obs supprimée chez PN sans être passée par nos serveurs
258
				}
278
				}
259
 
279
 
260
				$images = [];
280
				$images = [];
261
				$tags_images = [];
281
				$tags_images = [];
262
				$images_size = 0;
282
				$images_size = 0;
263
				foreach ($obs['images'] ?? [] as $i => $image) {
283
				foreach ($obs['images'] ?? [] as $i => $image) {
264
					if ($image['deleted']) {
284
					if ($image['deleted']) {
-
 
285
						$this->log("Obs {$obs['id']} image {$image['id']} supprimée coté PlantNet");
265
						continue;
286
						continue;
266
					}
287
					}
267
 
288
 
268
					// téléchargement de l'image PN
289
					// téléchargement de l'image PN
269
					$img = false;
290
					$img = false;
270
					$retry = 3;
291
					$retry = 3;
271
					do {
292
					do {
272
						$img = file_get_contents($image['o']);
293
						$img = file_get_contents($image['o']);
273
						$retry--;
294
						$retry--;
-
 
295
						$this->log("Obs {$obs['id']} lecture image {$image['id']} tentatives restantes : $retry");
274
					} while (!$img && $retry);
296
					} while (!$img && $retry);
275
 
297
 
276
					if (!$img) {
298
					if (!$img) {
277
						echo "Abandon, image impossible à télécharger : {$image['o']}\n";
299
						echo "Abandon, image impossible à télécharger : {$image['o']}\n";
278
						continue;
300
						continue;
279
					}
301
					}
280
 
302
 
281
					// Écriture dans un fichier temporaire
303
					// Écriture dans un fichier temporaire
282
					$tempfile = tempnam("/tmp", "PullPN_") . ".jpg";
304
					$tempfile = tempnam("/tmp", "PullPN_") . ".jpg";
283
					$handle = fopen($tempfile, "w");
305
					$handle = fopen($tempfile, "w");
284
					fwrite($handle, $img);
306
					fwrite($handle, $img);
285
					fclose($handle);
307
					fclose($handle);
286
					$images_size += filesize($tempfile);
308
					$images_size += filesize($tempfile);
287
 
309
 
288
					// echo "Image de " . number_format(filesize($tempfile), 0, ',', ' ') . " octets créé : $tempfile\n";
310
					$this->log("Obs {$obs['id']} image {$image['id']} créée " . number_format(filesize($tempfile), 0, ',', ' ') . " octets : $tempfile");
289
 
311
 
290
					$params = [
312
					$params = [
291
						'name' => 'image' . $i,
313
						'name' => 'image' . $i,
292
						'fichier' => new CURLFile($tempfile, 'image/jpg')
314
						'fichier' => new CURLFile($tempfile, 'image/jpg')
293
					];
315
					];
294
 
316
 
295
					// envoi de l'image PN à l'api de création
317
					// envoi de l'image PN à l'api de création
296
					$ch = curl_init(Config::get('urlCelUploadImageTemp'));
318
					$ch = curl_init(Config::get('urlCelUploadImageTemp'));
297
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
319
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
298
					curl_setopt($ch, CURLOPT_POST, true);
320
					curl_setopt($ch, CURLOPT_POST, true);
299
					curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
321
					curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
300
					$reponse = curl_exec($ch);
322
					$reponse = curl_exec($ch);
301
					// var_dump($reponse);
323
					$this->log("Obs {$obs['id']} image {$image['id']} envoyée à l'api de création. Réponse : $reponse");
302
					$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
324
					$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
303
					curl_close($ch);
325
					curl_close($ch);
304
 
326
 
305
					unlink($tempfile);
327
					unlink($tempfile);
306
 
328
 
307
					$reponse = simplexml_load_string($reponse);
329
					$reponse = simplexml_load_string($reponse);
308
					/* Response format if success:
330
					/* Response format if success:
309
					<?xml version="1.0" encoding="UTF-8"?>
331
					<?xml version="1.0" encoding="UTF-8"?>
310
					<root>
332
					<root>
311
						<miniature-url>https://api-test.tela-botanica.org/tmp/cel/imgtmp/chaflipe_min.jpg</miniature-url>
333
						<miniature-url>https://api-test.tela-botanica.org/tmp/cel/imgtmp/chaflipe_min.jpg</miniature-url>
312
						<image-nom>chaflipe.jpg</image-nom>
334
						<image-nom>chaflipe.jpg</image-nom>
313
						<message></message>
335
						<message></message>
314
						<debogage></debogage>
336
						<debogage></debogage>
315
					</root>
337
					</root>
316
					*/
338
					*/
317
					// var_dump($reponse);
339
					// var_dump($reponse);
318
					if ($reponse && '' == $reponse->message /* && '' != $reponse->{'image-nom'} */) {
340
					if ($reponse && '' == $reponse->message /* && '' != $reponse->{'image-nom'} */) {
319
						$images[] = (string)$reponse->{'image-nom'};
341
						$images[] = (string)$reponse->{'image-nom'};
320
 
342
 
321
						$tag = $this->tagsImageTraduits[$image['organ']] ?? $image['organ'];
343
						$tag = $this->tagsImageTraduits[$image['organ']] ?? $image['organ'];
322
						if (trim($tag)) {
344
						if (trim($tag)) {
323
							$tags_images[] = $tag;
345
							$tags_images[] = $tag;
324
						}
346
						}
325
					}
347
					}
326
				}
348
				}
327
				// var_dump($images, $tags_images);
349
				// var_dump($images, $tags_images);
328
				// die();
350
				// die();
329
 
351
 
330
				$geo = $this->getGeoInfo($obs['geo']);
352
				$geo = $this->getGeoInfo($obs['geo']);
-
 
353
				$this->log("Obs {$obs['id']} décodage du geom : " . json_encode($obs['geo']));
-
 
354
				$this->log("Obs {$obs['id']} geom décodé : " . json_encode($geo));
331
 
355
 
332
				// on insère l'obs via le service CelWidgetSaisie
356
				// on insère l'obs via le service CelWidgetSaisie
333
				$infos_obs = [
357
				$infos_obs = [
334
					'obsId1[date]' => date('d/m/Y', intdiv($obs['dateObs'], 1000)),
358
					'obsId1[date]' => date('d/m/Y', intdiv($obs['dateObs'], 1000)),
335
					'obsId1[latitude]' => $geo['lat'] ?? null,
359
					'obsId1[latitude]' => $geo['lat'] ?? null,
336
					'obsId1[longitude]' => $geo['lon'] ?? null,
360
					'obsId1[longitude]' => $geo['lon'] ?? null,
337
					'obsId1[pays]' => $geo['countryCode'] ?? null,
361
					'obsId1[pays]' => $geo['countryCode'] ?? null,
338
					'obsId1[code_postal]' => $geo['postcode'] ?? null,
362
					'obsId1[code_postal]' => $geo['postcode'] ?? null,
339
					'obsId1[commune_nom]' => $geo['city'] ?? null,
363
					'obsId1[commune_nom]' => $geo['city'] ?? null,
340
					// 'obsId1[commune_code_insee]' => '',
364
					// 'obsId1[commune_code_insee]' => '',
341
					// 'obsId1[lieudit]' => '',
365
					// 'obsId1[lieudit]' => '',
342
					// 'obsId1[milieu]' => '',
366
					// 'obsId1[milieu]' => '',
343
					'obsId1[nom_sel]' => $obs['currentName'],
367
					'obsId1[nom_sel]' => $obs['currentName'],
344
					// 'obsId1[nom_ret]' => '',
368
					// 'obsId1[nom_ret]' => '',
345
					// 'obsId1[famille]' => '',
369
					// 'obsId1[famille]' => '',
346
					'obsId1[certitude]' => 'douteux',
370
					'obsId1[certitude]' => 'douteux',
347
					// 'obsId1[notes]' => '',
371
					// 'obsId1[notes]' => '',
348
					// 'obsId1[num_nom_ret]' => '',
372
					// 'obsId1[num_nom_ret]' => '',
349
					// 'obsId1[num_nom_sel]' => '',
373
					// 'obsId1[num_nom_sel]' => '',
350
					// 'obsId1[num_taxon]' => '',
374
					// 'obsId1[num_taxon]' => '',
351
					'obsId1[referentiel]' => $this->findProbableTaxoRepos($this->currentProject['id']),
375
					'obsId1[referentiel]' => $this->findProbableTaxoRepos($this->currentProject['id']), // @TODO faire une fois et mettre en cache
352
					// 'obsId1[station]' => '',
376
					// 'obsId1[station]' => '',
353
					'obsId1[obs_id]' => $obs['id'],
377
					'obsId1[obs_id]' => $obs['id'],
354
					'projet' => 'PlantNet',
378
					'projet' => 'PlantNet',
355
					'tag-img' => implode(', ', $tags_images ?? []),
379
					'tag-img' => implode(', ', $tags_images ?? []),
356
					'tag-obs' => 'plantnet, plantnet-mobile, pn:referentiel-' . $this->currentProject['id'],
380
					'tag-obs' => 'plantnet, plantnet-mobile, pn:referentiel-' . $this->currentProject['id'],
357
					'utilisateur[courriel]' => $email,
381
					'utilisateur[courriel]' => $email,
358
					'utilisateur[id_utilisateur]' => $infos_utilisateur['id'],
382
					'utilisateur[id_utilisateur]' => $infos_utilisateur['id'],
359
					'utilisateur[nom]' => $infos_utilisateur['nom'],
383
					'utilisateur[nom]' => $infos_utilisateur['nom'],
360
					'utilisateur[prenom]' => $infos_utilisateur['prenom'],
384
					'utilisateur[prenom]' => $infos_utilisateur['prenom'],
361
					'origin' => 'pullPlantnet',
385
					'origin' => 'pullPlantnet',
362
				];
386
				];
363
 
387
 
364
				foreach ($images as $i => $image) {
388
				foreach ($images as $i => $image) {
365
					$infos_obs["obsId1[image_nom][$i]"] = $image;
389
					$infos_obs["obsId1[image_nom][$i]"] = $image;
366
				}
390
				}
-
 
391
 
367
				// var_dump($infos_obs);
392
				$this->log("Obs {$obs['id']} prête à être insérée : " . json_encode($infos_obs));
368
 
393
 
369
				// curl post $infos_obs
394
				// curl post $infos_obs
370
				$ch = curl_init($url_cel_widget_saisie);
395
				$ch = curl_init($url_cel_widget_saisie);
371
				curl_setopt($ch, CURLOPT_POST, true);
396
				curl_setopt($ch, CURLOPT_POST, true);
372
				curl_setopt($ch, CURLOPT_POSTFIELDS, $infos_obs);
397
				curl_setopt($ch, CURLOPT_POSTFIELDS, $infos_obs);
373
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
398
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
374
				$reponse = curl_exec($ch);
399
				$reponse = curl_exec($ch);
-
 
400
				$this->log("Obs {$obs['id']} réponse de la requête d'insertion : " . json_encode($reponse));
375
				$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
401
				$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
376
				curl_close($ch);
402
				curl_close($ch);
377
 
403
 
378
				if (!$reponse || 200 != $code) {
404
				if (!$reponse || 200 != $code) {
379
					throw new Exception("Ça a pété à l'envoi de l'obs :\n $reponse\n avec l'obs : " . json_encode($infos_obs) . "\n");
405
					throw new Exception("Ça a pété à l'envoi de l'obs :\n $reponse\n avec l'obs : " . json_encode($infos_obs) . "\n");
380
				}
406
				}
381
 
-
 
382
				// var_dump($reponse);
407
 
383
				$id_obs_tb = json_decode($reponse, true)['id'];
408
				$id_obs_tb = json_decode($reponse, true)['id'];
384
				$id_obs_pn = $obs['id'];
409
				$id_obs_pn = $obs['id'];
385
				// on insère les ids de correspondance obsPN obsTB dans la table cel_plantnet
410
				// on insère les ids de correspondance obsPN obsTB dans la table cel_plantnet
386
				$sql = 'INSERT INTO cel_plantnet (id_observation, id_plantnet, date_maj)'
411
				$sql = 'INSERT INTO cel_plantnet (id_observation, id_plantnet, date_maj)'
387
						.' VALUES (%s, %s, NOW())'
412
						.' VALUES (%s, %s, NOW())'
388
						. ' -- ' . __FILE__ . ':' . __LINE__;
413
						. ' -- ' . __FILE__ . ':' . __LINE__;
389
				$sql = sprintf($sql, $this->bdd->proteger($id_obs_tb), $this->bdd->proteger($id_obs_pn));
414
				$sql = sprintf($sql, $this->bdd->proteger($id_obs_tb), $this->bdd->proteger($id_obs_pn));
390
				$this->bdd->requeter($sql);
415
				$this->bdd->requeter($sql);
391
 
416
 
392
				$date = date('d/m/Y H:i:s', intdiv($obs['dateObs'], 1000));
417
				$date = date('d/m/Y H:i:s', intdiv($obs['dateObs'], 1000));
393
				$count_img = count($images);
418
				$count_img = count($images);
394
				$images_size = number_format($images_size, 0, ',', ' ');
419
				$images_size = number_format($images_size, 0, ',', ' ');
395
				echo "Obs insérée en base : ID PN $id_obs_pn ; ID TB $id_obs_tb ; utilisateur_tb $email ; date $date ; images $count_img ; taille_octets $images_size \n";
420
				echo "Obs insérée en base : ID PN $id_obs_pn ; ID TB $id_obs_tb ; utilisateur_tb $email ; date_obs $date ; images $count_img ; taille_octets $images_size \n";
396
			}
421
			}
397
		}
422
		}
398
	}
423
	}
399
 
424
 
400
	/**
425
	/**
401
	 * Return TB taxo repos names for given PN project
426
	 * Return TB taxo repos names for given PN project
402
	 *
427
	 *
403
	 * @return string
428
	 * @return string
404
	 */
429
	 */
405
	private function findProbableTaxoRepos($pnProjectId) {
430
	private function findProbableTaxoRepos($pnProjectId) {
406
		$selectedRepos = [];
431
		$selectedRepos = [];
407
		// Référentiels tela botanica indexés par référentiels PN (version octobre 2019)
432
		// Référentiels tela botanica indexés par référentiels PN (version octobre 2019)
408
		// @TODO à revoir en fonction des nouvelles mises en prod de nouveaux référentiels
433
		// @TODO à revoir en fonction des nouvelles mises en prod de nouveaux référentiels
409
		$tbTaxoRepos = [
434
		$tbTaxoRepos = [
410
			'afn' => [/*'apd', */'isfan'],
435
			'afn' => [/*'apd', */'isfan'],
411
			'aft' => ['apd'],
436
			'aft' => ['apd'],
412
			'antilles' => ['bdtxa', 'taxref'],
437
			'antilles' => ['bdtxa', 'taxref'],
413
			'canada' => ['vascan'],
438
			'canada' => ['vascan'],
414
			'comores' => ['apd'],
439
			'comores' => ['apd'],
415
			'endemia' => ['florical', 'taxref'],
440
			'endemia' => ['florical', 'taxref'],
416
			'taxref	' => ['bdtfx'],
441
			'taxref	' => ['bdtfx'],
417
			'guyane' => ['aublet', 'taxref'],
442
			'guyane' => ['aublet', 'taxref'],
418
			'hawai' => [],
443
			'hawai' => [],
419
			'lapaz' => ['aublet', 'taxref'],
444
			'lapaz' => ['aublet', 'taxref'],
420
			'martinique' => ['bdtxa', 'taxref'],
445
			'martinique' => ['bdtxa', 'taxref'],
421
			'maurice' => ['apd'],
446
			'maurice' => ['apd'],
422
			'medor' => ['lbf'],
447
			'medor' => ['lbf'],
423
			'namerica' => [],
448
			'namerica' => [],
424
			'polynesiefr' => ['taxref'],
449
			'polynesiefr' => ['taxref'],
425
			'prosea' => ['asia'],
450
			'prosea' => ['asia'],
426
			'prota' => ['isfan', 'apd'],
451
			'prota' => ['isfan', 'apd'],
427
			'provence' => ['bdtfx', 'taxref'],
452
			'provence' => ['bdtfx', 'taxref'],
428
			'reunion' => ['bdtre', 'taxref'],
453
			'reunion' => ['bdtre', 'taxref'],
429
			'salad' => ['bdtfx', 'taxref'],
454
			'salad' => ['bdtfx', 'taxref'],
430
			'useful' => [],
455
			'useful' => [],
431
			'weurope' => ['bdtfx'],
456
			'weurope' => ['bdtfx'],
432
			'the-plant-list' => [],
457
			'the-plant-list' => [],
433
			'iscantree' => ['apd'],
458
			'iscantree' => ['apd'],
434
			'central-america' => [],
459
			'central-america' => [],
435
			'invasion' => ['bdtfx'],
460
			'invasion' => ['bdtfx'],
436
			'weeds' => ['bdtfx'],
461
			'weeds' => ['bdtfx'],
437
			'malaysia' => [],
462
			'malaysia' => [],
438
		];
463
		];
439
 
464
 
440
		if (array_key_exists($pnProjectId, $tbTaxoRepos)) {
465
		if (array_key_exists($pnProjectId, $tbTaxoRepos)) {
441
			array_map(function($repo) use ($selectedRepos) {
466
			array_map(function($repo) use ($selectedRepos) {
442
				$selectedRepos[] = $repo;
467
				$selectedRepos[] = $repo;
443
			}, $tbTaxoRepos[$pnProjectId]);
468
			}, $tbTaxoRepos[$pnProjectId]);
444
		}
469
		}
445
 
470
 
446
		// @TODO chercher le nom dans plusieurs référentiels avant d'envoyer l'obs
471
		// @TODO chercher le nom dans plusieurs référentiels avant d'envoyer l'obs
447
		// prendre "enrichirDonneesTaxonomiques" du service de saisie et le coller ici
472
		// prendre "enrichirDonneesTaxonomiques" du service de saisie et le coller ici
448
		// en attendant on envoi juste le premier référentiel trouvé
473
		// en attendant on envoi juste le premier référentiel trouvé
449
		return $selectedRepos[0] ?? '';
474
		return $selectedRepos[0] ?? '';
450
	}
475
	}
451
 
476
 
452
	/*
477
	/*
453
	 * Retrouve les infos d'un utilisateur tela à partir de son email
478
	 * Retrouve les infos d'un utilisateur tela à partir de son email
454
	 * @param string $email User email
479
	 * @param string $email User email
455
	 * @return mixed 		Tableau des infos de l'utilisateur
480
	 * @return mixed 		Tableau des infos de l'utilisateur
456
	 *						False si l'email n'est pas trouvé dans l'annuaire
481
	 *						False si l'email n'est pas trouvé dans l'annuaire
457
	 */
482
	 */
458
	private function findUserInfo($email) {
483
	private function findUserInfo($email) {
459
		// cherche dans le cache
484
		// cherche dans le cache
460
		if (array_key_exists($email, $this->userInfos)) {
485
		if (array_key_exists($email, $this->userInfos)) {
461
			$infosUtilisateur = $this->userInfos[$email];
486
			$infosUtilisateur = $this->userInfos[$email];
462
			// echo("Email in cache : $email\n");
487
			// echo("Email in cache : $email\n");
463
		} else {
488
		} else {
464
			$infosUtilisateur = false;
489
			$infosUtilisateur = false;
465
			$urlInfosUtilisateur = str_replace('{email}', $email, Config::get('urlAnnuaireIdParEmail'));
490
			$urlInfosUtilisateur = str_replace('{email}', $email, Config::get('urlAnnuaireIdParEmail'));
466
 
491
 
467
			$ch = curl_init($urlInfosUtilisateur);
492
			$ch = curl_init($urlInfosUtilisateur);
468
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
493
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
469
			$reponse = curl_exec($ch);
494
			$reponse = curl_exec($ch);
470
			$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
495
			$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
471
			curl_close($ch);
496
			curl_close($ch);
472
 
497
 
473
			// on peut pas tester le code de réponse de cette api, si l'utilisateur n'est pas trouvé ça fait une 500 >_<
498
			// on peut pas tester le code de réponse de cette api, si l'utilisateur n'est pas trouvé ça fait une 500 >_<
474
			// une bonne réponse ressemble à ça :
499
			// une bonne réponse ressemble à ça :
475
			// {"killian@tela-botanica.org":{"id":"30489","prenom":"Killian","nom":"Stefanini","pseudo":"Killian Stefanini","pseudoUtilise":true,"intitule":"Killian Stefanini","avatar":"\/\/www.gravatar.com\/avatar\/a9b9b8484076540924c03af816c77fc8?s=50&r=g&d=mm","groupes":{"19226":"adm","18943":"","23152":"","21684":"","21598":"adm","23184":"","23516":""},"permissions":["editor"],"nomWiki":"KillianStefanini"}}
500
			// {"killian@tela-botanica.org":{"id":"30489","prenom":"Killian","nom":"Stefanini","pseudo":"Killian Stefanini","pseudoUtilise":true,"intitule":"Killian Stefanini","avatar":"\/\/www.gravatar.com\/avatar\/a9b9b8484076540924c03af816c77fc8?s=50&r=g&d=mm","groupes":{"19226":"adm","18943":"","23152":"","21684":"","21598":"adm","23184":"","23516":""},"permissions":["editor"],"nomWiki":"KillianStefanini"}}
476
			$reponse = json_decode($reponse, true);
501
			$reponse = json_decode($reponse, true);
477
			if (!isset($reponse)) {
502
			if (!isset($reponse)) {
478
				throw new Exception("\nL'annuaire n'a pas répondu avec du json : code $code à l'adresse $urlInfosUtilisateur : $reponse\n");
503
				throw new Exception("\nL'annuaire n'a pas répondu avec du json : code $code à l'adresse $urlInfosUtilisateur : $reponse\n");
479
			}
504
			}
480
 
505
 
481
			if (!isset($reponse['error'])) {
506
			if (!isset($reponse['error'])) {
482
				$infosUtilisateur = array_shift($reponse);
507
				$infosUtilisateur = array_shift($reponse);
483
			}
508
			}
484
 
509
 
485
			// met en cache
510
			// met en cache
486
			$this->userInfos[$email] = $infosUtilisateur;
511
			$this->userInfos[$email] = $infosUtilisateur;
487
			// echo("Email cached : $email\n");
512
			// echo("Email cached : $email\n");
488
		}
513
		}
489
 
514
 
490
		return $infosUtilisateur;
515
		return $infosUtilisateur;
491
	}
516
	}
492
 
517
 
493
	private function getGeoInfo($obs) {
518
	private function getGeoInfo($obs) {
494
		$geo = [];
519
		$geo = [];
495
		if (!isset($obs['lat']) && !isset($obs['lon'])) {
520
		if (!isset($obs['lat']) && !isset($obs['lon'])) {
496
			return $geo;
521
			return $geo;
497
		}
522
		}
498
 
523
 
499
		// $data = [
524
		// $data = [
500
		// 	'hitsPerPage' => 1,
525
		// 	'hitsPerPage' => 1,
501
		// 	'aroundLatLng' => "{$obs['lat']},{$obs['lon']}"
526
		// 	'aroundLatLng' => "{$obs['lat']},{$obs['lon']}"
502
		// ];
527
		// ];
503
		$headers = [
528
		$headers = [
504
			'X-Algolia-Application-Id' => Config::get('algoliaApplicationId'),
529
			'X-Algolia-Application-Id' => Config::get('algoliaApplicationId'),
505
			'X-Algolia-API-Key' => Config::get('algoliaAPIKey'),
530
			'X-Algolia-API-Key' => Config::get('algoliaAPIKey'),
506
		];
531
		];
507
 
532
 
508
		$lat = number_format($obs['lat'], 6, '.', '');
533
		$lat = number_format($obs['lat'], 6, '.', '');
509
		$lon = number_format($obs['lon'], 6, '.', '');
534
		$lon = number_format($obs['lon'], 6, '.', '');
510
 
535
 
511
		$ch = curl_init(Config::get('urlReverseGeocodingLatLng')."$lat,$lon");
536
		$ch = curl_init(Config::get('urlReverseGeocodingLatLng')."$lat,$lon");
512
		curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
537
		curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
513
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
538
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
514
		$reponse = curl_exec($ch);
539
		$reponse = curl_exec($ch);
515
		$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
540
		$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
516
		curl_close($ch);
541
		curl_close($ch);
517
 
542
 
518
		if ($reponse) {
543
		if ($reponse) {
519
			$reponse = json_decode($reponse, true);
544
			$reponse = json_decode($reponse, true);
520
			// die(var_dump($reponse));
545
			// die(var_dump($reponse));
521
			$infos = $reponse['hits'][0];
546
			$infos = $reponse['hits'][0];
522
 
547
 
523
			$geo = [
548
			$geo = [
524
				'lat' => (string) $obs['lat'],
549
				'lat' => (string) $obs['lat'],
525
				'lon' => (string) $obs['lon'],
550
				'lon' => (string) $obs['lon'],
526
				'country' => $infos['country']['fr'] ?? $infos['country']['default'],
551
				'country' => $infos['country']['fr'] ?? $infos['country']['default'],
527
				'city' => $infos['city']['default'][0] ?? null,
552
				'city' => $infos['city']['default'][0] ?? null,
528
				'postcode' => $infos['postcode'][0] ?? null,
553
				'postcode' => $infos['postcode'][0] ?? null,
529
				'countryCode' => strtoupper($infos['country_code']),
554
				'countryCode' => strtoupper($infos['country_code']),
530
			];
555
			];
531
		}
556
		}
532
 
557
 
533
		return $geo;
558
		return $geo;
534
	}
559
	}
535
 
560
 
536
	private function updateFromPN(array $pnObs, string $tbObsId) {
561
	private function updateFromPN(array $pnObs, string $tbObsId) {
537
		if (!is_array($pnObs) || !is_string($tbObsId)) {
562
		if (!is_array($pnObs) || !is_string($tbObsId)) {
538
			// die(var_dump($pnObs, $tbObsId));
563
			// die(var_dump($pnObs, $tbObsId));
539
			throw new Exception("\nBad params types, give me an array and an integer plz\n");
564
			throw new Exception("\nBad params types, give me an array and an integer plz\n");
540
		}
565
		}
541
		// die(var_dump($pnObs));
566
		// die(var_dump($pnObs));
542
 
567
 
543
		// log update date to cel_plantnet
568
		// log update date to cel_plantnet
544
		$sql = "UPDATE cel_plantnet SET date_maj = NOW() WHERE id_observation = $tbObsId"
569
		$sql = "UPDATE cel_plantnet SET date_maj = NOW() WHERE id_observation = $tbObsId"
545
				. ' -- ' . __FILE__ . ':' . __LINE__;
570
				. ' -- ' . __FILE__ . ':' . __LINE__;
546
		$this->bdd->requeter($sql);
571
		$this->bdd->requeter($sql);
547
 
572
 
548
		// check for deleted
573
		// check for deleted
549
		if (isset($pnObs['deleted']) && (true === $pnObs['deleted'])) {
574
		if (isset($pnObs['deleted']) && (true === $pnObs['deleted'])) {
550
			// est-ce une obs issue de PN ?
575
			// est-ce une obs issue de PN ?
551
			//// faut regarder le champ input_source == PlantNet
576
			//// faut regarder le champ input_source == PlantNet
552
			$sql = "SELECT input_source FROM occurrence WHERE id = '$tbObsId'"
577
			$sql = "SELECT input_source FROM occurrence WHERE id = '$tbObsId'"
553
					. ' -- ' . __FILE__ . ':' . __LINE__;
578
					. ' -- ' . __FILE__ . ':' . __LINE__;
554
			$res = $this->bdd->recupererTous($sql);
579
			$res = $this->bdd->recupererTous($sql);
555
 
580
 
556
			if (isset($res[0]) && ('PlantNet' === $res[0]['input_source'])) {
581
			if (isset($res[0]) && ('PlantNet' === $res[0]['input_source'])) {
557
				// oui ? alors supprimer obs !
582
				// oui ? alors supprimer obs !
558
				echo "Obs supprimée coté PN, suppression : ID PN {$obs['id']} ; ID TB {$res[0]['id_observation']}\n";
583
				echo "Obs supprimée coté PN, suppression : ID PN {$obs['id']} ; ID TB {$res[0]['id_observation']}\n";
559
 
584
 
560
				$sql = "UPDATE photos SET occurrence_id = NULL WHERE occurrence_id = '$tbObsId'"
585
				$sql = "UPDATE photos SET occurrence_id = NULL WHERE occurrence_id = '$tbObsId'"
561
						. ' -- ' . __FILE__ . ':' . __LINE__;
586
						. ' -- ' . __FILE__ . ':' . __LINE__;
562
				$this->bdd->requeter($sql);
587
				$this->bdd->requeter($sql);
563
 
588
 
564
				$sql = "DELETE FROM occurrence WHERE id = '$tbObsId'"
589
				$sql = "DELETE FROM occurrence WHERE id = '$tbObsId'"
565
						. ' -- ' . __FILE__ . ':' . __LINE__;
590
						. ' -- ' . __FILE__ . ':' . __LINE__;
566
				$this->bdd->requeter($sql);
591
				$this->bdd->requeter($sql);
567
			}
592
			}
568
		}
593
		}
569
 
594
 
570
		// check for deleted images
595
		// check for deleted images
571
		foreach ($pnObs['images'] as $image) {
596
		foreach ($pnObs['images'] as $image) {
572
			if ($image['deleted']) {
597
			if ($image['deleted']) {
573
				// no idea
598
				// no idea
574
				// y'a pas encore de lien tangible entre les images PN et les notres
599
				// y'a pas encore de lien tangible entre les images PN et les notres
575
			}
600
			}
576
		}
601
		}
577
 
602
 
578
		// @TODO : finir la comparaison des proposition entre celles de Tela et celles PN
603
		// @TODO : finir la comparaison des proposition entre celles de Tela et celles PN
579
		// // check for votes (notes et taxon)
604
		// // check for votes (notes et taxon)
580
		// $names = [];
605
		// $names = [];
581
		// foreach ($pnObs['computed']['votes'] as $vote) {
606
		// foreach ($pnObs['computed']['votes'] as $vote) {
582
		// 	echo($vote['name']);
607
		// 	echo($vote['name']);
583
		// 	echo($vote['score']['pn']);
608
		// 	echo($vote['score']['pn']);
584
		// 	echo($vote['score']['total']);
609
		// 	echo($vote['score']['total']);
585
 
610
 
586
		// 	$names[$vote['score']['pn']] = $vote['name'];
611
		// 	$names[$vote['score']['pn']] = $vote['name'];
587
		// }
612
		// }
588
		// ksort($names, SORT_NUMERIC); // votes sorted by score asc
613
		// ksort($names, SORT_NUMERIC); // votes sorted by score asc
589
		// $pn_sorted_votes = array_reverse($names, true); // desc
614
		// $pn_sorted_votes = array_reverse($names, true); // desc
590
 
615
 
591
		// // get existing votes
616
		// // get existing votes
592
		// $sql = "SELECT id_commentaire, nom_sel, nom_sel_nn, nom_referentiel, nom_referentiel, utilisateur_courriel, proposition_initiale, proposition_retenue"
617
		// $sql = "SELECT id_commentaire, nom_sel, nom_sel_nn, nom_referentiel, nom_referentiel, utilisateur_courriel, proposition_initiale, proposition_retenue"
593
		// 		. "WHERE ce_observation = '$tbObsId' AND ce_commentaire_parent = 0"
618
		// 		. "WHERE ce_observation = '$tbObsId' AND ce_commentaire_parent = 0"
594
		// 		. ' -- ' . __FILE__ . ':' . __LINE__;
619
		// 		. ' -- ' . __FILE__ . ':' . __LINE__;
595
		// $existing_votes = $this->bdd->recupererTous($sql);
620
		// $existing_votes = $this->bdd->recupererTous($sql);
596
 
621
 
597
		// // @TODO : compare votes
622
		// // @TODO : compare votes
598
		// // insert_new_votes($pn_sorted_votes, $existing_votes);
623
		// // insert_new_votes($pn_sorted_votes, $existing_votes);
599
	}
624
	}
600
 
625
 
601
	private function updateOnePN($pnObsId, $pnProjectId) {
626
	private function updateOnePN($pnObsId, $pnProjectId) {
602
		if (!is_int($pnObsId) || !is_string($pnProjectId)) {
627
		if (!is_int($pnObsId) || !is_string($pnProjectId)) {
603
			die(var_dump($pnObsId, $pnProjectId));
628
			die(var_dump($pnObsId, $pnProjectId));
604
			throw new Exception("\nBad params types, give me an integer and a string plz\n");
629
			throw new Exception("\nBad params types, give me an integer and a string plz\n");
605
		}
630
		}
606
		// get PN project list
631
		// get PN project list
607
		$list = [];
632
		$list = [];
608
		$pnProjects = $this->getProjects(); // refresh projects list
633
		$pnProjects = $this->getProjects(); // refresh projects list
609
		foreach ($pnProjects as $project) {
634
		foreach ($pnProjects as $project) {
610
			$list[$project['id']] = $project['name'];
635
			$list[$project['id']] = $project['name'];
611
		}
636
		}
612
 
637
 
613
		// if project not in list display list
638
		// if project not in list display list
614
		if (!array_key_exists($pnProjectId, $list)) {
639
		if (!array_key_exists($pnProjectId, $list)) {
615
			echo "Available projects:\n";
640
			echo "Available projects:\n";
616
			foreach ($list as $projectId => $projectName) {
641
			foreach ($list as $projectId => $projectName) {
617
				echo " - $projectId ($projectName)\n";
642
				echo " - $projectId ($projectName)\n";
618
			}
643
			}
619
			throw new Exception("Project $pnProjectId does not exist\n");
644
			throw new Exception("Project $pnProjectId does not exist\n");
620
		} else {
645
		} else {
621
			$this->currentProject = [
646
			$this->currentProject = [
622
				'id' => $pnProjectId,
647
				'id' => $pnProjectId,
623
				'name' => $list[$pnProjectId],
648
				'name' => $list[$pnProjectId],
624
			];
649
			];
625
		}
650
		}
626
 
651
 
627
		// get PN obs
652
		// get PN obs
628
		$urlInfosObs = str_replace(
653
		$urlInfosObs = str_replace(
629
			['{token}', '{project}', '{id}'],
654
			['{token}', '{project}', '{id}'],
630
			[Config::get('tokenPlantnet'), $pnProjectId, $pnObsId],
655
			[Config::get('tokenPlantnet'), $pnProjectId, $pnObsId],
631
			Config::get('urlPlantnetBase').Config::get('urlPlantnetObsById')
656
			Config::get('urlPlantnetBase').Config::get('urlPlantnetObsById')
632
		);
657
		);
633
		$ch = curl_init($urlInfosObs);
658
		$ch = curl_init($urlInfosObs);
634
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
659
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
635
		$response = curl_exec($ch);
660
		$response = curl_exec($ch);
636
		$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
661
		$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
637
		curl_close($ch);
662
		curl_close($ch);
638
 
663
 
639
		if (!$response || 200 != $code) {
664
		if (!$response || 200 != $code) {
640
			throw new Exception("\nPlantnet api ($urlInfosObs) replied with code $code : $response\n");
665
			throw new Exception("\nPlantnet api ($urlInfosObs) replied with code $code : $response\n");
641
		}
666
		}
642
 
667
 
643
		// change last modification date to force update
668
		// change last modification date to force update
644
		$obs = json_decode($response, true);
669
		$obs = json_decode($response, true);
645
		$date = new DateTime();
670
		$date = new DateTime();
646
		$date->setTimestamp(intdiv($obs['dateUpdated'], 1000) - 1);
671
		$date->setTimestamp(intdiv($obs['dateUpdated'], 1000) - 1);
647
		$date = $this->bdd->proteger($date->format('Y-m-d H:i:s'));
672
		$date = $this->bdd->proteger($date->format('Y-m-d H:i:s'));
648
		$sql = "UPDATE cel_plantnet SET date_maj = $date WHERE id_plantnet = '$pnObsId'"
673
		$sql = "UPDATE cel_plantnet SET date_maj = $date WHERE id_plantnet = '$pnObsId'"
649
				. ' -- ' . __FILE__ . ':' . __LINE__;
674
				. ' -- ' . __FILE__ . ':' . __LINE__;
650
		$this->bdd->requeter($sql);
675
		$this->bdd->requeter($sql);
651
 
676
 
652
		$this->updateObs([$obs]);
677
		$this->updateObs([$obs]);
653
	}
678
	}
654
 
679
 
655
	private function updateOneTB($id) {
680
	private function updateOneTB($id) {
656
		$corres = $this->findMatchingPartnerId($id, 'tb');
681
		$corres = $this->findMatchingPartnerId($id, 'tb');
657
		echo "L'obs tela avec l'id $id a été trouvée chez plantnet avec l'id : " .$corres['pn'] . "\n";
682
		echo "L'obs tela avec l'id $id a été trouvée chez plantnet avec l'id : " .$corres['pn'] . "\n";
658
		echo "La suite n'a pas été implémentée.\n";
683
		echo "La suite n'a pas été implémentée.\n";
659
	}
684
	}
660
 
685
 
661
	/*
686
	/*
662
	 * Trouve les ids d'une obs chez les partenaires
687
	 * Trouve les ids d'une obs chez les partenaires
663
	 * Il faut lancer updateMatchingPartnersIds pour obtenir une liste fraiche
688
	 * Il faut lancer updateMatchingPartnersIds pour obtenir une liste fraiche
664
	 *
689
	 *
665
	 * @param 	string $id			L'id de l'obs
690
	 * @param 	string $id			L'id de l'obs
666
	 * @param 	string $partner		Le partenaire correspondant à l'id de l'obs
691
	 * @param 	string $partner		Le partenaire correspondant à l'id de l'obs
667
	 *
692
	 *
668
	 * @return  mixed 				Le tableau de correspondances d'ids
693
	 * @return  mixed 				Le tableau de correspondances d'ids
669
	 *								False si l'id n'est pas trouvé
694
	 *								False si l'id n'est pas trouvé
670
	 */
695
	 */
671
	private function findMatchingPartnerId($id, $partner) {
696
	private function findMatchingPartnerId($id, $partner) {
672
		if (!in_array($partner, $this->allowedPartners)) {
697
		if (!in_array($partner, $this->allowedPartners)) {
673
			throw new Exception("\nUnknown partner : $partner. Available Partners : " . implode(', ', $this->allowedPartners) . "\n");
698
			throw new Exception("\nUnknown partner : $partner. Available Partners : " . implode(', ', $this->allowedPartners) . "\n");
674
		}
699
		}
675
 
700
 
676
		// le json ressemble à ça :
701
		// le json ressemble à ça :
677
		// {"tb":"2369732","pn":"1001020199"}, {"tb":"2369733","pn":"1001020176"}, ...
702
		// {"tb":"2369732","pn":"1001020199"}, {"tb":"2369733","pn":"1001020176"}, ...
678
		$ids = json_decode(file_get_contents($this->correspondingIdsFilename), true);
703
		$ids = json_decode(file_get_contents($this->correspondingIdsFilename), true);
679
		// extrait la colonne du partenaire recherché du tableau multidimensionnel fraichement décodé
704
		// extrait la colonne du partenaire recherché du tableau multidimensionnel fraichement décodé
680
		$partnerColumn = array_column($ids, $partner);
705
		$partnerColumn = array_column($ids, $partner);
681
		// cherche l'index de l'id recherché
706
		// cherche l'index de l'id recherché
682
		$index = array_search($id, $partnerColumn);
707
		$index = array_search($id, $partnerColumn);
683
 
708
 
684
		return $index ? $ids[$index] : false;
709
		return $index ? $ids[$index] : false;
685
	}
710
	}
686
 
711
 
687
	private function updateCorrespondingIdsTable() {
712
	private function updateCorrespondingIdsTable() {
688
		// first update cache file if necessary
713
		// first update cache file if necessary
689
		if (!file_exists($this->correspondingIdsFilename) || time() > (filemtime($this->correspondingIdsFilename) + (24 * 3600))) {
714
		if (!file_exists($this->correspondingIdsFilename) || time() > (filemtime($this->correspondingIdsFilename) + (24 * 3600))) {
690
			$this->updateMatchingPartnersIds();
715
			$this->updateMatchingPartnersIds();
691
		}
716
		}
692
 
717
 
693
		$data = file_get_contents($this->correspondingIdsFilename);
718
		$data = file_get_contents($this->correspondingIdsFilename);
694
		$data = json_decode($data, true);
719
		$data = json_decode($data, true);
695
 
720
 
696
		$now = new DateTime;
721
		$now = new DateTime;
697
		$now = $this->bdd->proteger($now->format('Y-m-d H:i:s'));
722
		$now = $this->bdd->proteger($now->format('Y-m-d H:i:s'));
698
 
723
 
699
		$sqlInsert = 'INSERT INTO cel_plantnet (id_observation, id_plantnet, date_maj)'
724
		$sqlInsert = 'INSERT INTO cel_plantnet (id_observation, id_plantnet, date_maj)'
700
					. ' VALUES %s'
725
					. ' VALUES %s'
701
					. ' ON DUPLICATE KEY UPDATE id_observation=VALUES(id_observation), id_plantnet=VALUES(id_plantnet)'
726
					. ' ON DUPLICATE KEY UPDATE id_observation=VALUES(id_observation), id_plantnet=VALUES(id_plantnet)'
702
					. ' -- ' . __FILE__ . ':' . __LINE__;
727
					. ' -- ' . __FILE__ . ':' . __LINE__;
703
		$values = '';
728
		$values = '';
704
 
729
 
705
		echo "Updating matching partners ids table\n";
730
		echo "Updating matching partners ids table\n";
706
		foreach ($data as $id => $corres) {
731
		foreach ($data as $id => $corres) {
707
			$id_obs_tb = $this->bdd->proteger($corres['tb']);
732
			$id_obs_tb = $this->bdd->proteger($corres['tb']);
708
			$id_obs_pn = $this->bdd->proteger($corres['pn']);
733
			$id_obs_pn = $this->bdd->proteger($corres['pn']);
709
			// on insère les ids de correspondance obsPN obsTB dans la table cel_plantnet
734
			// on insère les ids de correspondance obsPN obsTB dans la table cel_plantnet
710
			$values .= " ($id_obs_tb, $id_obs_pn, $now),";
735
			$values .= " ($id_obs_tb, $id_obs_pn, $now),";
711
 
736
 
712
			if (0 === $id % 100) {
737
			if (0 === $id % 100) {
713
				$values = substr($values, 0, -1); // retire la dernière virgule
738
				$values = substr($values, 0, -1); // retire la dernière virgule
714
				$sql = sprintf($sqlInsert, $values);
739
				$sql = sprintf($sqlInsert, $values);
715
				// var_dump($sql);
740
				// var_dump($sql);
716
				$this->bdd->requeter($sql);
741
				$this->bdd->requeter($sql);
717
				$values = '';
742
				$values = '';
718
			}
743
			}
719
		}
744
		}
720
		// Faut envoyer les dernières données qui sont pas passées dans le modulo
745
		// Faut envoyer les dernières données qui sont pas passées dans le modulo
721
		$values = substr($values, 0, -1); // retire la dernière virgule
746
		$values = substr($values, 0, -1); // retire la dernière virgule
722
		$sql = sprintf($sqlInsert, $values);
747
		$sql = sprintf($sqlInsert, $values);
723
		// var_dump($sql);
748
		// var_dump($sql);
724
		$this->bdd->requeter($sql);
749
		$this->bdd->requeter($sql);
725
 
750
 
726
		$count = count($data);
751
		$count = count($data);
727
		echo "Success: #$count updated\n";
752
		echo "Success: #$count updated\n";
728
	}
753
	}
729
 
754
 
730
	private function updateMatchingPartnersIds() {
755
	private function updateMatchingPartnersIds() {
731
		// curl -X GET --header 'Accept: application/json' 'https://bourbonnais.cirad.fr:8081/v1/edi/observations/partnerids?token=f0ca433febe320675c24ee2ddfab8b82f65d556b' > partnerids.txt
756
		// curl -X GET --header 'Accept: application/json' 'https://bourbonnais.cirad.fr:8081/v1/edi/observations/partnerids?token=f0ca433febe320675c24ee2ddfab8b82f65d556b' > partnerids.txt
732
		$matchingPartnersIdsServiceUrl = str_replace(
757
		$matchingPartnersIdsServiceUrl = str_replace(
733
			'{token}',
758
			'{token}',
734
			Config::get('tokenPlantnet'),
759
			Config::get('tokenPlantnet'),
735
			Config::get('urlPlantnetBase').Config::get('urlPlantnetMatchingPartner')
760
			Config::get('urlPlantnetBase').Config::get('urlPlantnetMatchingPartner')
736
		);
761
		);
737
		$ch = curl_init($matchingPartnersIdsServiceUrl);
762
		$ch = curl_init($matchingPartnersIdsServiceUrl);
738
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
763
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
739
 
764
 
740
		echo "Updating matching partners ids...\n";
765
		echo "Updating matching partners ids...\n";
741
		$reponse = curl_exec($ch);
766
		$reponse = curl_exec($ch);
742
		$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
767
		$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
743
		curl_close($ch);
768
		curl_close($ch);
744
 
769
 
745
 
770
 
746
		if (!$reponse || 200 != $code) {
771
		if (!$reponse || 200 != $code) {
747
			throw new Exception("\nError updating partners ids, http error code \"$code\" whith url \"$matchingPartnersIdsServiceUrl\"...\n");
772
			throw new Exception("\nError updating partners ids, http error code \"$code\" whith url \"$matchingPartnersIdsServiceUrl\"...\n");
748
		}
773
		}
749
		echo "Response data from PN api gathered, know computing...\n";
774
		echo "Response data from PN api gathered, know computing...\n";
750
 
775
 
751
		if (!file_put_contents($this->correspondingIdsFilename, $reponse)) {
776
		if (!file_put_contents($this->correspondingIdsFilename, $reponse)) {
752
			throw new Exception("\nError writing correspondingIdsFilename with path : " . $this->correspondingIdsFilename . "\n");
777
			throw new Exception("\nError writing correspondingIdsFilename with path : " . $this->correspondingIdsFilename . "\n");
753
		}
778
		}
754
		echo "Matching partners ids saved!";
779
		echo "Matching partners ids saved!";
755
	}
780
	}
-
 
781
 
-
 
782
	private function debug($text) {
-
 
783
		if ($this->debug) {
-
 
784
			echo 'DEBUG - ' . $text . "\n";
-
 
785
		}
-
 
786
	}
-
 
787
 
-
 
788
	private function log($text) {
-
 
789
		if ('log' == $this->debug) {
-
 
790
			echo 'LOG - ' . $text . "\n";
-
 
791
		}
-
 
792
	}
756
}
793
}
757
794