Subversion Repositories eFlore/Applications.cel

Rev

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

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