C'est un projet de développement d'une application mobile par l'organisme MONA, visant à découvrir, photographier et collectionner de l'art public.
J'ai rejoint l'équipe de développement du serveur MONA dans le cadre du cours projet IFT3150 hiver 2024.
Ma première rencontre avec l'équipe MONA a commencé avec Lena, elle m'a raconté l'histoire et le développement de ce projet. Après cela, j'en ai appris davantage grâce à des vidéos sur YouTube et en téléchargeant l'application sur mon mobile. Dans une série d'actions de fouille, ouvrir la carte de l'application, localiser, trouver une œuvre d'art, prendre une photo, enregistrer et récupérer des médailles. C'est vraiment une app très très cool.
Ce qui est également cool, c'est que notre équipe est également très bien organisée. La communication de groupe via Element peut améliorer l'efficacité du travail. Le cadre de code complet et clair sur Github me permet de démarrer très rapidement le travail de développement.
Tout d'abord, ma tâche principale est de réajuster la structure du site des rapports. Par exemple, certains collègues mettent leurs rapports sur github. Ce que je dois faire, c'est l'unifier sur le domaine picasso.iro.umontreal.ca. Je dois aussi améliorer l'organisation et la fonctionnalité d'interface de l'admin dans le futur et à mesure.
Pour cette semaine, j'ai commencé à toucher les codes. Tout d'abord, j'ai créé cette page HTML pour enregistrer mes propres rapports hebdomadaires. Et j'ai fait la réunion comme d'habitude, mais cette semaine, j'ai rencontré Lena, Simon et Kim. Un grand merci à eux pour me donner une présentation assez détaillée.
En parallèle, j'ai également utilisé TestFlight pour tester la version MONA mobile 4.0(5) sur iOS avec Kim.
Voici un petit résumé et l'image ci-dessous (à compléter).
Avec l'aide de Lena et Simon, ma prochaine étape consistait à avoir un accès via protocole SSH au serveur de production MONA. (Voir le tutoriel)
Ce lundi 22 janvier, j'ai rencontré Simon par Jitsi. Merci à Simon encore une fois. Il a été très patient et méticuleux en m'aidant tout au long du processus d'installation.
Ensuite, je migrerai certains rapports discordants des années précédentes, ainsi que je minimiserai et éviterai d'utiliser des dépendances.
J'ai installé un environnement de développement local.
Cliquez ici pour découvrir les détails de Installation
Pour l'IDE, j'ai choisi PhpStorm, car les projets Laravel sont largement soutenus par les fonctionnalités PhpStorm telles quelles.
Cette semaine, on a réalisé un board Miro pour mieux illustrer la structure de notre côté serveur.
En même temps, on veut transférer les données ouvertes CSV, ce qui de double stocker et de documenter seulement du côté du serveur. Donc, on a pris du temps pour gérer la documentation également.
Voici, j'ai exploré Admin et documenté les bogues, les fonctionnalités.
Tri
Détail
edit
Filtrer
modification
Cette semaine, j'ai rencontré beaucoup de questions sur le contrôle de version de Git. Tout d’abord, j’ai perdu la connexion à github.com/MaisonMONA. J’ai essayé de me reconnecter plusieurs fois, mais je n’arrive toujours pas. Finalement, j'ai réussi en générant avec github personal access token.
Ensuite, j'ai réglé un problème git reset
et
d'un article de blogue. J'ai appris
beaucoup de choses sur le contrôle de version de Git.
Je n'ai pas fait une grande chose cette semaine, car j'ai deux examens à réviser. Cette semaine, j'ai donc réécrit la sémantique ambiguë du rapport de la semaine dernière, j'ai continué à essayer de corriger le bug et j'ai essayé de pouvoir utiliser des fichiers statiques et log in l'interface admin dans le localhost en utilisant.
./vendor/bin/sail up --build
,
Je n'ai pas fait une grande chose cette semaine, car j'ai deux examens à réviser.
update
pour documenter les modifications, allezPlaceController
pour voir les détails
UserController
../users
, cela réduira considérablement la lisibilité et l'efficacité
d'utilisation de l'interface. Je suggère donc de le placer dans ../users/{..}
,
qui est l'interface après avoir cliqué sur le bouton "Voir l'utilisateur en détail".
Méthode de modification: enleve Route dans web.php
Route::get('users/{user}', [UserController::class, 'show'] )->name('user.show');
ArtworkGrid
PlaceController
,
les méthodes ne sont pas implémentées
Route::get('addTagPhoto', [TagPhotoController::class, 'show']);
Route::get('addTagDiscovery', [TagDiscoveryController::class, 'show']);
Route::put('form-store-preexisting-tag', [TagPhotoController::class, 'storePreexistingTag']);
Route::put('form-store-new-tag', [TagPhotoController::class, 'storeNewTag']);
Route::put('form-store-preexisting-tag-discovery', [TagDiscoveryController::class, 'storePreexistingTag']);
Route::put('form-store-new-tag-discovery', [TagDiscoveryController::class, 'storeNewTag']);
Je crois comprendre que bag s'applique à identifier photo et discovery autres que les catégories.
TagPhotoController
, TagDiscoveryController
De plus, je pense que si nous avons vraiment besoin de données telles que des balises,
alors nous ajouterons une colonne supplémentaire (en plus du titre, de la note, du territoire,
etc.)
au tableau pour l'afficher.
ArtistGrid
Route::get('heritages/create', [HeritageController::class, 'create']);
AdjustmentController
et j'ai découvert qu'il
ne s'agissait que d'un modèle de contrôleur dans Laravel. Donc, je suppose les développeurs backend de
l'époque voulaient laisser un template vide pour développer d'autres modèles, tels que index()
pour obtenir toutes les données du modèle « ajustement » dans la base de données sont converties
en JSON puis transmises à la vue de admin.adjustments.index
pour afficher les données dans
la liste, mais évidemment, nous n'avons pas de modèle « ajustement » dans BD.
Restore
Perdu la connexion logger
./vendor/bin/sail artisan migrate
laravel.test-1 | 2024-02-15 03:22:04,954 INFO spawned: 'php' with pid 15 selenium-1 | 03:22:05.093 INFO [LoggingOptions.configureLogEncoding] - Using the system default encoding selenium-1 | 03:22:05.114 INFO [OpenTelemetryTracer.createTracer] - Using OpenTelemetry for tracing selenium-1 | 03:22:05.816 INFO [NodeOptions.getSessionFactories] - Detected 8 available processors selenium-1 | 03:22:05.901 INFO [NodeOptions.report] - Adding chrome for {"browserName": "chrome","browserVersion": "120.0","goog:chromeOptions": {"binary": "\u002fusr\u002fbin\u002fgoogle-chrome"},"platformName": "linux","se:noVncPort": 7900,"se:vncEnabled": true} 1 times selenium-1 | 03:22:05.933 INFO [Node.] - Binding additional locator mechanisms: relative laravel.test-1 | 2024-02-15 03:22:05,957 INFO success: php entered RUNNING state, process has stayed up for > than 1 seconds (startsecs) selenium-1 | 03:22:05.972 INFO [GridModel.setAvailability] - Switching Node 338f38f7-1df6-4736-8f13-4bc842c79722 (uri: http://172.20.0.2:4444) from DOWN to UP selenium-1 | 03:22:05.973 INFO [LocalDistributor.add] - Added node 338f38f7-1df6-4736-8f13-4bc842c79722 at http://172.20.0.2:4444. Health check every 120s laravel.test-1 | PHP Deprecated: Using ${var} in strings is deprecated, use {$var} instead in /var/www/html/app/Console/Commands/RestoreDatabase.php on line 50
Cette semaine, j'ai résolu le problème de l'admin qui ne pouvait pas se connecter sur localhost. En gros, pour créer un nouveau compte admin, il suffit de créer un nouveau compte régulier via l'API, puis de l'élever en administrateur via SQL:
./vendor/bin/sail up -d
curl -X POST -F 'username=Miliya' -F 'password=MiliyaAi' -F 'password_confirmation=MiliyaAi' http://localhost/api/v3/register
curl
Envoyer ou recevoir des données et interagir avec le serveur
-X POST
Une requête HTTP (POST) pour créer l'utilisateur afin d'éviter de hasher le mot de passe à la mitaine.
-F
Précisez les champs à envoyer
'username=Miliya'
et 'password=MiliyaAi
pour créer un nouveau compte
http://localhost/api/v3/register
est l'URL à laquelle la demande est envoyée. Cette adresse est
endpoint de l'API, dans ce cas le chemin api/v3/register sur le serveur local.
On peut ensuite modifier l'entrée de ce user dans la base de données pour lui donner des privilèges admin. Pour faire les requêtes SQL:
En passant, j'ai mis une faute de frappe dans ma commande, donc je recommence
docker-compose up -d
.vendor/bin/sail mariadb
UPDATE users SET role='admin' WHERE username='Miliya'
J'ai aussi fait une copie de schéma de la BD sur miro cette semaine: j'ai ajouté quelques détails sur les étapes que les utilisateurs et les administrateurs doivent suivre depuis la connexion jusqu'à l'utilisation (telles que comment ils passent par MVC, comment référencer l'API et BD, etc.), et j'ai commenté quelques suggestions pour rendre le flux plus clair.
Au cours des deux dernières semaines (y compris la semaine de lecture), J'ai continué d'améliorer la table d'infrastructure sur Miro. Nous devons apporter un nouveau changement maintenant, à savoir le système de contrôle de version de la base de données. Par exemple, si on veut créer une trace de mise à jour → si une publication contient une nouvelle photo qui remplace l'ancienne photo, nous voulons connaître l'historique (comment ça marche, mécanique, etc.).
On peut concevoir une table de base de données pour stocker des informations historiques. Ce tableau peut contenir les champs suivants (ex: pour la table des places):Table "place_user" { "place_id" bigint(20) [not null] "user_id" bigint(20) [not null] "rating" tinyint(3) [default: NULL] "comment" text [default: NULL] "photo" varchar(191) [default: NULL] "created_at" timestamp [default: NULL] "updated_at" timestamp [default: NULL] }
place_user_history{ "user_id", "place_id" "old_photo". "new_photo" "created_at" }
Créez un modèle Laravel pour cette nouvelle table:php artisan make:model PlaceUserHistory
Logique de mise à jour: lorsqu'une image de lieu est remplacée, un nouvel enregistrement est inséré dans la table place_user_history. Cela peut être fait en écoutant l'événement de mise à jour du modèle ou en ajoutant du code directement dans la logique de mise à jour.use App\Models\PlaceUserHistory; public function update(Request $request, Place $place) { $old_photo = $place->photo; $place->update($request->all()); $new_photo = $place->photo; PlaceUserHistory::create([ 'user_id' => Auth::id(), 'place_id' => $place->id, 'old_photo' => $old_photo, 'new_photo' => $new_photo ]); return redirect()->route('places.index'); }
Requête d'historique: afin d'afficher l'historique de mise à jour des images dans un certain endroit, on peut créer une méthode pour interroger la table place_user_histories et trier par le champ créé_at pour obtenir l'historique de toutes les modifications d'image dans l'endroit.use App\Models\PlaceUserHistory; public function getPlaceUpdateHistory(Place $place) { $histories = PlaceUserHistory::where('place_id', $place->id)->orderBy('created_at', 'desc')->get(); return view('places.history', compact('histories')); }
Après la réunion avec Lena, nous avons besoin d'un modèle plus général pouvant demander le repeuplement de l'API, permettant aux utilisateurs de conserver les dernières données, tout en pouvant également afficher l'historique des modifications dans l'interface d'administration. Il m'est venu à l'esprit que puisque nous avons déjà une table de versions dans la base de données existante, ce serait un bon point de départ. Ce concept peut être étendu pour suivre chaque version de chaque entité (par exemple, artwork, place, heritage)
Lors du stockage de l'état d'une entité sous forme d'instantané JSON, cela signifie qu'à un moment précis, tout ou partie des données de l'entité est convertie en chaîne au format JSON puis stockée dans la base de données. Ce type d'instantané peut capturer et enregistrer l'état exact de l'entité avant et après modification pour faciliter la récupération, l'audit ou la comparaison ultérieure des données.Table "entity_histories" { "id" bigint(20) [pk, not null, increment] "entity_type" varchar(191) [not null] // stocker le type d'entité: 'artwork', 'place', 'heritage' "entity_id" bigint(20) [not null] // stocker l'ID de l'entité "version_id" bigint(20) [not null] // référence à la version de l'entité "action_type" varchar(191) [not null] // 'create', 'update', 'delete' "snapshot" longtext [not null] // stocker l'état de l'entité à ce moment "action_timestamp" timestamp [default: current_timestamp()] // l'heure à laquelle l'action a été effectuée "action_by_user_id" bigint(20) [not null] // référence à l'utilisateur qui a effectué l'action "created_at" timestamp [default: NULL] "updated_at" timestamp [default: NULL] }
Un exemple de logique de mise à jour pour l'entité Artwork:// $artwork est l'entité Artwork à mettre à jour $artwork->rating = $newTitle; // mise à jour du rating $artwork->save(); // enregistrer la mise à jour // Créer un enregistrement d'historique pour la mise à jour $history = new EntityHistory(); $history->entity_type = 'artwork'; $history->entity_id = $artwork->id; $history->version_id = createNewVersionId(); // C'est une fonction pour générer un nouvel ID de version $history->action_type = 'update'; $history->snapshot = json_encode($artwork); // générer un instantané JSON de l'entité $history->action_by_user_id = $currentUser->id; // l'utilisateur actuel qui a effectué l'action $history->save(); // enregistrer l'enregistrement d'historique
Côté client, nous pouvons utiliser «soft delete», une méthode de gestion des enregistrements de base de données qui permet de marquer les données comme « supprimées » sans être définitivement supprimées. 1. Ajoutez le champ delete_at au fichier de migration Ajoutez un champ delete_at pour les modèles nécessitant une suppression logicielle, qui contient l'horodatage du moment où l'enregistrement a été marqué pour suppression. Si l'enregistrement n'a pas été supprimé, ce champ reste vide (ou NULL)Schema::table('artwork', function (Blueprint $table) { $table->softDeletes(); });
2. Utilisez le trait SoftDeletes dans le modèle Utilisez le trait Illuminate\Database\Eloquent\SoftDeletes dans le modèle de Laravel. Cela permettra la suppression logicielle du modèle. par exemple: dans Artwork.php(Models/Artwork/Artwork.php)use Illuminate\Database\Eloquent\SoftDeletes; class Artwork extends Model { use SoftDeletes; }
Une fois SoftDeletes activé, lorsque la méthode delete() est appelée, Laravel ne supprimera pas réellement l'enregistrement de la base de données, mais définira le champ delete_at sur l'horodatage actuel. Le dossier est alors considéré comme « supprimé » 3. Interroger les enregistrements supprimés de manière logicielle Lorsque softdelete est activée, la requête par défaut ne renverra pas les enregistrements supprimés. Si on veut faire une requête pour interroger tous les enregistrements, y compris les softdelets, on peut utiliser la méthode withTrashed:$artworks = Artwork::withTrashed()->get();
Si on veut interroger uniquement les enregistrements supprimés, on peut utiliser la méthode onlyTrashed:$artworks = Artwork::onlyTrashed()->get();
4. Récupérer les enregistrements supprimés de manière softdelete Si on veut restaurer des enregistrements 'supprimés', on peut utiliser la méthode de restauration:$artwork = Artwork::withTrashed()->find($id); $artwork->restore();
Interroger les versions historiques: interroger tous les enregistrements de l'identifiant d'entité et du type d'entité spécifiés via la table entités_histories
Si nous avons le modèle EntityHistory correspondant à la table entity_historiesuse Illuminate\Database\Eloquent\Model; class EntityHistory extends Model { protected $table = 'entity_histories'; }
Ensuite, Créer un contrôleur pour interroger les versions historiques:use App\Models\EntityHistory; use Illuminate\Http\Request; class EntityHistoryController extends Controller { /** * Get the history of an entity * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function showEntityHistory(Request $request) { $entityType = $request->input('entity_type'); $entityId = $request->input('entity_id'); // Query the entity history $histories = EntityHistory::where('entity_type', $entityType) ->where('entity_id', $entityId) ->orderBy('action_timestamp', 'desc') ->get(); // Return the history to the view return response()->json($histories); } }
Amélioration sur BD google slide
Et modélisation UML, veuillez vous référer au dossier «miliya»
interface admin google slide
interface admin + serveur google slide
interface admin + serveur (Avancé) google slide
Modélisation UML pour interface admin et User (Diagramme de cas d'utilisation, diagramme d'activité) google slide
Générer une paire de clés ssh privé/publique avec la commande (ici ed25519 est id_mona)
ssh-keygen -t ed25519
Exemple:
miliya.ai@miliyaaidemac ~ % ssh-keygen -t ed25519
Generating public/private ed25519 key pair.
Ensuite, il va demander de stocker dans quel dossier et en mettant
~/.ssh/id_mona
Exemple:
Enter file in which to save the key (/Users/miliya.ai/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/miliya.ai/.ssh/id_ed25519
Your public key has been saved in /Users/miliya.ai/.ssh/id_ed25519.pub
Peu important après...
Finalement, envoyer la clé publique,le contenu du fichier (~/.ssh/id_mona.pub) et demander à Raouf (bencherr bencherr@iro.umontreal.ca) avec l'appui de Lena
Host arcade
Hostname arcade.iro.umontreal.ca
User {ton-username-du-DIRO} (https://support.iro.umontreal.ca/mdp/)
Host picasso
HostName picasso
ProxyJump arcade
User mona
IdentityFile ~/.ssh/id_mona
Exemple:
Host arcade
HostName arcade.iro.umontreal.ca
User aimiliya
Host picasso
Hostname picasso
ProxyJump arcade
User mona
IdentityFile ~/.ssh/ed25519
Tout d’abord, afin de mieux utiliser Laravel, le framework de développement web PHP, nous utiliserons Laravel Sail, une interface légère en ligne de commande qui interagit avec l’environnement de développement Docker.
Bien sûr, vérifier si on a bien installé Docker en utilisant
docker-compose --version
Ça l'aire bon si ça affiche comme:Docker Compose version v2.17.3
Téléchargez et exécutez maintenant la commande du script via curl et exécutez le script avec l'interpréteur Bash.
curl -s " https://laravel.build/mona-server?with=mariadb,redis,meilisearch,selenium" | bash
Cloner le dépôt githubgit clone git@github.com:MaisonMONA/mona-server.git
Tourner au ton compte github maintenant, dans le volet SSH and GPG keys, ajouter ton SSH keys
Installez le package laravel/sail dans le projet Laravel Sail et marquez-le comme package de dépendances pour l'environnement de développement.
./vendor/bin/sail composer require laravel/sail --dev
Utilisez Composer pour installer le package laravel/sanctum dans le projet Laravel Sail afin d'utiliser l'authentification Sanctum dans le projet
./vendor/bin/sail composer require laravel/sanctum
Ça devrait bon maintenant, Let's gogogogo
1. ./vendor/bin/sail up
: Cette commande est utilisée pour démarrer l'environnement Sail.
Il démarrera tous les services définis dans le projet (tels que les serveurs Web, les bases de données,
etc.)
et les laissera s'exécuter dans des conteneurs Docker. Habituellement, l'exécution de cette commande
affichera
la sortie du journal du service dans la fenêtre actuelle du terminal, ce qui facilitera la visualisation de
l'état d'exécution du service.
2. ./vendor/bin/sail up -d
: Cette commande est similaire à la commande précédente,
mais utilise l'option `-d`, qui signifie « détaché » (mode détaché). Cela entraînera le démarrage de
l'environnement Sail en arrière-plan et la sortie du journal du service ne sera pas affichée dans la fenêtre
du terminal actuel. Cela maintient la fenêtre du terminal disponible et non occupée par la sortie du journal
du service.
3. ./vendor/bin/sail down
: Cette commande est utilisée pour arrêter l'environnement Sail.
L'exécution de cette commande arrêtera tous les services définis dans le projet et supprimera les conteneurs
Docker associés. Cela peut être utilisé pour libérer des ressources lorsque le service n'est plus nécessaire
pour s'exécuter, ou pour nettoyer lorsque l'environnement doit être redémarré.