diff --git a/Semaine_07/Jour_01/Company_MySQL_001.mwb b/Semaine_07/Jour_01/Company_MySQL_001.mwb new file mode 100644 index 0000000..92d43c6 Binary files /dev/null and b/Semaine_07/Jour_01/Company_MySQL_001.mwb differ diff --git a/Semaine_07/Jour_01/Company_MySQL_001.mwb.bak b/Semaine_07/Jour_01/Company_MySQL_001.mwb.bak new file mode 100644 index 0000000..6a2fb32 Binary files /dev/null and b/Semaine_07/Jour_01/Company_MySQL_001.mwb.bak differ diff --git a/Semaine_07/Jour_01/docker-compose.yml b/Semaine_07/Jour_01/docker-compose.yml new file mode 100644 index 0000000..bf96c04 --- /dev/null +++ b/Semaine_07/Jour_01/docker-compose.yml @@ -0,0 +1,22 @@ +services: + mysql: + image: mysql:8.0 + container_name: mysql + environment: + MYSQL_ROOT_PASSWORD: rootpassword + MYSQL_DATABASE: company + MYSQL_USER: mysql_user + MYSQL_PASSWORD: Azerty123 + command: --log_bin_trust_function_creators=1 + ports: + - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + mysql_data: \ No newline at end of file diff --git a/Semaine_07/Jour_02/SQL_scripts.sql b/Semaine_07/Jour_02/SQL_scripts.sql new file mode 100644 index 0000000..2e9fffa --- /dev/null +++ b/Semaine_07/Jour_02/SQL_scripts.sql @@ -0,0 +1,208 @@ +USE company; + +SELECT * FROM country; + +SELECT first_name "Firstname", last_name "Lastname" from employee; + +select distinct department_ID from employee; + +select count(job_id) from employee; + +select count(distinct job_id) from employee; + +-- liste des employés embauchés avant le 01.01.1991 +select * from employee where hire_date < '1991-01-01'; + +-- FOnctions +-- -- mettre en majuscule +select upper(first_name) "Firstname" from employee; +-- -- mettre les 3 premières lettres des nom et prénom des employés +select substring(upper(first_name), 1, 3), substring(upper(last_name), 1, 3) +from employee; +-- -- prénom + nom + longueur totale du prénom+nom +select first_name, last_name, +char_length(first_name) + char_length(last_name) "Longueur totale" +from employee; +-- -- Qui gagne le plus dans l'entreprise +select first_name, last_name, salary +from employee order by salary desc limit 1; +select first_name, last_name, +salary >= (select max(salary) from employee) +from employee order by salary desc; +-- -- salaire min et max +select min(salary), max(salary), avg(salary), sum(salary) from employee; +-- -- les gens de l'IT qui gagnent moins de 5000 +select first_name, last_name, department_id, salary +from employee where department_id = 60 and salary > 5000; +-- -- les gens dans la vente (80) et le mercatique (20) +select first_name, last_name, salary +from employee where department_id in (20, 80); +-- -- salaire entre 5000 et 10000 +select first_name, last_name, salary +from employee where salary between 5000 and 10000; +-- -- avec une commission +select first_name, last_name, commission_pct +from employee where commission_pct != 0; +-- -- employés dont le nom de famille commance par "K" +select first_name, last_name +from employee where last_name like "K%"; +-- -- employés dont le prénom contient "an" +select first_name, last_name +from employee where first_name like "%an%"; +-- -- employés dont le prénom fait 4 lettres +select first_name, last_name +from employee where first_name like "____"; +-- -- REGEX : nom de famille commence par "S" ou "T" +select first_name, last_name +from employee where last_name regexp "^[ST]"; +-- -- le nombre de jour depuis l'embauche +select first_name, last_name, hire_date, +datediff(curdate(), hire_date) as nb_jours +from employee order by nb_jours; + +-- Exercice +-- -- les commerciaux gagnant plus de 9000 triés par commission décroissante +select first_name, last_name, salary, commission_pct +from employee +where department_id = 80 and salary > 9000 order by commission_pct desc; +-- -- les postes dont le salaire est au moins le double du salaire minimum +select first_name, last_name, salary +from employee where salary > (select min(salary) from employee) * 2 +order by salary asc; +-- -- les employés dont le prénom commence et fini par la même lettre +select first_name, last_name +from employee +where left(first_name, 1) = right(first_name, 1); +-- -- le top 5 des employés les mieux payés n'étant pas chefs +select first_name, last_name, salary, job_id +from employee +where employee_id not in (select manager_id from employee) +order by salary desc limit 10; + +-- COURS +-- -- Jointures +-- -- -- https://cartman34.fr/informatique/sgbd/differences-entre-inner-left-right-et-outer-join-en-sql.html +select first_name, last_name, department_name +from employee e +inner join department d +on e.department_id = d.department_id; +-- -- -- LEFT avec condition : les employés qui ne sont affectés à aucun départment +select e.first_name, e.last_name, d.department_name +from employee e +left join department d +on e.department_id = d.department_id +where d.department_id is null; +-- -- -- RIGHT avec condition : la liste des départments ne contenant aucun employé +select e.first_name, e.last_name, d.department_name +from employee e +right join department d +on e.department_id = d.department_id +where e.department_id is null; +-- -- -- FULL OUTER JOIN +select e.first_name, e.last_name, d.department_name +from employee e +left join department d +on e.department_id = d.department_id +union +select e.first_name, e.last_name, d.department_name +from employee e +right join department d +on e.department_id = d.department_id; +-- -- -- NATURAL JOIN -> INNER JOIN +select location_id, street_address, city, country_name +from location +natural join country; +-- -- -- EXEMPLE : nous voulons la liste des employés travaillant à Seattle +-- -- -- On doit faire EMPLOYEE -> DEPARTMENT -> LOCATION +select e.first_name, e.last_name, l.city +from employee e +join department d +on e.department_id = d.department_id +join location l +on d.location_id = l.location_id +where lower(l.city) = "seattle"; + +-- -- -- EXEMPLE : nous voulons la liste des employés travaillant aux USA +-- -- -- On doit faire EMPLOYEE -> DEPARTMENT -> LOCATION -> COUNTRY +select e.first_name, e.last_name, l.city +from employee e +join department d +on e.department_id = d.department_id +join location l +on d.location_id = l.location_id +join country c +on l.country_id = c.country_id +where lower(c.country_name) like "%united%states%"; +-- -- -- Prénom et nom de l'employé suivi du prénom de son chef +select e.first_name, e.last_name, m.first_name "Prénom chef", m.last_name "Nom chef" +from employee e +left join employee m +on m.employee_id = e.manager_id; +-- -- -- Nombre de gens dans chaque poste +select j.job_title, count(*) +from employee e +join job j +on e.job_id = j.job_id +group by j.job_id; +-- -- -- la liste des chefs et le nombre d'employés sous leurs ordres +-- -- -- et dont le nombre d'employés est d'au moins 5 +select m.first_name "Prénom chef", +m.last_name "Nom chef", +count(e.employee_id) as count +from employee e +join employee m +on m.employee_id = e.manager_id +group by e.manager_id +having count >= 5 +order by count desc; + +-- -- Sous-requêtes +-- -- -- Tous les employés gagnant plus que l'employé nommé "Bull" +select first_name, last_name, salary +from employee +where salary > +(select salary from employee where lower(last_name) = "Bull"); +-- -- -- Tous les employés ayant pour chef un prénommé "Payam" +-- -- -- JOINTURE +select e.first_name, e.last_name, m.first_name "Prénom chef", m.last_name "Nom chef" +from employee e +left join employee m +on m.employee_id = e.manager_id +where lower(m.first_name) = "payam"; +-- -- -- Tous les employés ayant pour chef un prénommé "Payam" +-- -- -- SOUS-REQUETE +select first_name, last_name +from employee +where manager_id = +(select employee_id from employee where lower(first_name) = "payam"); +-- -- -- Les employés dont le salaire est compris entre le plus petit et 3000 +select first_name, last_name, salary +from employee +where salary +between (select min(salary) from employee) and 3000 +order by salary asc; +-- -- -- les employés dont le chef bosse aux USA +select first_name, last_name +from employee +where manager_id in +( +select employee_id from employee where department_id in + ( + select department_id from department where location_id in + ( + select location_id from location where lower(country_id) = "us" + ) + ) +); +-- -- Création de "Vues" +-- -- Grosso modo, c'est une requête pré-enregistrée +create view `seattle_employees` as +select e.first_name, e.last_name, e.salary, d.department_name, l.city +from employee e +join department d +on e.department_id = d.department_id +join location l +on d.location_id = l.location_id +where lower(l.city) = "seattle"; + +select * from seattle_employees; \ No newline at end of file diff --git a/Semaine_07/Jour_02/SQL_transactions.sql b/Semaine_07/Jour_02/SQL_transactions.sql new file mode 100644 index 0000000..ec23c27 --- /dev/null +++ b/Semaine_07/Jour_02/SQL_transactions.sql @@ -0,0 +1,38 @@ +-- Création de "Transactions" +-- -- ACID : Atomique, Cohérence, Isolation, Durabilité +-- -- minimum deux requêtes, et si l'une échoue, +-- -- la BDD revient à l'état initial. Toutes passent sinon aucune + +-- -- -- On veut transférer un employé vers un autre département +-- -- -- avec un nouveau job, et archiver son poste dans job_history + +START transaction; + +-- -- -- 1. Archivage + +insert into job_history ( + employee_id, + start_date, + end_date, + job_id, + department_id +) +select + employee_id, + e.hire_date, + current_date, + e.job_id, + e.department_id +from employee e +where e.employee_id = 101; + +-- -- -- 2. Mettre à jour le job et département + +update employee +set + job_id = "SA_REP", + department_id = 80, + hire_date = current_date +where employee_id = 101; + +COMMIT; \ No newline at end of file diff --git a/Semaine_07/Jour_02/hr_data.sql b/Semaine_07/Jour_02/hr_data.sql new file mode 100644 index 0000000..be39549 --- /dev/null +++ b/Semaine_07/Jour_02/hr_data.sql @@ -0,0 +1,241 @@ +use company; +SET FOREIGN_KEY_CHECKS = 0; + +-- vide les tables et remet les compteurs a zero +truncate table job_history; +truncate table employee; +truncate table job; +truncate table department; +truncate table location; +truncate table country; +truncate table region; + +-- drop et truncate + +INSERT INTO region (REGION_ID, REGION_NAME) VALUES +('1', 'Europe'), +('2', 'America'), +('3', 'Asia'), +('4', 'Middle East and Africa'); + +INSERT INTO country (COUNTRY_ID, COUNTRY_NAME, REGION_ID) VALUES +('AR', 'Argentina', '2'), +('AU', 'Australia', '3'), +('BE', 'Belgium', '1'), +('BR', 'Brazil', '2'), +('CA', 'Canada', '2'), +('CH', 'Switzerland', '1'), +('CN', 'China', '3'), +('DE', 'Germany', '1'), +('DK', 'Denmark', '1'), +('EG', 'Egypt', '4'), +('FR', 'France', '1'), +('HK', 'HongKong', '3'), +('IL', 'Israel', '4'), +('IN', 'India', '3'), +('IT', 'Italy', '1'), +('JP', 'Japan', '3'), +('KW', 'Kuwait', '4'), +('MX', 'Mexico', '2'), +('NG', 'Nigeria', '4'), +('NL', 'Netherlands', '1'), +('SG', 'Singapore', '3'), +('UK', 'United Kingdom', '1'), +('US', 'United States of America', '2'), +('ZM', 'Zambia', '4'), +('ZW', 'Zimbabwe', '4'); + +INSERT INTO location (LOCATION_ID, STREET_ADDRESS, POSTAL_CODE, CITY, STATE_PROVINCE, COUNTRY_ID) VALUES +('1000', '1297 Via Cola di Rie', '989', 'Roma', '', 'IT'), +('1100', '93091 Calle della Testa', '10934', 'Venice', '', 'IT'), +('1200', '2017 Shinjuku-ku', '1689', 'Tokyo', 'Tokyo Prefecture', 'JP'), +('1300', '9450 Kamiya-cho', '6823', 'Hiroshima', '', 'JP'), +('1400', '2014 Jabberwocky Rd', '26192', 'Southlake', 'Texas', 'US'), +('1500', '2011 Interiors Blvd', '99236', 'South San Francisco', 'California', 'US'), +('1600', '2007 Zagora St', '50090', 'South Brunswick', 'New Jersey', 'US'), +('1700', '2004 Charade Rd', '98199', 'Seattle', 'Washington', 'US'), +('1800', '147 Spadina Ave', 'M5V 2L7', 'Toronto', 'Ontario', 'CA'), +('1900', '6092 Boxwood St', 'YSW 9T2', 'Whitehorse', 'Yukon', 'CA'), +('2000', '40-5-12 Laogianggen', '190518', 'Beijing', '', 'CN'), +('2100', '1298 Vileparle (E)', '490231', 'Bombay', 'Maharashtra', 'IN'), +('2200', '12-98 Victoria Street', '2901', 'Sydney', 'New South Wales', 'AU'), +('2300', '198 Clementi North', '540198', 'Singapore', '', 'SG'), +('2400', '8204 Arthur St', '', 'London', '', 'UK'), +('2500', '"Magdalen Centre', ' The Oxford ', 'OX9 9ZB', 'Oxford', 'UK'), +('2600', '9702 Chester Road', '9629850293', 'Stretford', 'Manchester', 'UK'), +('2700', 'Schwanthalerstr. 7031', '80925', 'Munich', 'Bavaria', 'DE'), +('2800', 'Rua Frei Caneca 1360', '01307-002', 'Sao Paulo', 'Sao Paulo', 'BR'), +('2900', '20 Rue des Corps-Saints', '1730', 'Geneva', 'Geneve', 'CH'), +('3000', 'Murtenstrasse 921', '3095', 'Bern', 'BE', 'CH'), +('3100', 'Pieter Breughelstraat 837', '3029SK', 'Utrecht', 'Utrecht', 'NL'), +('3200', 'Mariano Escobedo 9991', '11932', 'Mexico City', '"Distrito Federal', 'MX'); + +INSERT INTO department (DEPARTMENT_ID , DEPARTMENT_NAME , MANAGER_ID, LOCATION_ID) VALUES +('10', 'Administration', '200', '1700'), +('20', 'Marketing', '201', '1800'), +('30', 'Purchasing', '114', '1700'), +('40', 'Human Resources', '203', '2400'), +('50', 'Shipping', '121', '1500'), +('60', 'IT', '103', '1400'), +('70', 'Public Relations', '204', '2700'), +('80', 'Sales', '145', '2500'), +('90', 'Executive', '100', '1700'), +('100', 'Finance', '108', '1700'), +('110', 'Accounting', '205', '1700'), +('120', 'Treasury', '0', '1700'), +('130', 'Corporate Tax', '0', '1700'), +('140', 'Control And Credit', '0', '1700'), +('150', 'Shareholder Services', '0', '1700'), +('160', 'Benefits', '0', '1700'), +('170', 'Manufacturing', '0', '1700'), +('180', 'Construction', '0', '1700'), +('190', 'Contracting', '0', '1700'), +('200', 'Operations', '0', '1700'), +('210', 'IT Support', '0', '1700'), +('220', 'NOC', '0', '1700'), +('230', 'IT Helpdesk', '0', '1700'), +('240', 'Government Sales', '0', '1700'), +('250', 'Retail Sales', '0', '1700'), +('260', 'Recruiting', '0', '1700'), +('270', 'Payroll', '0', '1700'); + +INSERT INTO job (JOB_ID, JOB_TITLE, MIN_SALARY, MAX_SALARY) VALUES +('AD_PRES', 'President', '20000', '40000'), +('AD_VP', 'Administration Vice President', '15000', '30000'), +('AD_ASST', 'Administration Assistant', '3000', '6000'), +('FI_MGR', 'Finance Manager', '8200', '16000'), +('FI_ACCOUNT', 'Accountant', '4200', '9000'), +('AC_MGR', 'Accounting Manager', '8200', '16000'), +('AC_ACCOUNT', 'Public Accountant', '4200', '9000'), +('SA_MAN', 'Sales Manager', '10000', '20000'), +('SA_REP', 'Sales Representative', '6000', '12000'), +('PU_MAN', 'Purchasing Manager', '8000', '15000'), +('PU_CLERK', 'Purchasing Clerk', '2500', '5500'), +('ST_MAN', 'Stock Manager', '5500', '8500'), +('ST_CLERK', 'Stock Clerk', '2000', '5000'), +('SH_CLERK', 'Shipping Clerk', '2500', '5500'), +('IT_PROG', 'Programmer', '4000', '10000'), +('MK_MAN', 'Marketing Manager', '9000', '15000'), +('MK_REP', 'Marketing Representative', '4000', '9000'), +('HR_REP', 'Human Resources Representative', '4000', '9000'), +('PR_REP', 'Public Relations Representative', '4500', '10500'); + +INSERT INTO employee (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB_ID, SALARY, COMMISSION_PCT, MANAGER_ID, DEPARTMENT_ID) VALUES +('100', 'Steven', 'King', 'SKING', '515.123.4567', '1987-06-20', 'AD_PRES', '24000.00', '0.00', '0', '90'), +('101', 'Neena', 'Kochhar', 'NKOCHHAR', '515.123.4568','1987-06-20' , 'AD_VP', '17000.00', '0.00', '100', '90'), +('102', 'Lex', 'De Haan', 'LDEHAAN', '515.123.4569', '1987-06-20', 'AD_VP', '17000.00', '0.00', '100', '90'), +('103', 'Alexander', 'Hunold', 'AHUNOLD', '590.423.4567', '1987-06-20', 'IT_PROG', '9000.00', '0.00', '102', '60'), +('104', 'Bruce', 'Ernst', 'BERNST', '590.423.4568', '1987-06-21', 'IT_PROG', '6000.00', '0.00', '103', '60'), +('105', 'David', 'Austin', 'DAUSTIN', '590.423.4569', '1987-06-22', 'IT_PROG', '4800.00', '0.00', '103', '60'), +('106', 'Valli', 'Pataballa', 'VPATABAL', '590.423.4560', '1987-06-23', 'IT_PROG', '4800.00', '0.00', '103', '60'), +('107', 'Diana', 'Lorentz', 'DLORENTZ', '590.423.5567', '1987-06-24', 'IT_PROG', '4200.00', '0.00', '103', '60'), +('108', 'Nancy', 'Greenberg', 'NGREENBE', '515.124.4569', '1987-06-25', 'FI_MGR', '12000.00', '0.00', '101', '100'), +('109', 'Daniel', 'Faviet', 'DFAVIET', '515.124.4169', '1987-06-26', 'FI_ACCOUNT', '9000.00', '0.00', '108', '100'), +('110', 'John', 'Chen', 'JCHEN', '515.124.4269', '1987-06-27', 'FI_ACCOUNT', '8200.00', '0.00', '108', '100'), +('111', 'Ismael', 'Sciarra', 'ISCIARRA', '515.124.4369', '1987-06-28', 'FI_ACCOUNT', '7700.00', '0.00', '108', '100'), +('112', 'Jose Manuel', 'Urman', 'JMURMAN', '515.124.4469', '1987-06-29', 'FI_ACCOUNT', '7800.00', '0.00', '108', '100'), +('113', 'Luis', 'Popp', 'LPOPP', '515.124.4567', '1987-06-30', 'FI_ACCOUNT', '6900.00', '0.00', '108', '100'), +('114', 'Den', 'Raphaely', 'DRAPHEAL', '515.127.4561', '1987-07-01', 'PU_MAN', '11000.00', '0.00', '100', '30'), +('115', 'Alexander', 'Khoo', 'AKHOO', '515.127.4562', '1987-07-02', 'PU_CLERK', '3100.00', '0.00', '114', '30'), +('116', 'Shelli', 'Baida', 'SBAIDA', '515.127.4563', '1987-07-03', 'PU_CLERK', '2900.00', '0.00', '114', '30'), +('117', 'Sigal', 'Tobias', 'STOBIAS', '515.127.4564', '1987-07-04', 'PU_CLERK', '2800.00', '0.00', '114', '30'), +('118', 'Guy', 'Himuro', 'GHIMURO', '515.127.4565', '1987-07-05', 'PU_CLERK', '2600.00', '0.00', '114', '30'), +('119', 'Karen', 'Colmenares', 'KCOLMENA', '515.127.4566', '1987-07-06', 'PU_CLERK', '2500.00', '0.00', '114', '30'), +('120', 'Matthew', 'Weiss', 'MWEISS', '650.123.1234', '1987-07-07', 'ST_MAN', '8000.00', '0.00', '100', '50'), +('121', 'Adam', 'Fripp', 'AFRIPP', '650.123.2234', '1987-07-08', 'ST_MAN', '8200.00', '0.00', '100', '50'), +('122', 'Payam', 'Kaufling', 'PKAUFLIN', '650.123.3234', '1987-07-09', 'ST_MAN', '7900.00', '0.00', '100', '50'), +('123', 'Shanta', 'Vollman', 'SVOLLMAN', '650.123.4234', '1987-07-10', 'ST_MAN', '6500.00', '0.00', '100', '50'), +('124', 'Kevin', 'Mourgos', 'KMOURGOS', '650.123.5234', '1987-07-11', 'ST_MAN', '5800.00', '0.00', '100', '50'), +('125', 'Julia', 'Nayer', 'JNAYER', '650.124.1214', '1987-07-12', 'ST_CLERK', '3200.00', '0.00', '120', '50'), +('126', 'Irene', 'Mikkilineni', 'IMIKKILI', '650.124.1224', '1987-07-13', 'ST_CLERK', '2700.00', '0.00', '120', '50'), +('127', 'James', 'Landry', 'JLANDRY', '650.124.1334', '1987-07-14', 'ST_CLERK', '2400.00', '0.00', '120', '50'), +('128', 'Steven', 'Markle', 'SMARKLE', '650.124.1434', '1987-07-15', 'ST_CLERK', '2200.00', '0.00', '120', '50'), +('129', 'Laura', 'Bissot', 'LBISSOT', '650.124.5234', '1987-07-16', 'ST_CLERK', '3300.00', '0.00', '121', '50'), +('130', 'Mozhe', 'Atkinson', 'MATKINSO', '650.124.6234', '1987-07-17', 'ST_CLERK', '2800.00', '0.00', '121', '50'), +('131', 'James', 'Marlow', 'JAMRLOW', '650.124.7234', '1987-07-18', 'ST_CLERK', '2500.00', '0.00', '121', '50'), +('132', 'TJ', 'Olson', 'TJOLSON', '650.124.8234', '1987-07-19', 'ST_CLERK', '2100.00', '0.00', '121', '50'), +('133', 'Jason', 'Mallin', 'JMALLIN', '650.127.1934', '1987-07-20', 'ST_CLERK', '3300.00', '0.00', '122', '50'), +('134', 'Michael', 'Rogers', 'MROGERS', '650.127.1834', '1987-07-21', 'ST_CLERK', '2900.00', '0.00', '122', '50'), +('135', 'Ki', 'Gee', 'KGEE', '650.127.1734', '1987-07-22', 'ST_CLERK', '2400.00', '0.00', '122', '50'), +('136', 'Hazel', 'Philtanker', 'HPHILTAN', '650.127.1634', '1987-07-23', 'ST_CLERK', '2200.00', '0.00', '122', '50'), +('137', 'Renske', 'Ladwig', 'RLADWIG', '650.121.1234', '1987-07-24', 'ST_CLERK', '3600.00', '0.00', '123', '50'), +('138', 'Stephen', 'Stiles', 'SSTILES', '650.121.2034', '1987-07-25', 'ST_CLERK', '3200.00', '0.00', '123', '50'), +('139', 'John', 'Seo', 'JSEO', '650.121.2019', '1987-07-26', 'ST_CLERK', '2700.00', '0.00', '123', '50'), +('140', 'Joshua', 'Patel', 'JPATEL', '650.121.1834', '1987-07-27', 'ST_CLERK', '2500.00', '0.00', '123', '50'), +('141', 'Trenna', 'Rajs', 'TRAJS', '650.121.8009', '1987-07-28', 'ST_CLERK', '3500.00', '0.00', '124', '50'), +('142', 'Curtis', 'Davies', 'CDAVIES', '650.121.2994', '1987-07-29', 'ST_CLERK', '3100.00', '0.00', '124', '50'), +('143', 'Randall', 'Matos', 'RMATOS', '650.121.2874', '1987-07-30', 'ST_CLERK', '2600.00', '0.00', '124', '50'), +('144', 'Peter', 'Vargas', 'PVARGAS', '650.121.2004', '1987-07-31', 'ST_CLERK', '2500.00', '0.00', '124', '50'), +('145', 'John', 'Russell', 'JRUSSEL', '011.44.1344.429268', '1987-08-01', 'SA_MAN', '14000.00', '0.40', '100', '80'), +('146', 'Karen', 'Partners', 'KPARTNER', '011.44.1344.467268', '1987-08-02', 'SA_MAN', '13500.00', '0.30', '100', '80'), +('147', 'Alberto', 'Errazuriz', 'AERRAZUR', '011.44.1344.429278', '1987-08-03', 'SA_MAN', '12000.00', '0.30', '100', '80'), +('148', 'Gerald', 'Cambrault', 'GCAMBRAU', '011.44.1344.619268', '1987-08-04', 'SA_MAN', '11000.00', '0.30', '100', '80'), +('149', 'Eleni', 'Zlotkey', 'EZLOTKEY', '011.44.1344.429018', '1987-08-05', 'SA_MAN', '10500.00', '0.20', '100', '80'), +('150', 'Peter', 'Tucker', 'PTUCKER', '011.44.1344.129268', '1987-08-06', 'SA_REP', '10000.00', '0.30', '145', '80'), +('151', 'David', 'Bernstein', 'DBERNSTE', '011.44.1344.345268', '1987-08-07', 'SA_REP', '9500.00', '0.25', '145', '80'), +('152', 'Peter', 'Hall', 'PHALL', '011.44.1344.478968', '1987-08-08', 'SA_REP', '9000.00', '0.25', '145', '80'), +('153', 'Christopher', 'Olsen', 'COLSEN', '011.44.1344.498718', '1987-08-09', 'SA_REP', '8000.00', '0.20', '145', '80'), +('154', 'Nanette', 'Cambrault', 'NCAMBRAU', '011.44.1344.987668', '1987-08-10', 'SA_REP', '7500.00', '0.20', '145', '80'), +('155', 'Oliver', 'Tuvault', 'OTUVAULT', '011.44.1344.486508', '1987-08-11', 'SA_REP', '7000.00', '0.15', '145', '80'), +('156', 'Janette', 'King', 'JKING', '011.44.1345.429268', '1987-08-12', 'SA_REP', '10000.00', '0.35', '146', '80'), +('157', 'Patrick', 'Sully', 'PSULLY', '011.44.1345.929268', '1987-08-13', 'SA_REP', '9500.00', '0.35', '146', '80'), +('158', 'Allan', 'McEwen', 'AMCEWEN', '011.44.1345.829268', '1987-08-14', 'SA_REP', '9000.00', '0.35', '146', '80'), +('159', 'Lindsey', 'Smith', 'LSMITH', '011.44.1345.729268', '1987-08-15', 'SA_REP', '8000.00', '0.30', '146', '80'), +('160', 'Louise', 'Doran', 'LDORAN', '011.44.1345.629268', '1987-08-16', 'SA_REP', '7500.00', '0.30', '146', '80'), +('161', 'Sarath', 'Sewall', 'SSEWALL', '011.44.1345.529268', '1987-08-17', 'SA_REP', '7000.00', '0.25', '146', '80'), +('162', 'Clara', 'Vishney', 'CVISHNEY', '011.44.1346.129268', '1987-08-18', 'SA_REP', '10500.00', '0.25', '147', '80'), +('163', 'Danielle', 'Greene', 'DGREENE', '011.44.1346.229268', '1987-08-19', 'SA_REP', '9500.00', '0.15', '147', '80'), +('164', 'Mattea', 'Marvins', 'MMARVINS', '011.44.1346.329268', '1987-08-20', 'SA_REP', '7200.00', '0.10', '147', '80'), +('165', 'David', 'Lee', 'DLEE', '011.44.1346.529268', '1987-08-21', 'SA_REP', '6800.00', '0.10', '147', '80'), +('166', 'Sundar', 'Ande', 'SANDE', '011.44.1346.629268', '1987-08-22', 'SA_REP', '6400.00', '0.10', '147', '80'), +('167', 'Amit', 'Banda', 'ABANDA', '011.44.1346.729268', '1987-08-23', 'SA_REP', '6200.00', '0.10', '147', '80'), +('168', 'Lisa', 'Ozer', 'LOZER', '011.44.1343.929268', '1987-08-24', 'SA_REP', '11500.00', '0.25', '148', '80'), +('169', 'Harrison', 'Bloom', 'HBLOOM', '011.44.1343.829268', '1987-08-25', 'SA_REP', '10000.00', '0.20', '148', '80'), +('170', 'Tayler', 'Fox', 'TFOX', '011.44.1343.729268', '1987-08-26', 'SA_REP', '9600.00', '0.20', '148', '80'), +('171', 'William', 'Smith', 'WSMITH', '011.44.1343.629268', '1987-08-27', 'SA_REP', '7400.00', '0.15', '148', '80'), +('172', 'Elizabeth', 'Bates', 'EBATES', '011.44.1343.529268', '1987-08-28', 'SA_REP', '7300.00', '0.15', '148', '80'), +('173', 'Sundita', 'Kumar', 'SKUMAR', '011.44.1343.329268', '1987-08-29', 'SA_REP', '6100.00', '0.10', '148', '80'), +('174', 'Ellen', 'Abel', 'EABEL', '011.44.1644.429267', '1987-08-30', 'SA_REP', '11000.00', '0.30', '149', '80'), +('175', 'Alyssa', 'Hutton', 'AHUTTON', '011.44.1644.429266', '1987-08-31', 'SA_REP', '8800.00', '0.25', '149', '80'), +('176', 'Jonathon', 'Taylor', 'JTAYLOR', '011.44.1644.429265', '1987-09-01', 'SA_REP', '8600.00', '0.20', '149', '80'), +('177', 'Jack', 'Livingston', 'JLIVINGS', '011.44.1644.429264', '1987-09-02', 'SA_REP', '8400.00', '0.20', '149', '80'), +('178', 'Kimberely', 'Grant', 'KGRANT', '011.44.1644.429263', '1987-09-03', 'SA_REP', '7000.00', '0.15', '149', '0'), +('179', 'Charles', 'Johnson', 'CJOHNSON', '011.44.1644.429262', '1987-09-04', 'SA_REP', '6200.00', '0.10', '149', '80'), +('180', 'Winston', 'Taylor', 'WTAYLOR', '650.507.9876', '1987-09-05', 'SH_CLERK', '3200.00', '0.00', '120', '50'), +('181', 'Jean', 'Fleaur', 'JFLEAUR', '650.507.9877', '1987-09-06', 'SH_CLERK', '3100.00', '0.00', '120', '50'), +('182', 'Martha', 'Sullivan', 'MSULLIVA', '650.507.9878', '1987-09-07', 'SH_CLERK', '2500.00', '0.00', '120', '50'), +('183', 'Girard', 'Geoni', 'GGEONI', '650.507.9879', '1987-09-08', 'SH_CLERK', '2800.00', '0.00', '120', '50'), +('184', 'Nandita', 'Sarchand', 'NSARCHAN', '650.509.1876', '1987-09-09', 'SH_CLERK', '4200.00', '0.00', '121', '50'), +('185', 'Alexis', 'Bull', 'ABULL', '650.509.2876', '1987-09-10', 'SH_CLERK', '4100.00', '0.00', '121', '50'), +('186', 'Julia', 'Dellinger', 'JDELLING', '650.509.3876', '1987-09-11', 'SH_CLERK', '3400.00', '0.00', '121', '50'), +('187', 'Anthony', 'Cabrio', 'ACABRIO', '650.509.4876', '1987-09-12', 'SH_CLERK', '3000.00', '0.00', '121', '50'), +('188', 'Kelly', 'Chung', 'KCHUNG', '650.505.1876', '1987-09-13', 'SH_CLERK', '3800.00', '0.00', '122', '50'), +('189', 'Jennifer', 'Dilly', 'JDILLY', '650.505.2876', '1987-09-14', 'SH_CLERK', '3600.00', '0.00', '122', '50'), +('190', 'Timothy', 'Gates', 'TGATES', '650.505.3876', '1987-09-15', 'SH_CLERK', '2900.00', '0.00', '122', '50'), +('191', 'Randall', 'Perkins', 'RPERKINS', '650.505.4876', '1987-09-16', 'SH_CLERK', '2500.00', '0.00', '122', '50'), +('192', 'Sarah', 'Bell', 'SBELL', '650.501.1876', '1987-09-17', 'SH_CLERK', '4000.00', '0.00', '123', '50'), +('193', 'Britney', 'Everett', 'BEVERETT', '650.501.2876', '1987-09-18', 'SH_CLERK', '3900.00', '0.00', '123', '50'), +('194', 'Samuel', 'McCain', 'SMCCAIN', '650.501.3876', '1987-09-19', 'SH_CLERK', '3200.00', '0.00', '123', '50'), +('195', 'Vance', 'Jones', 'VJONES', '650.501.4876', '1987-09-20', 'SH_CLERK', '2800.00', '0.00', '123', '50'), +('196', 'Alana', 'Walsh', 'AWALSH', '650.507.9811', '1987-09-21', 'SH_CLERK', '3100.00', '0.00', '124', '50'), +('197', 'Kevin', 'Feeney', 'KFEENEY', '650.507.9822', '1987-09-22', 'SH_CLERK', '3000.00', '0.00', '124', '50'), +('198', 'Donald', 'OConnell', 'DOCONNEL', '650.507.9833', '1987-09-23', 'SH_CLERK', '2600.00', '0.00', '124', '50'), +('199', 'Douglas', 'Grant', 'DGRANT', '650.507.9844', '1987-09-24', 'SH_CLERK', '2600.00', '0.00', '124', '50'), +('200', 'Jennifer', 'Whalen', 'JWHALEN', '515.123.4444', '1987-09-25', 'AD_ASST', '4400.00', '0.00', '101', '10'), +('201', 'Michael', 'Hartstein', 'MHARTSTE', '515.123.5555', '1987-09-26', 'MK_MAN', '13000.00', '0.00', '100', '20'), +('202', 'Pat', 'Fay', 'PFAY', '603.123.6666', '1987-09-27', 'MK_REP', '6000.00', '0.00', '201', '20'), +('203', 'Susan', 'Mavris', 'SMAVRIS', '515.123.7777', '1987-09-28', 'HR_REP', '6500.00', '0.00', '101', '40'), +('204', 'Hermann', 'Baer', 'HBAER', '515.123.8888', '1987-09-29', 'PR_REP', '10000.00', '0.00', '101', '70'), +('205', 'Shelley', 'Higgins', 'SHIGGINS', '515.123.8080', '1987-09-30', 'AC_MGR', '12000.00', '0.00', '101', '110'), +('206', 'William', 'Gietz', 'WGIETZ', '515.123.8181', '1987-10-01', 'AC_ACCOUNT', '8300.00', '0.00', '205', '110'); + +INSERT INTO job_history (EMPLOYEE_ID, START_DATE, END_DATE, JOB_ID, DEPARTMENT_ID) VALUES +('102', '1993-01-13', '1998-07-24', 'IT_PROG', '60'), +('101', '1989-09-21', '1993-10-27', 'AC_ACCOUNT', '110'), +('201', '1996-02-17', '1999-12-19', 'MK_REP', '20'), +('114', '1998-03-24', '1999-12-31', 'ST_CLERK', '50'), +('122', '1999-01-01', '1999-12-31', 'ST_CLERK', '50'), +('200', '1987-09-17', '1993-06-17', 'AD_ASST', '90'), +('176', '1998-03-24', '1998-12-31', 'SA_REP', '80'); + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/Semaine_07/Jour_02/sql_joins.jpg b/Semaine_07/Jour_02/sql_joins.jpg new file mode 100644 index 0000000..299dcc4 Binary files /dev/null and b/Semaine_07/Jour_02/sql_joins.jpg differ diff --git a/Semaine_07/Jour_03/BDD_Sujet_B.pdf b/Semaine_07/Jour_03/BDD_Sujet_B.pdf new file mode 100644 index 0000000..01e81f9 Binary files /dev/null and b/Semaine_07/Jour_03/BDD_Sujet_B.pdf differ diff --git a/Semaine_07/Jour_03/B_Boiché_Gauvain.mwb b/Semaine_07/Jour_03/B_Boiché_Gauvain.mwb new file mode 100644 index 0000000..ce65b1a Binary files /dev/null and b/Semaine_07/Jour_03/B_Boiché_Gauvain.mwb differ diff --git a/Semaine_07/Jour_03/B_Boiché_Gauvain.mwb.bak b/Semaine_07/Jour_03/B_Boiché_Gauvain.mwb.bak new file mode 100644 index 0000000..c982b2b Binary files /dev/null and b/Semaine_07/Jour_03/B_Boiché_Gauvain.mwb.bak differ diff --git a/Semaine_07/Jour_03/python/connector/.env b/Semaine_07/Jour_03/python/connector/.env new file mode 100644 index 0000000..3cbed32 --- /dev/null +++ b/Semaine_07/Jour_03/python/connector/.env @@ -0,0 +1,4 @@ +DB_HOST=localhost +DB_USER=mysql_user +DB_PASSWORD=Azerty123 +DB_NAME=company \ No newline at end of file diff --git a/Semaine_07/Jour_03/python/connector/.python-version b/Semaine_07/Jour_03/python/connector/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/Semaine_07/Jour_03/python/connector/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/Semaine_07/Jour_03/python/connector/README.md b/Semaine_07/Jour_03/python/connector/README.md new file mode 100644 index 0000000..e69de29 diff --git a/Semaine_07/Jour_03/python/connector/main.py b/Semaine_07/Jour_03/python/connector/main.py new file mode 100644 index 0000000..d5912e9 --- /dev/null +++ b/Semaine_07/Jour_03/python/connector/main.py @@ -0,0 +1,115 @@ +import os + +from dotenv import load_dotenv +from mysql.connector import Error, connect, errorcode + +load_dotenv() + +db_host = os.environ.get("DB_HOST") +db_user = os.environ.get("DB_USER") +db_password = os.environ.get("DB_PASSWORD") +db_name = os.environ.get("DB_NAME") + +try: + connection = connect( + host= db_host, + user= db_user, + password= db_password, + database= db_name + ) + + cursor = connection.cursor() + ### Exemple 1 : commande + # cursor.execute("SELECT * FROM employee LIMIT 5") + # results = cursor.fetchone() + # print(results) + + # ### Exemple 2 : requête complète comme variable + # query = """ + # CREATE TABLE IF NOT EXISTS movies ( + # id INT AUTO_INCREMENT PRIMARY KEY, + # title VARCHAR(100), + # release_year YEAR(4), + # genre VARCHAR(100), + # collection_in_mil DECIMAL(4,1) + # ) + # """ + # cursor.execute(query) + # # CIUD : Create, Insert, Update, Delete => Commit + # connection.commit() + + ### Exemple 3 : multiple requête + insert_query = """ + INSERT INTO movies (title, release_year, genre, collection_in_mil) + VALUES + ("Forrest Gump", 1994, "Drama", 330.2), + ("INception", 2010, "Aventure", 293.7), + ("Titanic", 1995, "Drama", 530.4) + """ + + create_query = """ + CREATE TABLE IF NOT EXISTS reviewers ( + id INT AUTO_INCREMENT PRIMARY KEY, + firstname VARCHAR(100), + lastname VARCHAR(100) + ) + """ + + insert_query_2 = """ + INSERT INTO reviewers (firstname, lastname) + VALUES (%s, %s) + """ + + reviewers = [ + ("Jean", "FAIBLE"), + ("Alain", "TERIEUR"), + ("Tabatha", "CLOISON"), + ("Ahmed", "BIEN") + ] + + cursor.execute(insert_query) + cursor.execute(create_query) + cursor.executemany(insert_query_2, reviewers) + connection.commit() + + cursor.execute("SELECT * FROM movies") + # lire un par un + resultat = cursor.fetchone() + print(resultat) + while resultat is not None: + print(resultat) + resultat = cursor.fetchone() + + # utilisation de lot + cursor.execute("SELECT * FROM movies") + batch = cursor.fetchmany(size= 4) + while batch: + print("----") + for row in batch: + print(row) + batch = cursor.fetchmany(4) + + # Out of Memory + + insert_movies_query = """ + INSERT INTO movies (title, release_year, genre, collection_in_mil) + VALUES (%s, %s, %s, %s) + """ + + with open("movies.csv", "r") as fichier: + reader = csv.reader(fichier) + next(reader) + +except Error as error: + match error.errno: + case errorcode.ER_ACCESS_DENIED_ERROR: + print("Accès refusé. Vérifier USER et PASSWORD") + case errorcode.ER_BAD_DB_ERROR: + print("BDD n'existe pas") + case _: + print(f"Erreur {error}") +finally: + if cursor is not None: + cursor.close() + if connection is not None and connection.is_connected(): + connection.close() \ No newline at end of file diff --git a/Semaine_07/Jour_03/python/connector/pyproject.toml b/Semaine_07/Jour_03/python/connector/pyproject.toml new file mode 100644 index 0000000..5f870b9 --- /dev/null +++ b/Semaine_07/Jour_03/python/connector/pyproject.toml @@ -0,0 +1,10 @@ +[project] +name = "connector" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "mysql-connector-python>=9.6.0", + "python-dotenv>=1.2.2", +] diff --git a/Semaine_07/Jour_03/python/connector/uv.lock b/Semaine_07/Jour_03/python/connector/uv.lock new file mode 100644 index 0000000..edbfd32 --- /dev/null +++ b/Semaine_07/Jour_03/python/connector/uv.lock @@ -0,0 +1,51 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "connector" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "mysql-connector-python" }, + { name = "python-dotenv" }, +] + +[package.metadata] +requires-dist = [ + { name = "mysql-connector-python", specifier = ">=9.6.0" }, + { name = "python-dotenv", specifier = ">=1.2.2" }, +] + +[[package]] +name = "mysql-connector-python" +version = "9.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6e/c89babc7de3df01467d159854414659c885152579903a8220c8db02a3835/mysql_connector_python-9.6.0.tar.gz", hash = "sha256:c453bb55347174d87504b534246fb10c589daf5d057515bf615627198a3c7ef1", size = 12254999, upload-time = "2026-02-10T12:04:52.63Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d9/2a4b4d90b52f4241f0f71618cd4bd8779dd6d18db8058b0a4dd83ec0541c/mysql_connector_python-9.6.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9664e217c72dd6fb700f4c8512af90261f72d2f5d7c00c4e13e4c1e09bfa3d5e", size = 17585672, upload-time = "2026-02-10T12:03:52.955Z" }, + { url = "https://files.pythonhosted.org/packages/33/91/2495835733a054e716a17dc28404748b33f2dc1da1ae4396fb45574adf40/mysql_connector_python-9.6.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:1ed4b5c4761e5333035293e746683890e4ef2e818e515d14023fd80293bc31fa", size = 18452624, upload-time = "2026-02-10T12:03:56.153Z" }, + { url = "https://files.pythonhosted.org/packages/7a/69/e83abbbbf7f8eed855b5a5ff7285bc0afb1199418ac036c7691edf41e154/mysql_connector_python-9.6.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5095758dcb89a6bce2379f349da336c268c407129002b595c5dba82ce387e2a5", size = 34169154, upload-time = "2026-02-10T12:03:58.831Z" }, + { url = "https://files.pythonhosted.org/packages/82/44/67bb61c71f398fbc739d07e8dcadad94e2f655874cb32ae851454066bea0/mysql_connector_python-9.6.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4ae4e7780fad950a4f267dea5851048d160f5b71314a342cdbf30b154f1c74f7", size = 34542947, upload-time = "2026-02-10T12:04:02.408Z" }, + { url = "https://files.pythonhosted.org/packages/ba/39/994c4f7e9c59d3ca534a831d18442ac4c529865db20aeaa4fd94e2af5efd/mysql_connector_python-9.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:c180e0b4100d7402e03993bfac5c97d18e01d7ca9d198d742fffc245077f8ffe", size = 16515709, upload-time = "2026-02-10T12:04:04.924Z" }, + { url = "https://files.pythonhosted.org/packages/2f/58/9521aa678708ec6cebfd40524c14c3d151e4f29e3774e6086aa0a30d203b/mysql_connector_python-9.6.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e86e45a7b540ca09af8a18ecfa761e0cdeccfdb62818331614ec030ae44bfd26", size = 17585837, upload-time = "2026-02-10T12:04:07.004Z" }, + { url = "https://files.pythonhosted.org/packages/39/8d/b108f9bcce9780f6a1f91decb2af54defdaf845e237ddc42f2b4578f1cd7/mysql_connector_python-9.6.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:8d3e9252384e1b7f95b07020664f2673d9c29c5e95eeda2e048b3331e190b9d4", size = 18452844, upload-time = "2026-02-10T12:04:09.418Z" }, + { url = "https://files.pythonhosted.org/packages/d6/28/735cd93d16e76dc2feb4abb3f1229a1d9475af34d80c26712fec6abe1d70/mysql_connector_python-9.6.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:0fa18ead33cb699ea92005695077cef09aa494eebf51164ee30c891c3eaea90c", size = 34169374, upload-time = "2026-02-10T12:04:12.13Z" }, + { url = "https://files.pythonhosted.org/packages/42/07/069983799cf4050c68f61a494f94b06f095fee6026ab0dd863a14de30867/mysql_connector_python-9.6.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:a26490cb029bf7b18a1d2093101105b3526a1036b51ad01553d30138f5beb8d2", size = 34543019, upload-time = "2026-02-10T12:04:15.065Z" }, + { url = "https://files.pythonhosted.org/packages/32/00/fbeb7d666ab8153f719e620bac5abfbc74640e8ec511612493110a75fe66/mysql_connector_python-9.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:3460ed976e1b88b7284335d9397a3c519dff56d71580ca1f76ff1c0c7714c813", size = 16515701, upload-time = "2026-02-10T12:04:19.26Z" }, + { url = "https://files.pythonhosted.org/packages/70/51/13cc90b2a703784cd9a0aa0a6fce07946cf6a2abe7c8fd0b585562e250fc/mysql_connector_python-9.6.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:e2cc13cd3dcdb845d636e52c4e7a9509b63da09bec6ce1b3696be53a79847e2d", size = 17585800, upload-time = "2026-02-10T12:04:21.6Z" }, + { url = "https://files.pythonhosted.org/packages/c8/6b/ce7ab998fbdd17f35a1b54624365d039045cbb2d42bbc7b03f50d7597c7b/mysql_connector_python-9.6.0-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:a08c2149d4b52a010c4353f18c84716d18114a4ecd00b466ea34138de2c640f2", size = 18452823, upload-time = "2026-02-10T12:04:23.995Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/8157ed61d17878c33511dcb97c68ecaaaf6220bea5a2944ea4eba73cc63a/mysql_connector_python-9.6.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:b00228b985edd208b20f45c5e684c54e08e31e01bc1d8c3c18a36641c3be5bf7", size = 34171594, upload-time = "2026-02-10T12:04:27.401Z" }, + { url = "https://files.pythonhosted.org/packages/f7/06/5efdd28819afdb9f1487a62842fda4277febe128a3cd6e9090dbe0a6524e/mysql_connector_python-9.6.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4617ef5216da7ca32dd46afda61a1552807762434127413bba46fbe4379f59d4", size = 34542851, upload-time = "2026-02-10T12:04:31.021Z" }, + { url = "https://files.pythonhosted.org/packages/40/6a/26e08a4a79f159cd8e5b64eb10bd056e7735b65d4464d98641f59eb9ca3a/mysql_connector_python-9.6.0-cp314-cp314-win_amd64.whl", hash = "sha256:bc782f64ca00b6b933d4c6a35568f1349d115cc4434c849b5b9edc015bee3e62", size = 17002947, upload-time = "2026-02-10T12:04:34.386Z" }, + { url = "https://files.pythonhosted.org/packages/15/dd/b3250826c29cee7816de4409a2fe5e469a68b9a89f6bfaa5eed74f05532c/mysql_connector_python-9.6.0-py2.py3-none-any.whl", hash = "sha256:44b0fb57207ebc6ae05b5b21b7968a9ed33b29187fe87b38951bad2a334d75d5", size = 480527, upload-time = "2026-02-10T12:04:36.176Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] diff --git a/Semaine_07/Jour_04/bank/.python-version b/Semaine_07/Jour_04/bank/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/Semaine_07/Jour_04/bank/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/Semaine_07/Jour_04/bank/README.md b/Semaine_07/Jour_04/bank/README.md new file mode 100644 index 0000000..e486ebc --- /dev/null +++ b/Semaine_07/Jour_04/bank/README.md @@ -0,0 +1,3 @@ +```py +uv add sqlalchemy +``` \ No newline at end of file diff --git a/Semaine_07/Jour_04/bank/database/__init__.py b/Semaine_07/Jour_04/bank/database/__init__.py new file mode 100644 index 0000000..0e3caa7 --- /dev/null +++ b/Semaine_07/Jour_04/bank/database/__init__.py @@ -0,0 +1,12 @@ +from sqlalchemy.orm import DeclarativeMeta, declarative_base +from database.connection import get_engine + +Base: DeclarativeMeta = declarative_base() # permet à SQLAlchemy de faire la liaison Python <-> SQL +engine = get_engine() + +from entities.accounts import Account +from entities.banks import Bank +from entities.clients import Client, ClientNotFound +from entities.credit_cards import CreditCard + +Base.metadata.create_all(engine) \ No newline at end of file diff --git a/Semaine_07/Jour_04/bank/database/bank.db b/Semaine_07/Jour_04/bank/database/bank.db new file mode 100644 index 0000000..e69de29 diff --git a/Semaine_07/Jour_04/bank/database/connection.py b/Semaine_07/Jour_04/bank/database/connection.py new file mode 100644 index 0000000..a706f18 --- /dev/null +++ b/Semaine_07/Jour_04/bank/database/connection.py @@ -0,0 +1,16 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import declarative_base, sessionmaker # constructeur de classe + +db_url = "sqlite:///database/bank.db" + +Base = declarative_base() + +# moteur_dialect +def get_engine(): + return create_engine(url= db_url) + +# session => cursor +def get_session(): + engine = get_engine() + Session = sessionmaker(bind= engine) + return Session() \ No newline at end of file diff --git a/Semaine_07/Jour_04/bank/entities/accounts.py b/Semaine_07/Jour_04/bank/entities/accounts.py new file mode 100644 index 0000000..c74cc09 --- /dev/null +++ b/Semaine_07/Jour_04/bank/entities/accounts.py @@ -0,0 +1,51 @@ +from random import randint + +from sqlalchemy import ForeignKey, Column, Integer, Float +from sqlalchemy.orm import relationship + +from database import Base, engine + +class Account(Base): + __tablename__ = "accounts" + + id = Column(Integer, primary_key= True, autoincrement= True) + number = Column(Integer, nullable= False) + balance = Column(Float, default= 0, nullable= False) + + client_id = Column(Integer, ForeignKey("clients.id"), nullable= False) + credit_card_id = Column(Integer, ForeignKey("credit_cards.id")) + + client = relationship('Client', back_populates= "accounts") + + def __init__(self, initial_deposit: int | float) -> None: + self.number = self.account_number() + self.amount = initial_deposit + + def __repr__(self) -> str: + return f"\ + [Numéro de compte] : {self.number}\n\ + [Montant disponible] : {self.amount}" + + def __eq__(self, other: 'Account'): + return self.number == other.number + + def account_number(self): + number = "".join(str(randint(0,9)) for _ in range(7)) + return " ".join(number[_: _ + 4] for _ in range(0,7,4)) + + def add_money(self, add_amount: int | float): + if add_amount < 0: + return f"{add_amount} n'est pas un entier positif. Recommencez." + self.amount += add_amount + return f"{add_amount} ont été ajoutés. Le nouveau solde est de {self.amount}." + + def take_money(self, take_amount: int | float): + if take_amount < 0: + return f"{take_amount} n'est pas un entier positif. Recommencez." + self.amount -= take_amount + if self.amount < 0: + return f"{take_amount} ont été retirés. Le nouveau solde est de {self.amount}.\n\ + Vous êtes débiteur et devrez payer des agios si vous ne régularisez pas votre situation." + return f"{take_amount} ont été retirés. Le nouveau solde est de {self.amount}." + +Base.metadata.create_all(engine) \ No newline at end of file diff --git a/Semaine_07/Jour_04/bank/entities/banks.py b/Semaine_07/Jour_04/bank/entities/banks.py new file mode 100644 index 0000000..3ac04c0 --- /dev/null +++ b/Semaine_07/Jour_04/bank/entities/banks.py @@ -0,0 +1,37 @@ +from clients import Client, ClientNotFound +from credit_cards import CreditCard + +from sqlalchemy import Column, Integer, String +from sqlalchemy.orm import relationship + +from database import Base, engine + +class Bank(Base): + __tablename__ = "banks" + + id = Column(Integer, primary_key= True, autoincrement= True) + name = Column(String(200), nullable= False) + + clients = relationship("Client", back_populate= "bank", uselist= True) # type: ignore + # Le uselist définit le One ou le Many d'une relation inter-tables + # Cela permet aussi de définir la dépendance d'une table à l'autre + + def __init__(self, name: str) -> None: + self.name = name + self.clients: list["Client"] = [] + # Tout attribut faisant référence à une autre classe + # est une RELATION, pas une COLONNE + + def __repr__(self) -> str: + return f"\ + [Banque] : {self.name}" + + def add_client(self, client: "Client"): + self.clients.append(client) + + def request_credit_card(self, client: "Client"): + if client not in self.clients: + raise ClientNotFound() + return CreditCard(account = client.account) + +Base.metadata.create_all(engine) \ No newline at end of file diff --git a/Semaine_07/Jour_04/bank/entities/clients.py b/Semaine_07/Jour_04/bank/entities/clients.py new file mode 100644 index 0000000..78aabb8 --- /dev/null +++ b/Semaine_07/Jour_04/bank/entities/clients.py @@ -0,0 +1,43 @@ +from accounts import Account +from services import client_service + +from sqlalchemy import ForeignKey, Column, Integer, String, Float +from sqlalchemy.orm import relationship + +from database import Base, engine + +class Client(Base): + __tablename__ = "clients" + + id = Column(Integer, primary_key= True, autoincrement= True) + first_name = Column(String(200), nullable= False) + last_name = Column(String(200), nullable= False) + city = Column(String(200)) + salary = Column(Float) + + bank_id = Column(Integer, ForeignKey("banks.id"), nullable= False) + + bank = relationship("Bank", back_populates= "clients") + account = relationship("Account", back_populates= "clients", uselist= False) # type: ignore + services = relationship("Services", back_populates= "clients", secondary= client_service) + + def __init__(self, first_name: str, last_name: str, salary: float, city: str, initial_deposit: int | float = 0) -> None: + self.first_name = first_name + self.last_name = last_name + self.salary = salary + self.city = city + self.account: Account = Account(initial_deposit = initial_deposit) + + def __eq__(self, other: "Client") -> bool: + return self.first_name == other.first_name and\ + self.last_name == other.last_name and\ + self.account == other.account # type: ignore + + def __repr__(self) -> str: + return f"Client({self.first_name} {self.last_name})" + +class ClientNotFound(Exception): + def __init__(self, *args: object) -> None: + super().__init__("Client not found") + +Base.metadata.create_all(engine) \ No newline at end of file diff --git a/Semaine_07/Jour_04/bank/entities/credit_cards.py b/Semaine_07/Jour_04/bank/entities/credit_cards.py new file mode 100644 index 0000000..f23a9cc --- /dev/null +++ b/Semaine_07/Jour_04/bank/entities/credit_cards.py @@ -0,0 +1,45 @@ +from datetime import datetime +from random import randint + +from accounts import Account + +from sqlalchemy import Column, Integer, CHAR +from sqlalchemy.orm import relationship + +from database import Base, engine + +class CreditCard(Base): + __tablename__ = "credit_cards" + + id = Column(Integer, primary_key= True, autoincrement= True) + number = Column(CHAR(19), nullable= False) + secure_code = Column(CHAR(3), nullable= False) + expire_date = Column(CHAR(5), nullable= False) + + account = relationship("Account", back_populates= "credit_cards", uselist= False) # type: ignore + + def __init__(self, account: "Account") -> None: + self.number = self.credit_card_number() + self.expire_date = self.expiration_date() + self.secure_code = self.security_code() + self.account = account + + def __repr__(self) -> str: + return f"\ + [Numéro de carte] : {self.number}\n\ + [Date d'expiration] : {self.expire_date}\n\ + [Code de sécurité] : {self.secure_code}" + + def credit_card_number(self): + number = "".join(str(randint(0,9)) for _ in range(16)) + return " ".join(number[_: _ + 4] for _ in range(0,16,4)) + + def security_code(self): + return "".join(str(randint(0,9)) for _ in range(3)) + + def expiration_date(self): + current_date = datetime.now() + future_date = current_date.replace(year=current_date.year + 5) + return str(future_date.strftime("%m/%y")) + +Base.metadata.create_all(engine) \ No newline at end of file diff --git a/Semaine_07/Jour_04/bank/entities/services.py b/Semaine_07/Jour_04/bank/entities/services.py new file mode 100644 index 0000000..078b513 --- /dev/null +++ b/Semaine_07/Jour_04/bank/entities/services.py @@ -0,0 +1,30 @@ +from sqlalchemy import ForeignKey, Column, Integer, String, Float, Table +from sqlalchemy.orm import relationship + +from database.connection import Base + +# Table d'association +client_service = Table( + 'client_service', + Base.metadata, + Column('client_id', Integer, ForeignKey('clients.id'), primary_key= True), + Column('service_id', Integer, ForeignKey('clients.id'), primary_key= True) +) + +class Service(Base): + __tablename__ = "Services" + + id = Column(Integer, primary_key= True, autoincrement= True) + name = Column(String(200), nullable= False) + description = Column(String(500)) + monthly_price = Column(Float) + + clients = relationship("Client", back_populates= "services", secondary= client_service) + + def __init__(self, name, description= "", price= 0): + self.name = name + self.description = description + self.monthly_price = price + + def __repr__(self): + return f"Service({self.name})" \ No newline at end of file diff --git a/Semaine_07/Jour_04/bank/main (# Edit conflict 2026-03-19 yty269C #).py b/Semaine_07/Jour_04/bank/main (# Edit conflict 2026-03-19 yty269C #).py new file mode 100644 index 0000000..8c66729 --- /dev/null +++ b/Semaine_07/Jour_04/bank/main (# Edit conflict 2026-03-19 yty269C #).py @@ -0,0 +1,51 @@ +from entities.accounts import Account +from entities.banks import Bank +from entities.clients import Client, ClientNotFound +from entities.credit_cards import CreditCard +# from database import create_table + +""" +On veut gérer des banques +Une banque a comme attributs : +- un nom +- une liste de clients + +Un client a comme attributs : +- un prénom +- un nom +- un salaire +- une ville + +Une banque peut ajouter un client à sa liste, ce qui va créer un compte + +Un compte a : +- un numéro à 7 chiffres +- un solde initial de 0 + +On peut : +- déposer de l'argent +- retirer de l'argent + +Une banque peut demander unne CB pour un client +Une CB a un numéro à 16 chiffres, un code de sécurité de 3 chiffres et une date d'expiration de 5 ans +""" + +def create_tables(): + Base.metadata.create_all(get_engine()) + +create_tables() +session = get_session() + +# Banques +bank_001 = Bank("Crédit Agricool") +bank_002 = Bank("Société Géniale") +# Clients +client_001 = Client("Alex", "TERIEUR", 3000, "Lorient") +client_002 = Client("Emma", "TITEGOUTTE", 1750, "Brest") + +session.add(bank_001) +session.add(bank_002) +session.add(client_001) +session.add(client_002) + +session.commit() \ No newline at end of file diff --git a/Semaine_07/Jour_04/bank/main.py b/Semaine_07/Jour_04/bank/main.py new file mode 100644 index 0000000..3f7e2d1 --- /dev/null +++ b/Semaine_07/Jour_04/bank/main.py @@ -0,0 +1,51 @@ +from entities.accounts import Account +from entities.banks import Bank +from entities.clients import Client, ClientNotFound +from entities.credit_cards import CreditCard +from database import create_table + +""" +On veut gérer des banques +Une banque a comme attributs : +- un nom +- une liste de clients + +Un client a comme attributs : +- un prénom +- un nom +- un salaire +- une ville + +Une banque peut ajouter un client à sa liste, ce qui va créer un compte + +Un compte a : +- un numéro à 7 chiffres +- un solde initial de 0 + +On peut : +- déposer de l'argent +- retirer de l'argent + +Une banque peut demander unne CB pour un client +Une CB a un numéro à 16 chiffres, un code de sécurité de 3 chiffres et une date d'expiration de 5 ans +""" + +def create_tables(): + Base.metadata.create_all(get_engine()) + +create_tables() +session = get_session() + +# Banques +bank_001 = Bank("Crédit Agricool") +bank_002 = Bank("Société Géniale") +# Clients +client_001 = Client("Alex", "TERIEUR", 3000, "Lorient") +client_002 = Client("Emma", "TITEGOUTTE", 1750, "Brest") + +session.add(bank_001) +session.add(bank_002) +session.add(client_001) +session.add(client_002) + +session.commit() \ No newline at end of file diff --git a/Semaine_07/Jour_04/bank/pyproject.toml b/Semaine_07/Jour_04/bank/pyproject.toml new file mode 100644 index 0000000..7da3742 --- /dev/null +++ b/Semaine_07/Jour_04/bank/pyproject.toml @@ -0,0 +1,9 @@ +[project] +name = "bank" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "sqlalchemy>=2.0.48", +] diff --git a/Semaine_07/Jour_04/bank/uv.lock b/Semaine_07/Jour_04/bank/uv.lock new file mode 100644 index 0000000..e99a00f --- /dev/null +++ b/Semaine_07/Jour_04/bank/uv.lock @@ -0,0 +1,108 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "bank" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "sqlalchemy" }, +] + +[package.metadata] +requires-dist = [{ name = "sqlalchemy", specifier = ">=2.0.48" }] + +[[package]] +name = "greenlet" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", size = 188267, upload-time = "2026-02-20T20:54:15.531Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", size = 280358, upload-time = "2026-02-20T20:17:43.971Z" }, + { url = "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", size = 601217, upload-time = "2026-02-20T20:47:31.462Z" }, + { url = "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", size = 611792, upload-time = "2026-02-20T20:55:58.423Z" }, + { url = "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", size = 613875, upload-time = "2026-02-20T20:21:01.102Z" }, + { url = "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", size = 1571467, upload-time = "2026-02-20T20:49:33.495Z" }, + { url = "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", size = 1640001, upload-time = "2026-02-20T20:21:09.154Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/cc802e067d02af8b60b6771cea7d57e21ef5e6659912814babb42b864713/greenlet-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f", size = 231081, upload-time = "2026-02-20T20:17:28.121Z" }, + { url = "https://files.pythonhosted.org/packages/58/2e/fe7f36ff1982d6b10a60d5e0740c759259a7d6d2e1dc41da6d96de32fff6/greenlet-3.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643", size = 230331, upload-time = "2026-02-20T20:17:23.34Z" }, + { url = "https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4", size = 279120, upload-time = "2026-02-20T20:19:01.9Z" }, + { url = "https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986", size = 603238, upload-time = "2026-02-20T20:47:32.873Z" }, + { url = "https://files.pythonhosted.org/packages/59/0e/4223c2bbb63cd5c97f28ffb2a8aee71bdfb30b323c35d409450f51b91e3e/greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92", size = 614219, upload-time = "2026-02-20T20:55:59.817Z" }, + { url = "https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab", size = 616774, upload-time = "2026-02-20T20:21:02.454Z" }, + { url = "https://files.pythonhosted.org/packages/0a/03/996c2d1689d486a6e199cb0f1cf9e4aa940c500e01bdf201299d7d61fa69/greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a", size = 1571277, upload-time = "2026-02-20T20:49:34.795Z" }, + { url = "https://files.pythonhosted.org/packages/d9/c4/2570fc07f34a39f2caf0bf9f24b0a1a0a47bc2e8e465b2c2424821389dfc/greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b", size = 1640455, upload-time = "2026-02-20T20:21:10.261Z" }, + { url = "https://files.pythonhosted.org/packages/91/39/5ef5aa23bc545aa0d31e1b9b55822b32c8da93ba657295840b6b34124009/greenlet-3.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124", size = 230961, upload-time = "2026-02-20T20:16:58.461Z" }, + { url = "https://files.pythonhosted.org/packages/62/6b/a89f8456dcb06becff288f563618e9f20deed8dd29beea14f9a168aef64b/greenlet-3.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327", size = 230221, upload-time = "2026-02-20T20:17:37.152Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ae/8bffcbd373b57a5992cd077cbe8858fff39110480a9d50697091faea6f39/greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab", size = 279650, upload-time = "2026-02-20T20:18:00.783Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c0/45f93f348fa49abf32ac8439938726c480bd96b2a3c6f4d949ec0124b69f/greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082", size = 650295, upload-time = "2026-02-20T20:47:34.036Z" }, + { url = "https://files.pythonhosted.org/packages/b3/de/dd7589b3f2b8372069ab3e4763ea5329940fc7ad9dcd3e272a37516d7c9b/greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9", size = 662163, upload-time = "2026-02-20T20:56:01.295Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d8/09bfa816572a4d83bccd6750df1926f79158b1c36c5f73786e26dbe4ee38/greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506", size = 664160, upload-time = "2026-02-20T20:21:04.015Z" }, + { url = "https://files.pythonhosted.org/packages/48/cf/56832f0c8255d27f6c35d41b5ec91168d74ec721d85f01a12131eec6b93c/greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce", size = 1619181, upload-time = "2026-02-20T20:49:36.052Z" }, + { url = "https://files.pythonhosted.org/packages/0a/23/b90b60a4aabb4cec0796e55f25ffbfb579a907c3898cd2905c8918acaa16/greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5", size = 1687713, upload-time = "2026-02-20T20:21:11.684Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ca/2101ca3d9223a1dc125140dbc063644dca76df6ff356531eb27bc267b446/greenlet-3.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492", size = 232034, upload-time = "2026-02-20T20:20:08.186Z" }, + { url = "https://files.pythonhosted.org/packages/f6/4a/ecf894e962a59dea60f04877eea0fd5724618da89f1867b28ee8b91e811f/greenlet-3.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71", size = 231437, upload-time = "2026-02-20T20:18:59.722Z" }, + { url = "https://files.pythonhosted.org/packages/98/6d/8f2ef704e614bcf58ed43cfb8d87afa1c285e98194ab2cfad351bf04f81e/greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54", size = 286617, upload-time = "2026-02-20T20:19:29.856Z" }, + { url = "https://files.pythonhosted.org/packages/5e/0d/93894161d307c6ea237a43988f27eba0947b360b99ac5239ad3fe09f0b47/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4", size = 655189, upload-time = "2026-02-20T20:47:35.742Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2c/d2d506ebd8abcb57386ec4f7ba20f4030cbe56eae541bc6fd6ef399c0b41/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff", size = 658225, upload-time = "2026-02-20T20:56:02.527Z" }, + { url = "https://files.pythonhosted.org/packages/8e/30/3a09155fbf728673a1dea713572d2d31159f824a37c22da82127056c44e4/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4", size = 657907, upload-time = "2026-02-20T20:21:05.259Z" }, + { url = "https://files.pythonhosted.org/packages/f3/fd/d05a4b7acd0154ed758797f0a43b4c0962a843bedfe980115e842c5b2d08/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727", size = 1618857, upload-time = "2026-02-20T20:49:37.309Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e1/50ee92a5db521de8f35075b5eff060dd43d39ebd46c2181a2042f7070385/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e", size = 1680010, upload-time = "2026-02-20T20:21:13.427Z" }, + { url = "https://files.pythonhosted.org/packages/29/4b/45d90626aef8e65336bed690106d1382f7a43665e2249017e9527df8823b/greenlet-3.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a", size = 237086, upload-time = "2026-02-20T20:20:45.786Z" }, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/73/b4a9737255583b5fa858e0bb8e116eb94b88c910164ed2ed719147bde3de/sqlalchemy-2.0.48.tar.gz", hash = "sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7", size = 9886075, upload-time = "2026-03-02T15:28:51.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/91/a42ae716f8925e9659df2da21ba941f158686856107a61cc97a95e7647a3/sqlalchemy-2.0.48-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b", size = 2155737, upload-time = "2026-03-02T15:49:13.207Z" }, + { url = "https://files.pythonhosted.org/packages/b9/52/f75f516a1f3888f027c1cfb5d22d4376f4b46236f2e8669dcb0cddc60275/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb", size = 3337020, upload-time = "2026-03-02T15:50:34.547Z" }, + { url = "https://files.pythonhosted.org/packages/37/9a/0c28b6371e0cdcb14f8f1930778cb3123acfcbd2c95bb9cf6b4a2ba0cce3/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894", size = 3349983, upload-time = "2026-03-02T15:53:25.542Z" }, + { url = "https://files.pythonhosted.org/packages/1c/46/0aee8f3ff20b1dcbceb46ca2d87fcc3d48b407925a383ff668218509d132/sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9", size = 3279690, upload-time = "2026-03-02T15:50:36.277Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/a957bc91293b49181350bfd55e6dfc6e30b7f7d83dc6792d72043274a390/sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e", size = 3314738, upload-time = "2026-03-02T15:53:27.519Z" }, + { url = "https://files.pythonhosted.org/packages/4b/44/1d257d9f9556661e7bdc83667cc414ba210acfc110c82938cb3611eea58f/sqlalchemy-2.0.48-cp312-cp312-win32.whl", hash = "sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99", size = 2115546, upload-time = "2026-03-02T15:54:31.591Z" }, + { url = "https://files.pythonhosted.org/packages/f2/af/c3c7e1f3a2b383155a16454df62ae8c62a30dd238e42e68c24cebebbfae6/sqlalchemy-2.0.48-cp312-cp312-win_amd64.whl", hash = "sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a", size = 2142484, upload-time = "2026-03-02T15:54:34.072Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c6/569dc8bf3cd375abc5907e82235923e986799f301cd79a903f784b996fca/sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4", size = 2152599, upload-time = "2026-03-02T15:49:14.41Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ff/f4e04a4bd5a24304f38cb0d4aa2ad4c0fb34999f8b884c656535e1b2b74c/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f", size = 3278825, upload-time = "2026-03-02T15:50:38.269Z" }, + { url = "https://files.pythonhosted.org/packages/fe/88/cb59509e4668d8001818d7355d9995be90c321313078c912420603a7cb95/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed", size = 3295200, upload-time = "2026-03-02T15:53:29.366Z" }, + { url = "https://files.pythonhosted.org/packages/87/dc/1609a4442aefd750ea2f32629559394ec92e89ac1d621a7f462b70f736ff/sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658", size = 3226876, upload-time = "2026-03-02T15:50:39.802Z" }, + { url = "https://files.pythonhosted.org/packages/37/c3/6ae2ab5ea2fa989fbac4e674de01224b7a9d744becaf59bb967d62e99bed/sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8", size = 3265045, upload-time = "2026-03-02T15:53:31.421Z" }, + { url = "https://files.pythonhosted.org/packages/6f/82/ea4665d1bb98c50c19666e672f21b81356bd6077c4574e3d2bbb84541f53/sqlalchemy-2.0.48-cp313-cp313-win32.whl", hash = "sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131", size = 2113700, upload-time = "2026-03-02T15:54:35.825Z" }, + { url = "https://files.pythonhosted.org/packages/b7/2b/b9040bec58c58225f073f5b0c1870defe1940835549dafec680cbd58c3c3/sqlalchemy-2.0.48-cp313-cp313-win_amd64.whl", hash = "sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2", size = 2139487, upload-time = "2026-03-02T15:54:37.079Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/7b17bd50244b78a49d22cc63c969d71dc4de54567dc152a9b46f6fae40ce/sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae", size = 3558851, upload-time = "2026-03-02T15:57:48.607Z" }, + { url = "https://files.pythonhosted.org/packages/20/0d/213668e9aca61d370f7d2a6449ea4ec699747fac67d4bda1bb3d129025be/sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb", size = 3525525, upload-time = "2026-03-02T16:04:38.058Z" }, + { url = "https://files.pythonhosted.org/packages/85/d7/a84edf412979e7d59c69b89a5871f90a49228360594680e667cb2c46a828/sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b", size = 3466611, upload-time = "2026-03-02T15:57:50.759Z" }, + { url = "https://files.pythonhosted.org/packages/86/55/42404ce5770f6be26a2b0607e7866c31b9a4176c819e9a7a5e0a055770be/sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121", size = 3475812, upload-time = "2026-03-02T16:04:40.092Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ae/29b87775fadc43e627cf582fe3bda4d02e300f6b8f2747c764950d13784c/sqlalchemy-2.0.48-cp313-cp313t-win32.whl", hash = "sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485", size = 2141335, upload-time = "2026-03-02T15:52:51.518Z" }, + { url = "https://files.pythonhosted.org/packages/91/44/f39d063c90f2443e5b46ec4819abd3d8de653893aae92df42a5c4f5843de/sqlalchemy-2.0.48-cp313-cp313t-win_amd64.whl", hash = "sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79", size = 2173095, upload-time = "2026-03-02T15:52:52.79Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b3/f437eaa1cf028bb3c927172c7272366393e73ccd104dcf5b6963f4ab5318/sqlalchemy-2.0.48-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd", size = 2154401, upload-time = "2026-03-02T15:49:17.24Z" }, + { url = "https://files.pythonhosted.org/packages/6c/1c/b3abdf0f402aa3f60f0df6ea53d92a162b458fca2321d8f1f00278506402/sqlalchemy-2.0.48-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f", size = 3274528, upload-time = "2026-03-02T15:50:41.489Z" }, + { url = "https://files.pythonhosted.org/packages/f2/5e/327428a034407651a048f5e624361adf3f9fbac9d0fa98e981e9c6ff2f5e/sqlalchemy-2.0.48-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b", size = 3279523, upload-time = "2026-03-02T15:53:32.962Z" }, + { url = "https://files.pythonhosted.org/packages/2a/ca/ece73c81a918add0965b76b868b7b5359e068380b90ef1656ee995940c02/sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0", size = 3224312, upload-time = "2026-03-02T15:50:42.996Z" }, + { url = "https://files.pythonhosted.org/packages/88/11/fbaf1ae91fa4ee43f4fe79661cead6358644824419c26adb004941bdce7c/sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2", size = 3246304, upload-time = "2026-03-02T15:53:34.937Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5fb0deb13930b4f2f698c5541ae076c18981173e27dd00376dbaea7a9c82/sqlalchemy-2.0.48-cp314-cp314-win32.whl", hash = "sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6", size = 2116565, upload-time = "2026-03-02T15:54:38.321Z" }, + { url = "https://files.pythonhosted.org/packages/95/7e/e83615cb63f80047f18e61e31e8e32257d39458426c23006deeaf48f463b/sqlalchemy-2.0.48-cp314-cp314-win_amd64.whl", hash = "sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0", size = 2142205, upload-time = "2026-03-02T15:54:39.831Z" }, + { url = "https://files.pythonhosted.org/packages/83/e3/69d8711b3f2c5135e9cde5f063bc1605860f0b2c53086d40c04017eb1f77/sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241", size = 3563519, upload-time = "2026-03-02T15:57:52.387Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4f/a7cce98facca73c149ea4578981594aaa5fd841e956834931de503359336/sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0", size = 3528611, upload-time = "2026-03-02T16:04:42.097Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7d/5936c7a03a0b0cb0fa0cc425998821c6029756b0855a8f7ee70fba1de955/sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3", size = 3472326, upload-time = "2026-03-02T15:57:54.423Z" }, + { url = "https://files.pythonhosted.org/packages/f4/33/cea7dfc31b52904efe3dcdc169eb4514078887dff1f5ae28a7f4c5d54b3c/sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b", size = 3478453, upload-time = "2026-03-02T16:04:44.584Z" }, + { url = "https://files.pythonhosted.org/packages/c8/95/32107c4d13be077a9cae61e9ae49966a35dc4bf442a8852dd871db31f62e/sqlalchemy-2.0.48-cp314-cp314t-win32.whl", hash = "sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f", size = 2147209, upload-time = "2026-03-02T15:52:54.274Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d7/1e073da7a4bc645eb83c76067284a0374e643bc4be57f14cc6414656f92c/sqlalchemy-2.0.48-cp314-cp314t-win_amd64.whl", hash = "sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933", size = 2182198, upload-time = "2026-03-02T15:52:55.606Z" }, + { url = "https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl", hash = "sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096", size = 1940202, upload-time = "2026-03-02T15:52:43.285Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/.env b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/.env new file mode 100644 index 0000000..02d7de1 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/.env @@ -0,0 +1 @@ +DB_PASSWORD=MementoMarsouin96 \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/.python-version b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/02_evaluation.pdf b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/02_evaluation.pdf new file mode 100644 index 0000000..2252c3e Binary files /dev/null and b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/02_evaluation.pdf differ diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Biche_Gauvain_mwb.mwb.beforefix b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Biche_Gauvain_mwb.mwb.beforefix new file mode 100644 index 0000000..13a9774 Binary files /dev/null and b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Biche_Gauvain_mwb.mwb.beforefix differ diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Boiche_Gauvain_eval_20_03_2026.zip b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Boiche_Gauvain_eval_20_03_2026.zip new file mode 100644 index 0000000..95c3075 Binary files /dev/null and b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Boiche_Gauvain_eval_20_03_2026.zip differ diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Boiche_Gauvain_mwb.mwb b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Boiche_Gauvain_mwb.mwb new file mode 100644 index 0000000..13a9774 Binary files /dev/null and b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Boiche_Gauvain_mwb.mwb differ diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Boiche_Gauvain_mwb.mwb.beforefix b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Boiche_Gauvain_mwb.mwb.beforefix new file mode 100644 index 0000000..13a9774 Binary files /dev/null and b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Boiche_Gauvain_mwb.mwb.beforefix differ diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/.env b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/.env new file mode 100644 index 0000000..4776e66 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/.env @@ -0,0 +1,4 @@ +DB_HOST=localhost +DB_USER=root +DB_PASSWORD=MementoMarsouin96 +DB_NAME=petcare \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/__init__.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/__init__.py new file mode 100644 index 0000000..d915343 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/__init__.py @@ -0,0 +1,23 @@ +from peewee import * + +import os +from dotenv import load_dotenv + +load_dotenv() + +db_name = os.environ.get("DB_NAME") +db_user = os.environ.get("DB_USER") +db_password = os.environ.get("DB_PASSWORD") +db_host = os.environ.get("DB_HOST") + +db = MySQLDatabase( + database= db_name, + user= db_user, + password= db_password, + host= db_host, + port= 3306 +) + +class BaseModel(Model): + class Meta: + database = db \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/care.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/care.py new file mode 100644 index 0000000..89ff2bd --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/care.py @@ -0,0 +1,10 @@ +from __init__ import * + +class Care(BaseModel): + id = AutoField(primary_key= True) + nom = CharField(max_length= 150, null= False) + description = TextField() + prix = DecimalField(max_digits= 6, decimal_places= 2) + + class Meta: + table_name = "traitement" diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/consultation.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/consultation.py new file mode 100644 index 0000000..d47bdff --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/consultation.py @@ -0,0 +1,27 @@ +from __init__ import * +from classes.pet import Pet +from classes.veterinarian import Veterinarian +from classes.care import Care + +class Consultation(BaseModel): + id = AutoField(primary_key= True) + date_consultation = DateField(formats= '%Y-%m-%d') # formats= '%Y-%m-%d %H:%M:%S + motif = CharField(max_length= 255, null= False) + diagnostic = TextField() + prix_consultation = DecimalField(max_digits= 6, decimal_places= 2) + + animal = ForeignKeyField(Pet, backref= "animal", null= False) + veterinaire = ForeignKeyField(Veterinarian, backref= "veterinaire", null= False) + + class Meta: + table_name = "consultation" + +class ConsultationCare(BaseModel): + consultation = ForeignKeyField(Consultation) + traitement = ForeignKeyField(Care) + + class Meta: + table_name = "consultation_traitement" + indexes= ( + (("consultation", "traitement"), True), + ) \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/owner.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/owner.py new file mode 100644 index 0000000..4097ee4 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/owner.py @@ -0,0 +1,12 @@ +from __init__ import * + +class Owner(BaseModel): + id = AutoField(primary_key= True) + prenom = CharField(max_length= 100, null= False) + nom = CharField(max_length= 100, null= False) + telephone = CharField(max_length= 15, null= False) + email = CharField(max_length= 150, null= False, unique= True) + ville = CharField(max_length= 100, null= False) + + class Meta: + table_name = "proprietaire" \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/pet.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/pet.py new file mode 100644 index 0000000..5673a29 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/pet.py @@ -0,0 +1,15 @@ +from __init__ import * +from classes.owner import Owner + +class Pet(BaseModel): + id = AutoField(primary_key= True) + nom = CharField(max_length= 100, null= False) + espece = CharField(max_length= 50, null= False) + race = CharField(max_length= 100, null= False) + date_naissance = DateField(formats= '%Y-%m-%d') + poids = DecimalField(max_digits= 5, decimal_places= 2) + + proprietaire = ForeignKeyField(Owner, backref= "proprietaire", null= False) + + class Meta: + table_name = "animal" diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/veterinarian.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/veterinarian.py new file mode 100644 index 0000000..b72d290 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/classes/veterinarian.py @@ -0,0 +1,11 @@ +from __init__ import * + +class Veterinarian(BaseModel): + id = AutoField(primary_key= True) + prenom = CharField(max_length= 100, null= False) + nom = CharField(max_length= 100, null= False) + specialite = CharField(max_length= 100, null= False, unique= True) + telephone = CharField(max_length= 15, null= False) + + class Meta: + table_name = "veterinaire" diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/evaluation.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/evaluation.py new file mode 100644 index 0000000..e8d8897 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/evaluation.py @@ -0,0 +1,127 @@ +from __init__ import * + +# 1. Modèles + +from classes.care import * +from classes.consultation import * +from classes.owner import * +from classes.pet import * +from classes.veterinarian import * + +# 2. CRUD + +## 1. CREATE + +try: + with db.atomic(): + owner_01 = Owner.create( + prenom= "Camille", + nom= "LEMOINE", + telephone= "0656781234", + email= "camille.lemoine@mail.com", + ville= "Bordeaux" + ) + pet_01 = Pet.create( + nom= "Pixel", + espece= "Chat", + race= "Bengal", + date_naissance= "2023-06-15", + poids= 4.5, + proprietaire= owner_01 + ) + veterinarian_01_girard = Veterinarian.get(3) + consultation_01 = Consultation.create( + date_consultation= "2025-05-15", + motif= "Première visite", + diagnostic= "RAS", + prix_consultation= 35.00, + animal= pet_01, + veterinaire= veterinarian_01_girard + ) + + care_01_vermifuge = Care.get(Care.nom == "Vermifuge") + care_01_puce_elec = Care.get(10) + + ConsultationCare.create(consultation= consultation_01, traitement= care_01_vermifuge) + ConsultationCare.create(consultation= consultation_01, traitement= care_01_puce_elec) +except IntegrityError as error: + print(error) + +## 2. READ + +print("\n----- Animaux de Claire MARTIN -----") +claire_martin_pets = Pet.select().join(Owner).where( + (Owner.prenom == "Claire") & + (Owner.nom == "Martin") +) +for pet in claire_martin_pets: + print(f"{pet.nom} - {pet.espece} - {pet.poids}") + +print("\n----- Consultations de REX -----") +claire_martin_rex_consultations = Consultation\ + .select(Consultation, Veterinarian)\ + .join(Veterinarian)\ + .where(Consultation.animal == 1) +for consultation in claire_martin_rex_consultations: + print(f"{consultation.date_consultation} - {consultation.motif} - Dr {consultation.veterinaire.nom}") + +print("\n----- Statistiques par espèce -----") +# Partie faite à l'aide de l'IA, notamment pour le "group_by" avant quoi je n'avais que "Chien" sans explication +# et qui m'a soufflé l'idée de l'alias qui est quand même bien pratique +stats_species = Pet.select( + Pet.espece, + fn.COUNT(Pet.id).alias("nb_pets"), + fn.AVG(Pet.poids).alias("avg_weight") +).group_by(Pet.espece) +for stat in stats_species: + print(f"{stat.nb_pets} {stat.espece}(s) au total - {round(stat.avg_weight, 2)}kg en moyenne") + +## 3. UPDATE + +print("\n----- Le Gros Buddy qui mange trop -----") +try: + pet_buddy = Pet.get(Pet.nom == "Buddy") + print(f"Le poids de {pet_buddy.nom} est enregistré comme étant de {pet_buddy.poids}kg.") + pet_buddy.poids = 29.50 + pet_buddy.save() + print(f"Le poids de Buddy a été changé.") + print(f"Le poids de {pet_buddy.nom} est enregistré comme étant de {pet_buddy.poids}kg.") +except DoesNotExist: + print("L'animal \"Buddy\" n'existe pas.") + +## 4. DELETE + +print("\n----- Vous z'avez pas vu mon fils ? -----") +try: + pet_nemo = Pet.get(Pet.nom == "Nemo") + had_nemo_consultations = Consultation.select().where(Consultation.animal == pet_nemo).count() + + if had_nemo_consultations > 0: + print(f"Impossible de supprimer {pet_nemo.nom}, il possède des consultations associées.") + else: + pet_nemo.delete_instance() + print(f"L'animal {pet_nemo.nom} a été supprimé de la base de données.") +except DoesNotExist: + print(f"L'animal \"Nemo\" n'a pas été trouvé dans la base de données.") + +# 3. Requêtes avancées + +print("\n----- Top 3 soigneurs de zanimaux -----") +top_veterinarians = Veterinarian\ + .select( + Veterinarian.nom, + Veterinarian.specialite, + fn.COUNT(Consultation.id).alias("nb_cons") + )\ + .join(Consultation)\ + .group_by(Veterinarian.id)\ + .order_by(fn.COUNT(Consultation.id)\ + .desc()).limit(3) +for idx, veterinarian in enumerate(top_veterinarians): + print(f"{idx+1} - Dr {veterinarian.nom} avec {veterinarian.nb_cons} consultations.") + +print("\n----- Facture n°1 détaillée -----") + + +print("\n----- Historique-Animal -----") +def historique_animal(): \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/setup_petcare.sql b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/setup_petcare.sql new file mode 100644 index 0000000..9bd4d84 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_01/setup_petcare.sql @@ -0,0 +1,142 @@ +CREATE DATABASE IF NOT EXISTS petcare; +USE petcare; + +CREATE TABLE proprietaire ( + id INT AUTO_INCREMENT PRIMARY KEY, + nom VARCHAR(100) NOT NULL, + prenom VARCHAR(100) NOT NULL, + telephone VARCHAR(15) NOT NULL, + email VARCHAR(150) UNIQUE, + ville VARCHAR(100) NOT NULL +); + +CREATE TABLE veterinaire ( + id INT AUTO_INCREMENT PRIMARY KEY, + nom VARCHAR(100) NOT NULL, + prenom VARCHAR(100) NOT NULL, + specialite VARCHAR(100) NOT NULL, + telephone VARCHAR(15) NOT NULL UNIQUE +); + +CREATE TABLE animal ( + id INT AUTO_INCREMENT PRIMARY KEY, + nom VARCHAR(100) NOT NULL, + espece VARCHAR(50) NOT NULL, + race VARCHAR(100), + date_naissance DATE, + poids DECIMAL(5,2) CHECK (poids > 0), + proprietaire_id INT NOT NULL, + FOREIGN KEY (proprietaire_id) REFERENCES proprietaire(id) +); + +CREATE TABLE consultation ( + id INT AUTO_INCREMENT PRIMARY KEY, + date_consultation DATE NOT NULL, + motif VARCHAR(255) NOT NULL, + diagnostic TEXT, + prix_consultation DECIMAL(6,2) NOT NULL DEFAULT 35.00 CHECK (prix_consultation > 0), + animal_id INT NOT NULL, + veterinaire_id INT NOT NULL, + FOREIGN KEY (animal_id) REFERENCES animal(id), + FOREIGN KEY (veterinaire_id) REFERENCES veterinaire(id) +); + +CREATE TABLE traitement ( + id INT AUTO_INCREMENT PRIMARY KEY, + nom VARCHAR(150) NOT NULL, + description TEXT, + prix DECIMAL(6,2) NOT NULL CHECK (prix > 0) +); + +CREATE TABLE consultation_traitement ( + consultation_id INT, + traitement_id INT, + dosage VARCHAR(100), + PRIMARY KEY (consultation_id, traitement_id), + FOREIGN KEY (consultation_id) REFERENCES consultation(id), + FOREIGN KEY (traitement_id) REFERENCES traitement(id) +); + +-- Propriétaires +INSERT INTO proprietaire (nom, prenom, telephone, email, ville) VALUES +('Martin', 'Claire', '0612345678', 'claire.martin@mail.com', 'Paris'), +('Dupont', 'Jean', '0698765432', 'jean.dupont@mail.com', 'Lyon'), +('Bernard', 'Sophie', '0654321987', 'sophie.bernard@mail.com', 'Paris'), +('Petit', 'Thomas', '0687654321', 'thomas.petit@mail.com', 'Marseille'), +('Moreau', 'Julie', '0676543210', 'julie.moreau@mail.com', 'Lyon'), +('Garcia', 'Pierre', '0665432109', NULL, 'Toulouse'), +('Roux', 'Isabelle', '0643219876', 'isabelle.roux@mail.com', 'Paris'); + +-- Vétérinaires +INSERT INTO veterinaire (nom, prenom, specialite, telephone) VALUES +('Leroy', 'Marc', 'Chirurgie', '0611111111'), +('Fabre', 'Anne', 'Dermatologie', '0622222222'), +('Girard', 'Paul', 'Médecine générale', '0633333333'), +('Duval', 'Marie', 'Comportement', '0644444444'); + +-- Animaux +INSERT INTO animal (nom, espece, race, date_naissance, poids, proprietaire_id) VALUES +('Rex', 'Chien', 'Berger Allemand', '2020-03-15', 32.50, 1), +('Mimi', 'Chat', 'Siamois', '2019-07-22', 4.20, 1), +('Buddy', 'Chien', 'Labrador', '2021-01-10', 28.00, 2), +('Luna', 'Chat', 'Persan', '2022-05-18', 3.80, 3), +('Rocky', 'Chien', 'Bulldog', '2020-11-03', 22.50, 4), +('Nemo', 'Poisson', 'Poisson clown', '2023-01-01', 0.02, 4), +('Bella', 'Chien', 'Golden Retriever', '2019-09-14', 30.00, 5), +('Caramel', 'Chat', 'Européen', '2021-08-25', 5.10, 5), +('Max', 'Chien', 'Husky', '2022-02-28', 25.00, 6), +('Coco', 'Perroquet', 'Ara', '2018-04-12', 1.20, 7), +('Filou', 'Chat', 'Maine Coon', '2020-06-30', 7.50, 7), +('Oscar', 'Chien', 'Caniche', '2023-03-20', 8.00, 2); + +-- Traitements +INSERT INTO traitement (nom, description, prix) VALUES +('Vaccination rage', 'Vaccin antirabique annuel', 45.00), +('Vaccination typhus', 'Vaccin contre le typhus félin', 40.00), +('Vermifuge', 'Traitement antiparasitaire interne', 25.00), +('Anti-puces', 'Traitement antiparasitaire externe', 30.00), +('Détartrage', 'Nettoyage dentaire sous anesthésie', 80.00), +('Radiographie', 'Examen radiologique', 60.00), +('Analyse sanguine', 'Bilan sanguin complet', 55.00), +('Antibiotiques', 'Traitement antibactérien 10 jours', 35.00), +('Castration', 'Stérilisation chirurgicale', 120.00), +('Puce électronique', 'Identification par puce', 50.00); + +-- Consultations +INSERT INTO consultation (date_consultation, motif, diagnostic, prix_consultation, animal_id, veterinaire_id) VALUES +('2025-01-15', 'Visite annuelle', 'Bon état général', 35.00, 1, 1), +('2025-01-20', 'Boiterie', 'Entorse légère patte avant droite', 35.00, 3, 1), +('2025-02-10', 'Mauvaise haleine', 'Tartre important', 35.00, 2, 2), +('2025-02-14', 'Perte de poils', 'Allergie alimentaire', 45.00, 4, 2), +('2025-03-01', 'Vaccination', 'RAS', 35.00, 5, 3), +('2025-03-05', 'Griffures', 'Infection cutanée légère', 35.00, 8, 2), +('2025-03-10', 'Visite annuelle', 'Surpoids léger', 35.00, 7, 3), +('2025-03-15', 'Apathie', 'Anémie légère', 45.00, 11, 3), +('2025-04-01', 'Comportement agressif', 'Stress environnemental', 50.00, 9, 4), +('2025-04-10', 'Visite annuelle', 'Bon état général', 35.00, 10, 3), +('2025-04-15', 'Vomissements', 'Gastrite', 35.00, 12, 1), +('2025-04-20', 'Visite de contrôle', 'Guérison confirmée', 35.00, 3, 1), +('2025-05-01', 'Vaccination rappel', 'RAS', 35.00, 1, 3); + +-- Consultations-Traitements +INSERT INTO consultation_traitement (consultation_id, traitement_id, dosage) VALUES +(1, 1, '1 injection'), +(1, 3, '1 comprimé'), +(2, 6, '2 clichés'), +(2, 8, '1 comprimé/jour pendant 5 jours'), +(3, 5, 'Séance complète'), +(4, 7, 'Prélèvement standard'), +(4, 4, 'Application locale'), +(5, 1, '1 injection'), +(5, 10, 'Implantation épaule gauche'), +(6, 8, '1 comprimé/jour pendant 7 jours'), +(6, 4, 'Application locale'), +(7, 3, '1 comprimé'), +(7, 7, 'Prélèvement standard'), +(8, 7, 'Prélèvement standard'), +(9, 7, 'Prélèvement standard'), +(10, 3, '1 comprimé'), +(11, 8, '1 comprimé/jour pendant 5 jours'), +(12, 6, '1 cliché'), +(13, 1, '1 injection'), +(13, 3, '1 comprimé'); diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/.env b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/.env new file mode 100644 index 0000000..f66f657 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/.env @@ -0,0 +1,4 @@ +DB_HOST=localhost +DB_USER=root +DB_PASSWORD=MementoMarsouin96 +DB_NAME=datacenters \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/__init__.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/__init__.py new file mode 100644 index 0000000..d915343 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/__init__.py @@ -0,0 +1,23 @@ +from peewee import * + +import os +from dotenv import load_dotenv + +load_dotenv() + +db_name = os.environ.get("DB_NAME") +db_user = os.environ.get("DB_USER") +db_password = os.environ.get("DB_PASSWORD") +db_host = os.environ.get("DB_HOST") + +db = MySQLDatabase( + database= db_name, + user= db_user, + password= db_password, + host= db_host, + port= 3306 +) + +class BaseModel(Model): + class Meta: + database = db \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/client.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/client.py new file mode 100644 index 0000000..c357867 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/client.py @@ -0,0 +1,11 @@ +from __init__ import * + +class Client(BaseModel): + id = AutoField(primary_key= True) + nom = CharField(max_length= 150, null= False) + email = CharField(max_length= 200, null= False) + telephone = CharField(max_length= 20, null= False) + type_contrat = CharField(max_length= 50, null= False) + + class Meta: + table_name = "client" \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/datacenter.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/datacenter.py new file mode 100644 index 0000000..265f8e1 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/datacenter.py @@ -0,0 +1,12 @@ +from __init__ import * + +class Datacenter(BaseModel): + id = AutoField(primary_key= True) + nom = CharField(max_length= 100, null= False) + ville = CharField(max_length= 100, null= False) + pays = CharField(max_length= 100, null= False) + capacite_racks = IntegerField(null= False) + annee_ouverture = IntegerField(null= False) + + class Meta: + table_name = "datacenter" \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/hosting.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/hosting.py new file mode 100644 index 0000000..e55144a --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/hosting.py @@ -0,0 +1,15 @@ +from __init__ import * +from classes.client import Client +from classes.servers import Serveur + +class Hebergement(BaseModel): + id = AutoField(primary_key= True) + date_debut = DateField(formats= "%Y-%m-%d", null= False) + date_fin = DateField(formats= "%Y-%m-%d", null= True) + prix_mensuel = DecimalField(max_digits= 8, decimal_places= 2, null= False) + + serveur = ForeignKeyField(Serveur, backref= "serveur", null= False) + client = ForeignKeyField(Client, backref= "client", null= False) + + class Meta: + table_name = "hebergement" \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/intervention.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/intervention.py new file mode 100644 index 0000000..ca9ee32 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/intervention.py @@ -0,0 +1,15 @@ +from __init__ import * +from classes.servers import Serveur + +class Intervention(BaseModel): + id = AutoField(primary_key= True) + date_intervention = DateField(formats= "%Y-%m-%d") + type_intervention = CharField(max_length= 100, null= False) + description = TextField() + duree_heures = DecimalField(max_digits= 4, decimal_places= 2, null= False) + cout = DecimalField(max_digits= 8, decimal_places= 2, null= False) + + serveur = ForeignKeyField(Serveur, backref= "serveur", null= False) + + class Meta: + table_name = "intervention" \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/salle.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/salle.py new file mode 100644 index 0000000..f579f1f --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/salle.py @@ -0,0 +1,14 @@ +from __init__ import * +from classes.datacenter import Datacenter + +class Salle(BaseModel): + id = AutoField(primary_key= True) + nom = CharField(max_length= 100, null= False) + etage = IntegerField(null= False) + superficie_m2 = DecimalField(max_digits= 7, decimal_places= 2, null= False) + nb_racks = IntegerField(null= False) + + datacenter = ForeignKeyField(Datacenter, backref= "datacenter", null= False) + + class Meta: + table_name = "salle" \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/servers.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/servers.py new file mode 100644 index 0000000..121fac1 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/classes/servers.py @@ -0,0 +1,17 @@ +from __init__ import * +from classes.salle import Salle + +class Serveur(BaseModel): + id = AutoField(primary_key= True) + hostname = CharField(max_length= 100, null= False) + marque = CharField(max_length= 100, null= False) + modele = CharField(max_length= 100, null= False) + cpu_cores = IntegerField(null= False) + ram_go = IntegerField(null= False) + stockage_to = DecimalField(max_digits= 5, decimal_places= 2, null= False) + date_installation = DateField(formats= "%Y-%m-%d") + + salle = ForeignKeyField(Salle, backref= "salle", null= False) + + class Meta: + table_name = "serveur" \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/evaluation.py b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/evaluation.py new file mode 100644 index 0000000..e03c150 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/evaluation.py @@ -0,0 +1,115 @@ +from __init__ import * + +from classes.client import * +from classes.datacenter import * +from classes.hosting import * +from classes.intervention import * +from classes.salle import * +from classes.servers import * + +# 1. Requêtes de base + +print("\n----- Datacenters en France par année d'ouverture -----") +datacenters_france = (Datacenter + .select(Datacenter.nom, Datacenter.ville) + .where(Datacenter.pays == "France") + .order_by(Datacenter.annee_ouverture)) +for datacenter in datacenters_france: + print(f"{datacenter.nom} - {datacenter.ville}") + +print("\n----- Serveurs Dell par nom d'hôte -----") +dell_servers = (Serveur + .select(Serveur.hostname, Serveur.modele, Serveur.cpu_cores) + .where(Serveur.marque == 'Dell') + .order_by(Serveur.hostname)) +for serveur in dell_servers: + print(f"{serveur.hostname} - Modèle {serveur.modele} - {serveur.cpu_cores} coeurs") + +print("\n----- Interventions de mars 2025 -----") +march_2025_interventions = (Intervention + .select(Intervention.type_intervention, Intervention.cout, Intervention.date_intervention) + .where(Intervention.date_intervention.between('2025-03-01', '2025-03-31'))) +for intervention in march_2025_interventions: + print(f"{intervention.date_intervention} : {intervention.type_intervention} pour {intervention.cout}€.") + +# 2. Jointures + +print("\n----- Liste des serveurs -----") +server_listing = (Serveur + .select(Serveur, Salle, Datacenter) + .join(Salle) + .join(Datacenter) + .order_by(Datacenter.nom, Serveur.hostname)) +for server in server_listing: + print(f"{server.hostname} - Salle {server.salle.nom}, DC {server.salle.datacenter.nom}") + +print("\n----- Hébergements actifs -----") +active_hostings = (Hebergement + .select(Hebergement, Client, Serveur) + .join(Client) + .switch(Hebergement) + .join(Serveur) + .where(Hebergement.date_fin.is_null()) + .order_by(Hebergement.prix_mensuel.desc())) +for active_hosting in active_hostings: + print(f"{active_hosting.client.nom} - sur {active_hosting.serveur.hostname} pour {active_hosting.prix_mensuel}€ / mois.") + +print("\n----- Serveurs qui n'ont jamais panné (ni poisson ni rien) -----") +servers_without_problem = (Serveur + .select(Serveur.hostname, Serveur.date_installation) + .join(Intervention, JOIN.LEFT_OUTER) + .where(Intervention.id.is_null())) +for server in servers_without_problem: + print(f"{server.hostname} - Installation {server.date_installation}.)") + +print("\n----- Hébergements par client -----") +client_hostings = (Client + .select(Client.nom, + fn.COUNT(Hebergement.id).alias("nb_servers"), + fn.SUM(Hebergement.prix_mensuel).alias("monthly_price")) + .join(Hebergement) + .group_by(Client.id, Client.nom) + .order_by(fn.SUM(Hebergement.prix_mensuel).desc())) +for client_hosting in client_hostings: + total = client_hosting.monthly_price if client_hosting.monthly_price else 0 + print(f"{client_hosting.nom} - {client_hosting.nb_servers} serveur(s) pour un total de {total}€ / mois.") + +# 3. Agrégations + +print("\n----- Liste des serveurs par marque -----") +servers_brand = (Serveur + .select(Serveur.marque, fn.COUNT(Serveur.id).alias("nb_servers")) + .group_by(Serveur.marque)) +for server_brand in servers_brand: + print(f"{server_brand.marque} - {server_brand.nb_servers} serveur(s).") + +print("\n----- Coût total par intervention -----") +intervention_cost = (Intervention + .select(Intervention.type_intervention, + fn.SUM(Intervention.cout).alias("cost"), + fn.AVG(Intervention.cout).alias("avg")) + .group_by(Intervention.type_intervention)) +for cost in intervention_cost: + print(f"{cost.type_intervention} - {cost.cost}€ - moyenne à {round(cost.avg, 2)}€.") # Bonus pour le fun + +print("\n----- Datacenters qui tournent pas trop à vide -----") +empty_datacenters = (Datacenter + .select(Datacenter.nom, fn.COUNT(Serveur.id).alias("nb_serveurs")) + .join(Salle) + .join(Serveur) + .group_by(Datacenter.id, Datacenter.nom) + .having(fn.COUNT(Serveur.id) > 3)) +for datacenter in empty_datacenters: + print(f"{datacenter.nom} - {datacenter.nb_serveurs} serveurs.") + +print("\n----- Mois le plus chargé de l'année -----") +heaviest_month = (Intervention + .select(fn.DATE_FORMAT(Intervention.date_intervention, '%Y-%m').alias('mois'), + fn.COUNT(Intervention.id).alias('nb_interventions'), + fn.SUM(Intervention.cout).alias('cout_total')) + .group_by(fn.DATE_FORMAT(Intervention.date_intervention, '%Y-%m')) + .order_by(fn.COUNT(Intervention.id).desc()) + .limit(1)) +for record in heaviest_month: + print(f"En {record.mois}, il y a eu {record.nb_interventions} interventions totalisant {record.cout_total}€.\n\ +Pensez aux actionnaires !") \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/setup_datacenters.sql b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/setup_datacenters.sql new file mode 100644 index 0000000..83f6d97 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/Part_02/setup_datacenters.sql @@ -0,0 +1,152 @@ +CREATE DATABASE IF NOT EXISTS datacenters; +USE datacenters; + +CREATE TABLE datacenter ( + id INT AUTO_INCREMENT PRIMARY KEY, + nom VARCHAR(100) NOT NULL UNIQUE, + ville VARCHAR(100) NOT NULL, + pays VARCHAR(100) NOT NULL, + capacite_racks INT NOT NULL CHECK (capacite_racks > 0), + annee_ouverture INT NOT NULL CHECK (annee_ouverture >= 2000) +); + +CREATE TABLE salle ( + id INT AUTO_INCREMENT PRIMARY KEY, + nom VARCHAR(100) NOT NULL, + etage INT NOT NULL, + superficie_m2 DECIMAL(7,2) NOT NULL CHECK (superficie_m2 > 0), + nb_racks INT NOT NULL CHECK (nb_racks > 0), + datacenter_id INT NOT NULL, + FOREIGN KEY (datacenter_id) REFERENCES datacenter(id) +); + +CREATE TABLE client ( + id INT AUTO_INCREMENT PRIMARY KEY, + nom VARCHAR(150) NOT NULL, + email VARCHAR(200) NOT NULL UNIQUE, + telephone VARCHAR(20), + type_contrat VARCHAR(50) NOT NULL CHECK (type_contrat IN ('Essentiel', 'Standard', 'Premium')) +); + +CREATE TABLE serveur ( + id INT AUTO_INCREMENT PRIMARY KEY, + hostname VARCHAR(100) NOT NULL UNIQUE, + marque VARCHAR(100) NOT NULL, + modele VARCHAR(100) NOT NULL, + cpu_cores INT NOT NULL CHECK (cpu_cores > 0), + ram_go INT NOT NULL CHECK (ram_go > 0), + stockage_to DECIMAL(5,2) NOT NULL CHECK (stockage_to > 0), + date_installation DATE NOT NULL, + salle_id INT NOT NULL, + FOREIGN KEY (salle_id) REFERENCES salle(id) +); + +CREATE TABLE intervention ( + id INT AUTO_INCREMENT PRIMARY KEY, + date_intervention DATE NOT NULL, + type_intervention VARCHAR(100) NOT NULL, + description TEXT, + duree_heures DECIMAL(4,2) NOT NULL CHECK (duree_heures > 0), + cout DECIMAL(8,2) NOT NULL CHECK (cout >= 0), + serveur_id INT NOT NULL, + FOREIGN KEY (serveur_id) REFERENCES serveur(id) +); + +CREATE TABLE hebergement ( + id INT AUTO_INCREMENT PRIMARY KEY, + date_debut DATE NOT NULL, + date_fin DATE, + prix_mensuel DECIMAL(8,2) NOT NULL CHECK (prix_mensuel > 0), + serveur_id INT NOT NULL, + client_id INT NOT NULL, + FOREIGN KEY (serveur_id) REFERENCES serveur(id), + FOREIGN KEY (client_id) REFERENCES client(id) +); + +-- Datacenters +INSERT INTO datacenter (nom, ville, pays, capacite_racks, annee_ouverture) VALUES +('DC-Paris-Nord', 'Paris', 'France', 500, 2015), +('DC-Lyon-Est', 'Lyon', 'France', 300, 2018), +('DC-Marseille-Sud', 'Marseille', 'France', 200, 2020), +('DC-Bruxelles-Centre', 'Bruxelles', 'Belgique', 400, 2016), +('DC-Geneve-Lac', 'Genève', 'Suisse', 250, 2019); + +-- Salles +INSERT INTO salle (nom, etage, superficie_m2, nb_racks, datacenter_id) VALUES +('Salle Alpha', 0, 450.00, 120, 1), +('Salle Beta', 0, 380.00, 100, 1), +('Salle Gamma', 1, 500.00, 140, 1), +('Salle Delta', 0, 300.00, 80, 2), +('Salle Epsilon', 1, 280.00, 70, 2), +('Salle Zeta', 0, 200.00, 50, 3), +('Salle Eta', 0, 350.00, 100, 4), +('Salle Theta', 1, 320.00, 90, 4), +('Salle Iota', 0, 250.00, 65, 5), +('Salle Kappa', 1, 230.00, 60, 5); + +-- Clients +INSERT INTO client (nom, email, telephone, type_contrat) VALUES +('TechnoWeb SAS', 'contact@technoweb.fr', '0145678901', 'Premium'), +('StartupFlow', 'admin@startupflow.io', '0234567890', 'Standard'), +('MegaStore Europe', 'infra@megastore.eu', '0156789012', 'Premium'), +('DataLab Research', 'tech@datalab.org', '0145671234', 'Standard'), +('CloudFirst Agency', 'ops@cloudfirst.fr', '0198765432', 'Premium'), +('GreenEnergy Corp', 'it@greenenergy.com', '0187654321', 'Essentiel'), +('MediaPlus', 'support@mediaplus.fr', '0176543210', 'Standard'), +('FinanceSecure SA', 'infra@financesecure.ch', '0223456789', 'Premium'), +('EduConnect', 'admin@educonnect.fr', '0156784321', 'Essentiel'), +('LogiTrans SARL', 'it@logitrans.fr', '0167890123', 'Standard'); + +-- Serveurs +INSERT INTO serveur (hostname, marque, modele, cpu_cores, ram_go, stockage_to, date_installation, salle_id) VALUES +('srv-web-01', 'Dell', 'PowerEdge R750', 32, 128, 2.00, '2023-03-15', 1), +('srv-web-02', 'Dell', 'PowerEdge R750', 32, 128, 2.00, '2023-03-15', 1), +('srv-db-01', 'HP', 'ProLiant DL380', 64, 512, 8.00, '2022-06-10', 1), +('srv-app-01', 'Lenovo', 'ThinkSystem SR650', 48, 256, 4.00, '2023-09-01', 2), +('srv-storage-01', 'Dell', 'PowerEdge R760', 16, 64, 24.00, '2024-01-20', 3), +('srv-lyon-01', 'HP', 'ProLiant DL360', 32, 128, 2.00, '2023-05-12', 4), +('srv-lyon-02', 'HP', 'ProLiant DL380', 48, 256, 4.00, '2022-11-08', 4), +('srv-lyon-db-01', 'Dell', 'PowerEdge R750', 64, 512, 8.00, '2023-01-25', 5), +('srv-mars-01', 'Lenovo', 'ThinkSystem SR630', 16, 64, 1.00, '2024-03-10', 6), +('srv-mars-02', 'Lenovo', 'ThinkSystem SR650', 32, 128, 2.00, '2024-03-10', 6), +('srv-brux-01', 'Dell', 'PowerEdge R750', 48, 256, 4.00, '2021-07-15', 7), +('srv-brux-02', 'HP', 'ProLiant DL380', 64, 512, 8.00, '2021-07-15', 7), +('srv-brux-03', 'Dell', 'PowerEdge R760', 32, 128, 2.00, '2023-04-20', 8), +('srv-gen-01', 'HP', 'ProLiant DL380', 48, 256, 4.00, '2022-09-05', 9), +('srv-gen-02', 'Lenovo', 'ThinkSystem SR650', 32, 128, 2.00, '2023-08-18', 10); + +-- Interventions +INSERT INTO intervention (date_intervention, type_intervention, description, duree_heures, cout, serveur_id) VALUES +('2025-01-10', 'Maintenance préventive', 'Remplacement disques préventif', 2.00, 350.00, 3), +('2025-01-15', 'Panne matérielle', 'Remplacement alimentation défectueuse', 1.50, 280.00, 1), +('2025-02-01', 'Mise à jour firmware', 'Update BIOS et firmware RAID', 1.00, 150.00, 6), +('2025-02-12', 'Panne matérielle', 'Remplacement barrette RAM défectueuse', 0.50, 420.00, 12), +('2025-02-20', 'Extension capacité', 'Ajout 256 Go RAM', 1.00, 1200.00, 7), +('2025-03-01', 'Maintenance préventive', 'Nettoyage et vérification câblage', 3.00, 200.00, 11), +('2025-03-05', 'Mise à jour firmware', 'Update BIOS', 0.50, 100.00, 4), +('2025-03-10', 'Panne matérielle', 'Remplacement ventilateur', 0.50, 180.00, 9), +('2025-03-15', 'Extension capacité', 'Ajout disque SSD 2 To', 1.00, 450.00, 14), +('2025-04-01', 'Maintenance préventive', 'Vérification générale', 2.00, 200.00, 1), +('2025-04-05', 'Panne matérielle', 'Remplacement contrôleur RAID', 2.50, 750.00, 8), +('2025-04-10', 'Mise à jour firmware', 'Update firmware stockage', 1.00, 150.00, 5), +('2025-04-15', 'Maintenance préventive', 'Remplacement pâte thermique CPU', 1.50, 120.00, 13), +('2025-05-01', 'Panne matérielle', 'Remplacement carte réseau', 1.00, 320.00, 2), +('2025-05-10', 'Extension capacité', 'Ajout 128 Go RAM', 0.50, 600.00, 10); + +-- Hébergements +INSERT INTO hebergement (date_debut, date_fin, prix_mensuel, serveur_id, client_id) VALUES +('2023-04-01', NULL, 850.00, 1, 1), +('2023-04-01', NULL, 850.00, 2, 1), +('2022-07-01', NULL, 1500.00, 3, 3), +('2023-10-01', NULL, 1100.00, 4, 5), +('2024-02-01', NULL, 600.00, 5, 3), +('2023-06-01', '2025-01-31', 750.00, 6, 2), +('2023-02-01', NULL, 1100.00, 7, 4), +('2023-02-01', NULL, 1500.00, 8, 8), +('2024-04-01', NULL, 400.00, 9, 9), +('2024-04-01', NULL, 750.00, 10, 6), +('2021-08-01', NULL, 1100.00, 11, 3), +('2021-08-01', NULL, 1500.00, 12, 8), +('2023-05-01', NULL, 750.00, 13, 7), +('2022-10-01', NULL, 1100.00, 14, 5), +('2023-09-01', NULL, 750.00, 15, 10); diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/README.md b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/README.md new file mode 100644 index 0000000..1c65c67 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/README.md @@ -0,0 +1,47 @@ +# Evaluation 20/03/2026 - BOICHÉ Gauvain + +## Démarrage + +### Initialisation + +```bash +uv init --python 3.12 +uv run main.py +.venv/Scripts/activate + +uv add dotenv +uv add peewee pymysql +``` + +### Fichier .env + +Les "secrets" sont gérés pour l'occasion dans un fichier .env + +Normalement, on met aussi un fichier + +`.gitignore` + +Dont le contenu mentionne : + +`*.env` + +Pour l'exercice évidemment, je garde le fichier en clair. + +## Partie 1 + + + +## Partie 2 + +### Requêtes normales + +Avoir une requête SQL normale fonctionne : + +```sql +select nom, ville +from datacenters.datacenter +where pays = "France" +order by annee_ouverture; +``` + +Maintenant faut les traduire. \ No newline at end of file diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/docker-compose.yml b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/docker-compose.yml new file mode 100644 index 0000000..15f94a3 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/docker-compose.yml @@ -0,0 +1,19 @@ +services: + mysql: + image: mysql:8.0 + container_name: mysql_evaluation + environment: + MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} + command: --log_bin_trust_function_creators=1 + ports: + - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + mysql_data: diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/evalutaion_vendredi.zip b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/evalutaion_vendredi.zip new file mode 100644 index 0000000..9509699 Binary files /dev/null and b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/evalutaion_vendredi.zip differ diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/pyproject.toml b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/pyproject.toml new file mode 100644 index 0000000..b13de2b --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/pyproject.toml @@ -0,0 +1,11 @@ +[project] +name = "boiche-gauvain-eval-20-03-2026" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "dotenv>=0.9.9", + "peewee>=4.0.2", + "pymysql>=1.1.2", +] diff --git a/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/uv.lock b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/uv.lock new file mode 100644 index 0000000..137b4a9 --- /dev/null +++ b/Semaine_07/Jour_05/Boiche_Gauvain_eval_20_03_2026/uv.lock @@ -0,0 +1,58 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "boiche-gauvain-eval-20-03-2026" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "dotenv" }, + { name = "peewee" }, + { name = "pymysql" }, +] + +[package.metadata] +requires-dist = [ + { name = "dotenv", specifier = ">=0.9.9" }, + { name = "peewee", specifier = ">=4.0.2" }, + { name = "pymysql", specifier = ">=1.1.2" }, +] + +[[package]] +name = "dotenv" +version = "0.9.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dotenv" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892, upload-time = "2025-02-19T22:15:01.647Z" }, +] + +[[package]] +name = "peewee" +version = "4.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/9b/3439dcc3ed5138eb26f28fc208f958ca97b831f5f898b26d5354302e5df4/peewee-4.0.2.tar.gz", hash = "sha256:fd5d026ce7787c6cd3a84316e665a76b72c9c4e87322598c413f44604fa6c38d", size = 714081, upload-time = "2026-03-15T15:46:30.049Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/8e/943f57ce32eb005c7037110a759e2ebfb22e467a9a0efcb6d516c5bf00c6/peewee-4.0.2-py3-none-any.whl", hash = "sha256:86d5d16bcda5bbe017a108f6efc57abaac8d89277915541904542df9d2a7f25d", size = 143344, upload-time = "2026-03-15T15:46:28.778Z" }, +] + +[[package]] +name = "pymysql" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/ae/1fe3fcd9f959efa0ebe200b8de88b5a5ce3e767e38c7ac32fb179f16a388/pymysql-1.1.2.tar.gz", hash = "sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03", size = 48258, upload-time = "2025-08-24T12:55:55.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/4c/ad33b92b9864cbde84f259d5df035a6447f91891f5be77788e2a3892bce3/pymysql-1.1.2-py3-none-any.whl", hash = "sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9", size = 45300, upload-time = "2025-08-24T12:55:53.394Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] diff --git a/Semaine_07/Jour_05/orm_peewee/.env b/Semaine_07/Jour_05/orm_peewee/.env new file mode 100644 index 0000000..6912a26 --- /dev/null +++ b/Semaine_07/Jour_05/orm_peewee/.env @@ -0,0 +1,5 @@ +DB_HOST=localhost +DB_USER=root +DB_PASSWORD=MementoMarsouin96 +DB_NAME=company +DB_PORT=3306 \ No newline at end of file diff --git a/Semaine_07/Jour_05/orm_peewee/.python-version b/Semaine_07/Jour_05/orm_peewee/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/Semaine_07/Jour_05/orm_peewee/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/Semaine_07/Jour_05/orm_peewee/README.md b/Semaine_07/Jour_05/orm_peewee/README.md new file mode 100644 index 0000000..5e95c73 --- /dev/null +++ b/Semaine_07/Jour_05/orm_peewee/README.md @@ -0,0 +1,11 @@ +Normalement, on met aussi un fichier + +`.gitignore` + +Dont le contenu mentionne : + +``` +*.env +``` + +Pour l'exercice évidemment, je garde le fichier en clair. \ No newline at end of file diff --git a/Semaine_07/Jour_05/orm_peewee/docker-compose.yml b/Semaine_07/Jour_05/orm_peewee/docker-compose.yml new file mode 100644 index 0000000..ceb99a7 --- /dev/null +++ b/Semaine_07/Jour_05/orm_peewee/docker-compose.yml @@ -0,0 +1,19 @@ +services: + mysql: + image: mysql:8.0 + container_name: mysql_peewee + environment: + MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} + command: --log_bin_trust_function_creators=1 + ports: + - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + mysql_data: \ No newline at end of file diff --git a/Semaine_07/Jour_05/orm_peewee/main.py b/Semaine_07/Jour_05/orm_peewee/main.py new file mode 100644 index 0000000..9705516 --- /dev/null +++ b/Semaine_07/Jour_05/orm_peewee/main.py @@ -0,0 +1,123 @@ +from peewee import * + +# import os +# from dotenv import load_dotenv + +# load_dotenv() + +# db_name = os.environ.get("DB_NAME") +# db_user = os.environ.get("DB_USER") +# db_password = os.environ.get("DB_PASSWORD") +# db_host = os.environ.get("DB_HOST") +# db_port = os.environ.get("DB_PORT") + +db_name = "company" +db_user = "root" +db_password = "MementoMarsouin96" +db_host = "localhost" +db_port = 3306 + +db = MySQLDatabase( + database= db_name, + user= db_user, + password= db_password, + host= db_host, + port= db_port +) + +# équivalent de "Base = declarative_base()" +class BaseModel(Model): + class Meta: + database = db + +class Department(BaseModel): + department_id = AutoField(primary_key= True) + department_name = CharField(max_length= 50, null= False, unique= True) + location_id = DecimalField(max_digits= 4, decimal_places= 0, null= False) + + # équivalent de "__tablename__ = "truc"" + class Meta: + table_name = "departments" + +class Employee(BaseModel): + employee_id = AutoField(primary_key= True) + first_name = CharField(max_length= 50, null= False) + last_name = CharField(max_length= 50, null= False) + email = CharField(max_length= 50, null= False, unique= True) + salary = IntegerField(null= False) + + department = ForeignKeyField(Department, backref= "employees", null= False) + + class Meta: + table_name = "employees" + +db.connect() +db.create_tables([Department, Employee]) + +try: + it_dept = Department.create(department_name= "IT", location_id= 1400) +except IntegrityError: + print("Département déjà existant.") + +it_dept = Department.get(Department.department_name == "IT") + +try: + Employee.create( + first_name= "Alice", + last_name= "Smith", + email= "a.smith@company.au", + salary= 4000, + department= it_dept # utiliser l'OBJET, pas le nom de l'objet + ) +except IntegrityError: + print("Employé(e) déjà existant(e).") + +new_employees = [ + {'first_name': 'Marie', 'last_name': 'Dupont', 'email': 'mdupont', 'salary': 4500}, + {'first_name': 'Jean', 'last_name': 'Martin', 'email': 'jmartin', 'salary': 5200}, + {'first_name': 'Sophie', 'last_name': 'Bernard', 'email': 'sbernard', 'salary': 4800}, +] + +# with db.atomic(): +# Employee.insert_many(new_employees).execute() + +employee_01 = Employee.get(1) +print(employee_01.first_name, employee_01.last_name) + +employee_02 = Employee.get(Employee.email == "a.smith@company.au") +print(employee_02.first_name, employee_02.salary) + +try: + employee_76 = Employee.get(76) +except DoesNotExist: + print("L'employé(e) 76 n'existe pas.") + +employee_select_01 = Employee.select() # SELECT * FROM employees +print(employee_select_01) +for employee in employee_select_01: + print(employee.first_name, employee.last_name, employee.email) + +employee_select_02 = Employee.select().where( + (Employee.salary > 8000) & (Employee.department == it_dept) +) +print(employee_select_02) + +# pagination +page_01 = Employee.select().order_by(Employee.employee_id).paginate(1, 10) + +# compter une valeur +salary_01 = Employee.select().where(Employee.salary > 10000).count() + +# Jointures (LEFT JOIN, RIGHT JOIN etc) +### FULL JOIN +query = (Employee + .select(Employee, Department) + .join(Department) + .where(Department.department_name == "IT")) + +### Employés affectés à un département +### et ceux non affectés à un département +query = (Employee + .select(Employee, Department) + .join(Department, JOIN.LEFT_OUTER) + .where(Department.department_name == "IT")) \ No newline at end of file diff --git a/Semaine_07/Jour_05/orm_peewee/pyproject.toml b/Semaine_07/Jour_05/orm_peewee/pyproject.toml new file mode 100644 index 0000000..d3ad998 --- /dev/null +++ b/Semaine_07/Jour_05/orm_peewee/pyproject.toml @@ -0,0 +1,10 @@ +[project] +name = "orm-peewee" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "peewee>=4.0.2", + "pymysql>=1.1.2", +] diff --git a/Semaine_07/Jour_05/orm_peewee/relations.py b/Semaine_07/Jour_05/orm_peewee/relations.py new file mode 100644 index 0000000..c2e5fcd --- /dev/null +++ b/Semaine_07/Jour_05/orm_peewee/relations.py @@ -0,0 +1,105 @@ +from peewee import * + +# import os +# from dotenv import load_dotenv + +# load_dotenv() + +# db_name = os.environ.get("DB_NAME") +# db_user = os.environ.get("DB_USER") +# db_password = os.environ.get("DB_PASSWORD") +# db_host = os.environ.get("DB_HOST") +# db_port = os.environ.get("DB_PORT") + +db_name = "company" +db_user = "root" +db_password = "MementoMarsouin96" +db_host = "localhost" +db_port = 3306 + +db = MySQLDatabase( + database= db_name, + user= db_user, + password= db_password, + host= db_host, + port= db_port +) + +# équivalent de "Base = declarative_base()" +class BaseModel(Model): + class Meta: + database = db + +# Relations One-To-Many +class User(BaseModel): + username = CharField(unique= True) + email = CharField() + + class Meta: + table_name = "user" + +class Todo(BaseModel): + title = CharField() + is_done = BooleanField(default= False) + user= ForeignKeyField(User, backref= "todos") # l'accès inverse est géré automatiquement + + class Meta: + table_name = "todos" + +class Tag(BaseModel): + name = CharField(unique= True) + + class Meta: + table_name = "tag" + +class TodoTag(BaseModel): + todo = ForeignKeyField(Todo) + tag = ForeignKeyField(Tag) + + class Meta: + table_name = 'todo_tag' + indexes= ( + (('todo', 'tag'), True), + ) + +class Profile(BaseModel): + user = ForeignKeyField(User, unique= True, backref= "profile") # One-To-One + bio = TextField(null= True) + avatar = CharField(null= True) + + class Meta: + table_name = "profile" + +db.connect() +db.create_tables([User, Todo, Profile, Tag, TodoTag]) + +try: + alice = User.create(username= "Alice", email= "a.smith@company.au") + Todo.create(title= "Apprendre Python", user= alice) + Todo.create(title= "Apprendre JS", user= alice) + Todo.create(title= "Apprendre COBOL", user= alice) + + for todo in alice.todos: + print(todo.title, todo.is_done) +except IntegrityError as error: + print(error) + +try: + Profile.create( + user= User.get(1), + bio = 'Developpeur', + avatar= 'http://example.com' + ) +except IntegrityError as error: + print(error) + +try: + tag_urgent = Tag.create(name= "Urgent") + tag_perso = Tag.create(name= "Perso") + tag_pro = Tag.create(name= "Pro") + + todo_cobol = Todo.get(Todo.title == "Apprendre COBOL") + + TodoTag.create(todo= todo_cobol, tag= tag_urgent) +except IntegrityError as error: + print(error) \ No newline at end of file diff --git a/Semaine_07/Jour_05/orm_peewee/uv.lock b/Semaine_07/Jour_05/orm_peewee/uv.lock new file mode 100644 index 0000000..bfe915d --- /dev/null +++ b/Semaine_07/Jour_05/orm_peewee/uv.lock @@ -0,0 +1,36 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "orm-peewee" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "peewee" }, + { name = "pymysql" }, +] + +[package.metadata] +requires-dist = [ + { name = "peewee", specifier = ">=4.0.2" }, + { name = "pymysql", specifier = ">=1.1.2" }, +] + +[[package]] +name = "peewee" +version = "4.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/9b/3439dcc3ed5138eb26f28fc208f958ca97b831f5f898b26d5354302e5df4/peewee-4.0.2.tar.gz", hash = "sha256:fd5d026ce7787c6cd3a84316e665a76b72c9c4e87322598c413f44604fa6c38d", size = 714081, upload-time = "2026-03-15T15:46:30.049Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/8e/943f57ce32eb005c7037110a759e2ebfb22e467a9a0efcb6d516c5bf00c6/peewee-4.0.2-py3-none-any.whl", hash = "sha256:86d5d16bcda5bbe017a108f6efc57abaac8d89277915541904542df9d2a7f25d", size = 143344, upload-time = "2026-03-15T15:46:28.778Z" }, +] + +[[package]] +name = "pymysql" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/ae/1fe3fcd9f959efa0ebe200b8de88b5a5ce3e767e38c7ac32fb179f16a388/pymysql-1.1.2.tar.gz", hash = "sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03", size = 48258, upload-time = "2025-08-24T12:55:55.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/4c/ad33b92b9864cbde84f259d5df035a6447f91891f5be77788e2a3892bce3/pymysql-1.1.2-py3-none-any.whl", hash = "sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9", size = 45300, upload-time = "2025-08-24T12:55:53.394Z" }, +]