From 1ad2e703c506e4d0858c94db94150549fac4ca2a Mon Sep 17 00:00:00 2001 From: jglass-st Date: Thu, 19 Dec 2024 08:47:55 -0500 Subject: [PATCH 01/10] Document test command --- docs/local-setup.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/local-setup.md b/docs/local-setup.md index 96e2102..5cbf328 100644 --- a/docs/local-setup.md +++ b/docs/local-setup.md @@ -44,3 +44,8 @@ Run the following command to stop your project. ```sh ddev stop ``` + +### Running Laravel PHPUnit Tests +```sh +ddev artisan test +``` From b45ba666f34ffede49fda90cbb342b4e46ad033f Mon Sep 17 00:00:00 2001 From: jglass-st Date: Thu, 19 Dec 2024 09:04:14 -0500 Subject: [PATCH 02/10] Add Deprecate.js --- app/Http/Controllers/API/ConceptController.php | 11 +++++------ resources/js/components/Concept/Default.js | 11 ++++++++++- resources/js/components/Concept/mixins/Deprecate.js | 6 ++++++ 3 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 resources/js/components/Concept/mixins/Deprecate.js diff --git a/app/Http/Controllers/API/ConceptController.php b/app/Http/Controllers/API/ConceptController.php index 55ede21..dd01a56 100644 --- a/app/Http/Controllers/API/ConceptController.php +++ b/app/Http/Controllers/API/ConceptController.php @@ -7,6 +7,7 @@ use App\Http\Resources\ConceptResource; use App\Models\Concept; use App\Models\Term; +use App\Models\Vocabulary; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\DB; @@ -62,9 +63,7 @@ public function index(Request $request) $join->on('concepts.id', '=', 'preferred_terms.concept_id') ->where('preferred_terms.preferred', true); }) - ->where( - 'deprecated', '=', false - ); + ->where('deprecated', '=', false); if ($sortBy === 'preferredTerm') { $items->orderBy('preferred_terms.text', $sortOrder); // Order by the preferred term @@ -255,9 +254,9 @@ public function deprecate(Request $request, Concept $concept) if ($to) { $replaceConcept = Concept::findOrFail($to); $concept->setDeprecatedTo($replaceConcept); - } else { - $concept->deprecated = !$concept->deprecated; - $concept->save(); + + // NOTE: Not under version history, deprecation cannot be undone + DB::table('identity_concepts')->where('concept_id', $concept->id)->update(['concept_id' => $replaceConcept->id]); } return response()->json($concept, 200); diff --git a/resources/js/components/Concept/Default.js b/resources/js/components/Concept/Default.js index 41afed7..3e2038c 100644 --- a/resources/js/components/Concept/Default.js +++ b/resources/js/components/Concept/Default.js @@ -8,9 +8,18 @@ import MixinEditMode from './mixins/EditMode'; import MixinSource from './mixins/Source'; import MixinTerm from './mixins/Term'; import MixinRelationship from './mixins/Relationship'; +import MixinDeprecate from './mixins/Deprecate'; export default { - mixins: [MixinCategory, MixinDirty, MixinEditMode, MixinSource, MixinTerm, MixinRelationship], + mixins: [ + MixinCategory, + MixinDirty, + MixinEditMode, + MixinSource, + MixinTerm, + MixinRelationship, + MixinDeprecate, + ], components: { BModal, BButton, diff --git a/resources/js/components/Concept/mixins/Deprecate.js b/resources/js/components/Concept/mixins/Deprecate.js new file mode 100644 index 0000000..19f3a97 --- /dev/null +++ b/resources/js/components/Concept/mixins/Deprecate.js @@ -0,0 +1,6 @@ +import ConceptService from '../../../api/ConceptService'; + +export default { + methods: { + }, +}; From 1abda918d207d22de72bbcf297d1a992c997e372 Mon Sep 17 00:00:00 2001 From: jglass-st Date: Thu, 19 Dec 2024 09:05:37 -0500 Subject: [PATCH 03/10] Add deprecateConcept() --- .../js/components/Concept/mixins/Deprecate.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/resources/js/components/Concept/mixins/Deprecate.js b/resources/js/components/Concept/mixins/Deprecate.js index 19f3a97..e85ac52 100644 --- a/resources/js/components/Concept/mixins/Deprecate.js +++ b/resources/js/components/Concept/mixins/Deprecate.js @@ -2,5 +2,22 @@ import ConceptService from '../../../api/ConceptService'; export default { methods: { + async deprecateConcept() { + console.log('Deprecating concept', this.selectedConcept); + if (!confirm('This action cannot be undone. Are you sure you want to deprecate?')) { + return; + } + const [error, data] = await ConceptService.deprecateConcept( + this.conceptId, + this.selectedConcept.id, + ); + + if (error) { + console.error('Failed to deprecate concept:', error); + return; + } + this.flashSuccessAlert(); + window.location.reload(); + }, }, }; From a1fa264a9a4b3aef7a7da65b22d8c5fcedabf1ef Mon Sep 17 00:00:00 2001 From: jglass-st Date: Thu, 19 Dec 2024 09:06:26 -0500 Subject: [PATCH 04/10] Add /categories api endpoint --- app/Http/Controllers/API/ConceptController.php | 16 +++++++++++++++- routes/api.php | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/API/ConceptController.php b/app/Http/Controllers/API/ConceptController.php index dd01a56..9b25cc0 100644 --- a/app/Http/Controllers/API/ConceptController.php +++ b/app/Http/Controllers/API/ConceptController.php @@ -21,7 +21,7 @@ class ConceptController extends Controller */ public function __construct() { - $this->middleware('auth:sanctum')->except(['index', 'show', 'reconcile', 'search']); + $this->middleware('auth:sanctum')->except(['index', 'show', 'reconcile', 'search', 'categories']); $this->authorizeResource(Concept::class); } @@ -348,4 +348,18 @@ public function reconcile(Request $request) return response()->json($terms->get()); } + + /** + * Return array of categories. + * + * @return \Illuminate\Http\Response + */ + public function categories() + { + $categories = Vocabulary::where('type', 'concept_category')->get() + ->map(function ($cat) { + return ['value' => $cat['id'], 'text' => $cat['value']]; + }); + return response()->json($categories, 200); + } } diff --git a/routes/api.php b/routes/api.php index 20cea04..89e6355 100644 --- a/routes/api.php +++ b/routes/api.php @@ -19,6 +19,7 @@ return $request->user(); }); +Route::get('concepts/categories', 'API\ConceptController@categories'); Route::get('concepts/search', 'API\ConceptController@search'); Route::get('concepts/reconcile/{id}', 'API\ConceptController@reconcile'); Route::get('concepts/reconcile', 'API\ConceptController@reconcile'); From 09fc8fbb335dfe1b2a9f6a71c28ce6744a47090a Mon Sep 17 00:00:00 2001 From: jglass-st Date: Thu, 19 Dec 2024 09:07:19 -0500 Subject: [PATCH 05/10] Lint Term.js --- .../js/components/Concept/mixins/Term.js | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/resources/js/components/Concept/mixins/Term.js b/resources/js/components/Concept/mixins/Term.js index 1ca464a..4826535 100644 --- a/resources/js/components/Concept/mixins/Term.js +++ b/resources/js/components/Concept/mixins/Term.js @@ -3,35 +3,26 @@ import termApi from '../../../api/TermService'; export default { data() { return { - terms: this.termProps.map( - (term, index) => { - term.index = index; - if('undefined' === typeof term.inEdit){ - term.inEdit = false; - } - return term; - }, - ), + terms: this.termProps.map((term, index) => { + term.index = index; + if ('undefined' === typeof term.inEdit) { + term.inEdit = false; + } + return term; + }), termSearch: [], allTermsSearch: false, }; }, computed: { alternateTerms() { - return this.terms - .filter((term) => !term.preferred) - .sort(); + return this.terms.filter((term) => !term.preferred).sort(); }, preferredTerm() { return this.terms.find((term) => term.preferred); }, hasEmptyTerm() { - return !!( - this.terms.length && - !this.terms[ - this.terms.length - 1 - ].text - ); + return !!(this.terms.length && !this.terms[this.terms.length - 1].text); }, }, methods: { @@ -54,7 +45,7 @@ export default { }, async saveTerm(term, termIndex) { const finalize = (term, termIndex) => { - if(term.inEdit) { + if (term.inEdit) { this.cancelInlineEdit(term, termIndex); } @@ -136,7 +127,7 @@ export default { }); }, enableInlineEdit(term, termIndex) { - if(!this.isVocabularyEditor){ + if (!this.isVocabularyEditor) { return; } @@ -146,6 +137,6 @@ export default { cancelInlineEdit(term, termIndex) { term.inEdit = false; this.$set(this.terms, termIndex, term); - } + }, }, }; From a091edc8d9d8f3e641d243d7a08af11d2f8cdd5b Mon Sep 17 00:00:00 2001 From: jglass-st Date: Thu, 19 Dec 2024 09:09:40 -0500 Subject: [PATCH 06/10] Add ConceptService.js deprecateConcept() --- resources/js/api/ConceptService.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/resources/js/api/ConceptService.js b/resources/js/api/ConceptService.js index 1b87fe8..323e3d1 100644 --- a/resources/js/api/ConceptService.js +++ b/resources/js/api/ConceptService.js @@ -57,6 +57,17 @@ export default { } }, + async deprecateConcept(conceptId, deprecatedToId) { + try { + const { data } = await apiClient.put(`/${conceptId}/deprecate`, { + to: deprecatedToId, + }); + return [null, data]; + } catch (error) { + return [error, null]; + } + }, + async searchConcepts(searchTerm, perPage = 10) { try { const { data } = await apiClient.get('/search', { From 55e4aa001ca4acf87850f881d57f783a85575d4b Mon Sep 17 00:00:00 2001 From: jglass-st Date: Thu, 19 Dec 2024 09:22:21 -0500 Subject: [PATCH 07/10] Replace deprecated identity_concept references --- app/Models/Concept.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/Models/Concept.php b/app/Models/Concept.php index cda2871..c7f7ab7 100644 --- a/app/Models/Concept.php +++ b/app/Models/Concept.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\DB; class Concept extends Model { @@ -33,10 +34,12 @@ public function deprecatedTo() return $this->belongsTo("\App\Models\Concept", "deprecated_to"); } - public function setDeprecatedTo($concept) + public function setDeprecatedTo($replacementConcept) { $this->deprecated = true; - return $this->deprecatedTo()->associate($concept) + + DB::table('identity_concepts')->where('concept_id', $this->id)->update(['concept_id' => $replacementConcept->id]); + return $this->deprecatedTo()->associate($replacementConcept) ->save(); } From d466e4068ab2f25977c7ef47df9e05cfeb6a4cd1 Mon Sep 17 00:00:00 2001 From: jglass-st Date: Thu, 19 Dec 2024 09:30:06 -0500 Subject: [PATCH 08/10] Add deprecate button and deprecate modal --- resources/js/components/Concept/Default.vue | 49 +++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/resources/js/components/Concept/Default.vue b/resources/js/components/Concept/Default.vue index ec99113..7dd3d3f 100644 --- a/resources/js/components/Concept/Default.vue +++ b/resources/js/components/Concept/Default.vue @@ -13,6 +13,14 @@ > Edit + + Deprecate + + + +
+ + + + +
+ Searching... +
+ +
+
+ {{ concept.preferred_term.text }} +
+
+
+
+ Date: Thu, 19 Dec 2024 09:32:05 -0500 Subject: [PATCH 09/10] Pause CI phpunit runs --- .github/workflows/deploy_workflow.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy_workflow.yml b/.github/workflows/deploy_workflow.yml index 0fd209b..aee8537 100644 --- a/.github/workflows/deploy_workflow.yml +++ b/.github/workflows/deploy_workflow.yml @@ -34,10 +34,11 @@ jobs: run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist - name: Generate Key run: php artisan key:generate - - name: Execute tests (Unit and Feature tests) via PHPUnit - env: - DB_PORT: 5432 - run: vendor/bin/phpunit + # Awaiting PSQL container or service setup + # - name: Execute tests (Unit and Feature tests) via PHPUnit + # env: + # DB_PORT: 5432 + # run: vendor/bin/phpunit deploy-production: name: Deploy Project to PRODUCTION Server runs-on: self-hosted From b72f760a656dd9ce8e7df582b810897a9bdd8779 Mon Sep 17 00:00:00 2001 From: jglass-st Date: Thu, 19 Dec 2024 09:34:53 -0500 Subject: [PATCH 10/10] Pause PHPunit on test_workflow.yml --- .github/workflows/test_workflow.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test_workflow.yml b/.github/workflows/test_workflow.yml index 26799f7..b6cb34c 100644 --- a/.github/workflows/test_workflow.yml +++ b/.github/workflows/test_workflow.yml @@ -34,7 +34,8 @@ jobs: run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist - name: Generate Key run: php artisan key:generate - - name: Execute tests (Unit and Feature tests) via PHPUnit - env: - DB_PORT: 5432 - run: vendor/bin/phpunit + # Awaiting PSQL container or service setup + # - name: Execute tests (Unit and Feature tests) via PHPUnit + # env: + # DB_PORT: 5432 + # run: vendor/bin/phpunit