diff --git a/api/composer.json b/api/composer.json index 61bfba85eb5230d3c8e0848f77782c937b399871..ab542cb78c8f0aeac539caf531a2071dda48849e 100644 --- a/api/composer.json +++ b/api/composer.json @@ -5,6 +5,7 @@ "ext-ctype": "*", "ext-iconv": "*", "api-platform/api-pack": "^1.1", + "beberlei/doctrineextensions": "^1.2", "doctrine/doctrine-migrations-bundle": "^2.0", "guzzlehttp/guzzle": "^6.3", "league/csv": "^9.5", diff --git a/api/composer.lock b/api/composer.lock index 8dc42cffad35f323e334b75a48306b32f7443e86..2ff282d3f80ffad36d355ef3fd092f59083a13be 100644 --- a/api/composer.lock +++ b/api/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "37aaa121253355e46b895eaf738b134d", + "content-hash": "61293d270d296b2e9ea43d49fe80c20d", "packages": [ { "name": "api-platform/api-pack", @@ -190,6 +190,60 @@ ], "time": "2019-11-23T11:27:39+00:00" }, + { + "name": "beberlei/doctrineextensions", + "version": "v1.2.6", + "source": { + "type": "git", + "url": "https://github.com/beberlei/DoctrineExtensions.git", + "reference": "af72c4a136b744f1268ca8bb4da47a2f8af78f86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beberlei/DoctrineExtensions/zipball/af72c4a136b744f1268ca8bb4da47a2f8af78f86", + "reference": "af72c4a136b744f1268ca8bb4da47a2f8af78f86", + "shasum": "" + }, + "require": { + "doctrine/orm": "^2.6", + "php": "^7.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.14", + "nesbot/carbon": "*", + "phpunit/phpunit": "^7.0 || ^8.0", + "symfony/yaml": "^4.2", + "zf1/zend-date": "^1.12", + "zf1/zend-registry": "^1.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "DoctrineExtensions\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Steve Lacey", + "email": "steve@stevelacey.net" + } + ], + "description": "A set of extensions to Doctrine 2 that add support for additional query functions available in MySQL and Oracle.", + "keywords": [ + "database", + "doctrine", + "orm" + ], + "time": "2019-12-05T09:49:04+00:00" + }, { "name": "doctrine/annotations", "version": "v1.8.0", diff --git a/api/config/packages/doctrine.yaml b/api/config/packages/doctrine.yaml index 66d671fe48d1c5b059ee91015fbe96124b76c08d..97df4085f93635d71740f34cffd20074d54d4abc 100644 --- a/api/config/packages/doctrine.yaml +++ b/api/config/packages/doctrine.yaml @@ -17,3 +17,7 @@ doctrine: dir: '%kernel.project_dir%/src/Entity' prefix: 'App\Entity' alias: App + dql: + string_functions: + TO_CHAR: DoctrineExtensions\Query\Postgresql\DateFormat + TO_DATE: DoctrineExtensions\Query\Postgresql\StrToDate diff --git a/api/src/Controller/MeanPriceForSquaredMeter.php b/api/src/Controller/MeanPriceForSquaredMeter.php new file mode 100644 index 0000000000000000000000000000000000000000..6a2aad8879cd1decc573723e9191e33bd704a988 --- /dev/null +++ b/api/src/Controller/MeanPriceForSquaredMeter.php @@ -0,0 +1,58 @@ +em = $em; + } + + public function __invoke(Request $request) { + $query = " SELECT + TO_CHAR(valeurs_fonciere.date_mutation, 'YYYY') AS year, + TO_CHAR(valeurs_fonciere.date_mutation, 'MM') AS month, + AVG(valeurs_fonciere.valeur_fonciere / valeurs_fonciere.surface_reelle_bati) AS mean_metre_price + FROM + App:ValeursFonciere AS valeurs_fonciere + WHERE + valeurs_fonciere.surface_reelle_bati > 0 + AND + valeurs_fonciere.valeur_fonciere > 0 + AND + lower(valeurs_fonciere.nature_mutation) = 'vente' + GROUP BY + year, + month + ORDER BY + year, + month"; + $metre_prices = $this->em->createQuery($query)->getResult(); + foreach ($metre_prices as &$metre_price){ + $metre_price['year'] = intval($metre_price['year']); + $metre_price['month'] = intval($metre_price['month']); + $metre_price['mean_metre_price'] = floatval($metre_price['mean_metre_price']); + } + $res = new JsonResponse( + $metre_prices, + Response::HTTP_OK + ); + return $res; + } +} diff --git a/api/src/Controller/NumberOfSoldByParams.php b/api/src/Controller/NumberOfSoldByParams.php new file mode 100644 index 0000000000000000000000000000000000000000..9dc07e895a8f216bd9374e81d6289ceedad7cbe8 --- /dev/null +++ b/api/src/Controller/NumberOfSoldByParams.php @@ -0,0 +1,83 @@ +em = $em; + } + + private static $INTERVAL_DAY = 'day'; + private static $INTERVAL_MONTH = 'month'; + private static $INTERVAL_YEAR = 'year'; + + public function __invoke(Request $request, $interval, $start_date, $end_date) { + $selection = "TO_CHAR(valeurs_fonciere.date_mutation, 'YYYY') AS year"; + $groupByAndOrder = "year"; + if($interval == self::$INTERVAL_DAY){ + $selection .= ", + TO_CHAR(valeurs_fonciere.date_mutation, 'MM') AS month, + TO_CHAR(valeurs_fonciere.date_mutation, 'DD') AS day + "; + $groupByAndOrder .= ", + month, + day + "; + }elseif ($interval == self::$INTERVAL_MONTH){ + $selection .= ", + TO_CHAR(valeurs_fonciere.date_mutation, 'MM') AS month + "; + $groupByAndOrder .= ", + month + "; + } + $query = " SELECT + {$selection}, + COUNT(valeurs_fonciere.date_mutation) AS number_of_sold + FROM + App:ValeursFonciere AS valeurs_fonciere + WHERE + lower(valeurs_fonciere.nature_mutation) = 'vente' + AND + valeurs_fonciere.date_mutation >= TO_DATE('{$start_date}', 'DD-MM-YYYY') + AND + valeurs_fonciere.date_mutation <= TO_DATE('{$end_date}', 'DD-MM-YYYY') + GROUP BY + {$groupByAndOrder} + ORDER BY + {$groupByAndOrder}"; + $sold_houses = $this->em->createQuery($query)->getResult(); + foreach ($sold_houses as &$sold_house){ + $sold_house['year'] = intval($sold_house['year']); + if($interval == self::$INTERVAL_DAY){ + $sold_house['month'] = intval($sold_house['month']); + $sold_house['day'] = intval($sold_house['day']); + }elseif ($interval == self::$INTERVAL_MONTH){ + $sold_house['month'] = intval($sold_house['month']); + } + $sold_house['number_of_sold'] = floatval($sold_house['number_of_sold']); + } + $res = new JsonResponse( + $sold_houses, + Response::HTTP_OK + ); + return $res; + } +} diff --git a/api/src/Controller/NumberOfSoldByRegion.php b/api/src/Controller/NumberOfSoldByRegion.php new file mode 100644 index 0000000000000000000000000000000000000000..34f2b93a8d36371b9ea2904f9f1d3a8632450be6 --- /dev/null +++ b/api/src/Controller/NumberOfSoldByRegion.php @@ -0,0 +1,95 @@ +em = $em; + } + + private static $REGIONS = [ + 'Auvergne-Rhône-Alpes' => [3,15,43,63,1,7,26,38,42,69,73,74], + 'Normandie' => [14,50,61,27,76], + 'Bourgogne-Franche-Comté' => [21,58,71,89,25,39,70,90], + 'Bretagne' => [22,29,35,56], + 'Centre-Val de Loire' => [18,28,36,37,41,45], + 'Grand Est' => [8,10,51,52,54,55,57,88,67,68], + 'Ile-de-France' => [75,77,78,91,92,93,94,95], + 'Occitanie' => [11,30,34,48,66,9,12,31,32,46,65,81,82], + 'Hauts-de-France' => [59,62,2,60,80], + 'Pays de la Loire' => [44,49,53,72,85], + 'Nouvelle-Aquitaine' => [16,17,79,86,19,23,87,24,33,40,47,64], + 'Provence-Alpes-Côte-d\'Azur' => [4,5,6,13,83,84], + 'Guadeloupe' => [971], + 'Martinique' => [972], + 'Guyane' => [973], + 'La Réunion' => [974], + 'Mayotte' => [976] + ]; + + public function __invoke(Request $request) { + $query = " SELECT + valeurs_fonciere.code_departement AS code_departement, + COUNT(valeurs_fonciere.code_departement) AS count_by_departement + FROM + App:ValeursFonciere AS valeurs_fonciere + WHERE + valeurs_fonciere.code_departement > 0 + AND + lower(valeurs_fonciere.nature_mutation) = 'vente' + GROUP BY + valeurs_fonciere.code_departement"; + $counts_by_departement = $this->em->createQuery($query)->getResult(); + $counts_by_region = []; + foreach ($counts_by_departement as $count_by_departement){ + $count_by_departement['code_departement'] = intval($count_by_departement['code_departement']); + $count_by_departement['count_by_departement'] = intval($count_by_departement['count_by_departement']); + $region = $this->getRegionByDepartement($count_by_departement['code_departement']); + if($region){ + if(!key_exists($region, $counts_by_region)){ + $counts_by_region[$region] = 0; + } + $counts_by_region[$region] += $count_by_departement['count_by_departement']; + } + } + $counts_by_region_fixed = []; + foreach ($counts_by_region as $region_name => $count_by_region){ + $counts_by_region_fixed[] = [ + 'region' => $region_name, + 'count_by_region' => $count_by_region + ]; + } + $res = new JsonResponse( + $counts_by_region_fixed, + Response::HTTP_OK + ); + return $res; + } + + private function getRegionByDepartement($departement){ + foreach (self::$REGIONS as $region_name => $region_departements){ + if(array_search($departement,$region_departements,true) !== false){ + return $region_name; + } + } + return false; + } +} diff --git a/api/src/DataFixtures/AppFixtures.php b/api/src/DataFixtures/AppFixtures.php index 626221126a063a81fb893b61cadfe11cc7a90731..e10291e25a489d68316c00158a4b3f34eb28a110 100644 --- a/api/src/DataFixtures/AppFixtures.php +++ b/api/src/DataFixtures/AppFixtures.php @@ -41,25 +41,28 @@ class AppFixtures extends Fixture $numRecordsRead = 0; foreach ($records as $row) { + $codeTypeLocal = intval($row[35]); + if($codeTypeLocal == 1 || $codeTypeLocal == 2) { + // Créer un nouveau "Entity" + $valeursFonciere = new ValeursFonciere(); + $valeursFonciere->setDateMutation(\DateTime::createFromFormat("d/m/Y", $row[8])); + $valeursFonciere->setNatureMutation($row[9]); + $valeursFonciere->setValeurFonciere(floatval(str_replace(',', '.', $row[10]))); + $valeursFonciere->setCommune($row[17]); + $valeursFonciere->setCodeDepartement(intval($row[18])); + $valeursFonciere->setCodeCommune(intval($row[19])); + $valeursFonciere->setCodeTypeLocal(intval($row[35])); + $valeursFonciere->setTypeLocal($row[36]); + $valeursFonciere->setSurfaceReelleBati(floatval(str_replace(',', '.', $row[38]))); + $manager->persist($valeursFonciere); - // Créer un nouveau "Entity" - $valeursFonciere = new ValeursFonciere(); - $valeursFonciere->setDateMutation(\DateTime::createFromFormat("d/m/Y", $row[8])); - $valeursFonciere->setNatureMutation($row[9]); - $valeursFonciere->setValeurFonciere(floatval(str_replace(',', '.', $row[10]))); - $valeursFonciere->setCommune($row[17]); - $valeursFonciere->setCodeCommune(intval($row[19])); - $valeursFonciere->setCodeTypeLocal(intval($row[35])); - $valeursFonciere->setTypeLocal($row[36]); - $valeursFonciere->setSurfaceReelleBati(floatval(str_replace(',', '.', $row[38]))); - $manager->persist($valeursFonciere); + $numRecordsRead++; - $numRecordsRead++; - - // On fait un "Flush" chaque 100 records - if(($numRecordsRead % $recordsLimitByLoop) == 0){ - $manager->flush(); - $manager->clear(); + // On fait un "Flush" chaque 100 records + if (($numRecordsRead % $recordsLimitByLoop) == 0) { + $manager->flush(); + $manager->clear(); + } } } diff --git a/api/src/Entity/ValeursFonciere.php b/api/src/Entity/ValeursFonciere.php index 75564359244199e14b7c351098bb1e2ff5833ad6..9a654f7fcf42d5bc0a76ad540141a176cd1bef66 100644 --- a/api/src/Entity/ValeursFonciere.php +++ b/api/src/Entity/ValeursFonciere.php @@ -6,7 +6,163 @@ use ApiPlatform\Core\Annotation\ApiResource; use Doctrine\ORM\Mapping as ORM; /** - * @ApiResource() + * @ApiResource( + * collectionOperations={ + * "get","post", + * "meanpriceforsquaredmeter"={ + * "method"="GET", + * "openapi_context"={ + * "summary"="Gets the mean of squared meter price for the selected year and month", + * "produces"={ + * "application/json" + * }, + * "responses"={ + * "200"={ + * "description"="Mean of squared meter price", + * "content"= { + * "application/json"={ + * "schema"={ + * "type"="array", + * "items"={ + * "type"="object", + * "properties"={ + * "year"={ + * "type"="number" + * }, + * "month"={ + * "type"="number" + * }, + * "mean_metre_price"={ + * "type"="number" + * } + * } + * } + * } + * } + * } + * } + * } + * }, + * "route_name"="mean_price_for_squared_meter", + * "controller"=MeanPriceForSquaredMeter::class, + * "pagination_enabled"=false, + * }, + * "numberofsoldbyparams"={ + * "method"="GET", + * "openapi_context"={ + * "summary"="Gets the number of sold houses by passed parameters", + * "produces"={ + * "application/json" + * }, + * "parameters"={ + * { + * "name"="interval", + * "in"="path", + * "schema"={ + * "type"= "string", + * "enum"={ + * "day", + * "month", + * "year" + * } + * }, + * "description"="The start date of the duration", + * "required"=true + * }, + * { + * "name"="start_date", + * "in"="path", + * "schema"={ + * "type"= "string" + * }, + * "description"="The start date of the duration", + * "required"=true, + * "example"="01-01-2015" + * }, + * { + * "name"="end_date", + * "in"="path", + * "schema"={ + * "type"= "string" + * }, + * "description"="The end date of the duration", + * "required"=true, + * "example"="01-06-2015" + * } + * }, + + * "responses"={ + * "200"={ + * "description"="Mean of squared meter price", + * "content"= { + * "application/json"={ + * "schema"={ + * "type"="array", + * "required"={"year", "mean_metre_price"}, + * "items"={ + * "type"="object", + * "properties"={ + * "year"={ + * "type"="number" + * }, + * "month"={ + * "type"="number" + * }, + * "day"={ + * "type"="number" + * }, + * "mean_metre_price"={ + * "type"="number" + * } + * } + * } + * } + * } + * } + * } + * } + * }, + * "route_name"="number_of_sold_by_params", + * "controller"=NumberOfSoldByParams::class, + * "pagination_enabled"=false, + * }, + * "numberofsoldbyregion"={ + * "method"="GET", + * "openapi_context"={ + * "summary"="Gets the number of sold items by region", + * "produces"={ + * "application/json" + * }, + * "responses"={ + * "200"={ + * "description"="Number of sold items by region", + * "content"= { + * "application/json"={ + * "schema"={ + * "type"="array", + * "items"={ + * "type"="object", + * "properties"={ + * "region"={ + * "type"="string" + * }, + * "count_by_region"={ + * "type"="number" + * } + * } + * } + * } + * } + * } + * } + * } + * }, + * "route_name"="number_of_sold_by_region", + * "controller"=NumberOfSoldByRegion::class, + * "pagination_enabled"=false, + * } + * } + * ) * @ORM\Entity(repositoryClass="App\Repository\ValeursFonciereRepository") */ class ValeursFonciere @@ -43,6 +199,11 @@ class ValeursFonciere */ private $commune; + /** + * @ORM\Column(type="integer") + */ + private $code_departement; + /** * @ORM\Column(type="integer") */ @@ -111,6 +272,18 @@ class ValeursFonciere return $this; } + public function getCodeDepartement(): ?int + { + return $this->code_departement; + } + + public function setCodeDepartement(int $code_departement): self + { + $this->code_departement = $code_departement; + + return $this; + } + public function getCommune(): ?string { return $this->commune;