diff --git a/.env b/.env
index b24b3fc..602b71f 100644
--- a/.env
+++ b/.env
@@ -17,3 +17,5 @@ GOOGLE_APPLICATION_CREDENTIALS=
GOOGLE_SHEET_ID=
GOOGLE_SHEET_EXPORT_ID=
GOOGLE_SHEET_SCRIPT_URL=
+
+API_URL=
\ No newline at end of file
diff --git a/.github/workflows/slovensko_digital_ci.yml b/.github/workflows/slovensko_digital_ci.yml
index 45444a7..3eadbbb 100644
--- a/.github/workflows/slovensko_digital_ci.yml
+++ b/.github/workflows/slovensko_digital_ci.yml
@@ -28,7 +28,8 @@ jobs:
with:
bundler-cache: true
- - run: bundle exec rails db:create db:schema:load --trace
+ - run: bundle exec rails db:create db:structure:load --trace
+ - run: RAILS_ENV=test_datahub bundle exec rails db:create db:structure:load DB_STRUCTURE=db/datahub_structure.sql --trace
- run: bundle exec rspec
gitlab-push:
diff --git a/.ruby-version b/.ruby-version
index 37c2961..6a81b4c 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-2.7.2
+2.7.8
diff --git a/Dockerfile b/Dockerfile
index eca10a4..7070182 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM ruby:2.7.2
+FROM ruby:2.7.8
# Install packages
RUN apt-get update && apt-get install -y build-essential nodejs libpq-dev
diff --git a/Gemfile b/Gemfile
index d1e5e00..fb0abbc 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,6 @@
source 'https://rubygems.org'
-ruby '2.7.2'
+ruby '2.7.8'
git_source(:github) do |repo_name|
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
diff --git a/app/assets/images/acf.png b/app/assets/images/acf.png
new file mode 100644
index 0000000..a9e0602
Binary files /dev/null and b/app/assets/images/acf.png differ
diff --git a/app/assets/images/icons/ai_logo.png b/app/assets/images/icons/ai_logo.png
new file mode 100644
index 0000000..34fded2
Binary files /dev/null and b/app/assets/images/icons/ai_logo.png differ
diff --git a/app/assets/images/icons/metais_logo.png b/app/assets/images/icons/metais_logo.png
new file mode 100644
index 0000000..d68c929
Binary files /dev/null and b/app/assets/images/icons/metais_logo.png differ
diff --git a/app/assets/images/icons/sd_logo.png b/app/assets/images/icons/sd_logo.png
new file mode 100644
index 0000000..178fca5
Binary files /dev/null and b/app/assets/images/icons/sd_logo.png differ
diff --git a/app/assets/images/pontis_white.svg b/app/assets/images/pontis_white.svg
new file mode 100644
index 0000000..9a636de
--- /dev/null
+++ b/app/assets/images/pontis_white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/assets/images/skdigital_biela.svg b/app/assets/images/skdigital_biela.svg
new file mode 100644
index 0000000..a93f86c
--- /dev/null
+++ b/app/assets/images/skdigital_biela.svg
@@ -0,0 +1,31 @@
+
+
+
+ skdigital_farba copy
+ Created with Sketch.
+
+
+
+
\ No newline at end of file
diff --git a/app/assets/images/us_embassy.png b/app/assets/images/us_embassy.png
new file mode 100644
index 0000000..d06b56f
Binary files /dev/null and b/app/assets/images/us_embassy.png differ
diff --git a/app/assets/javascripts/metais/projects.js b/app/assets/javascripts/metais/projects.js
new file mode 100644
index 0000000..a928944
--- /dev/null
+++ b/app/assets/javascripts/metais/projects.js
@@ -0,0 +1,61 @@
+$(document).ready(function() {
+ function openFilterSection(sectionId) {
+ var $filterSection = $('#' + sectionId);
+ if ($filterSection.length) {
+ $filterSection.toggleClass('show');
+ }
+ }
+
+ var urlParams = new URLSearchParams(window.location.search);
+ var status = urlParams.get('status');
+ var guarantor = urlParams.get('guarantor');
+ var code = urlParams.get('code');
+ var minPrice = urlParams.get('min_price');
+ var maxPrice = urlParams.get('max_price');
+ var hasEvaluation = urlParams.get('has_evaluation');
+
+ if (status || guarantor || code || minPrice || maxPrice || hasEvaluation) {
+ openFilterSection('filterForm');
+ }
+
+ $('#filterButton').click(function() {
+ openFilterSection('filterForm');
+ });
+});
+
+$(document).ready(function() {
+ function updateDropdownColors() {
+ var urlParams = new URLSearchParams(window.location.search);
+
+ var filters = [
+ { param: 'status', buttonId: 'dropdownMenuButton' },
+ { param: 'guarantor', buttonId: 'dropdownMenuButton2' },
+ { param: 'code', buttonId: 'dropdownMenuButton3' },
+ { param: 'min_price', buttonId: 'dropdownMenuButton4' },
+ { param: 'has_evaluation', buttonId: 'dropdownMenuButton5' }
+ ];
+
+ filters.forEach(function(filter) {
+ var filterValue = urlParams.get(filter.param);
+ var $dropdownButton = $('#' + filter.buttonId);
+
+ if (filterValue) {
+ $dropdownButton.addClass('btn-active').removeClass('btn-outline-primary').addClass('btn-primary');
+ } else {
+ $dropdownButton.removeClass('btn-active').addClass('btn-outline-primary').removeClass('btn-primary');
+ }
+ });
+ }
+
+ updateDropdownColors();
+});
+
+function removeFilter(filter) {
+ if (filter === 'price') {
+ document.getElementById('min_price').value = '';
+ document.getElementById('max_price').value = '';
+ } else {
+ document.getElementById(filter).value = '';
+ }
+ document.getElementById('form').submit();
+ }
\ No newline at end of file
diff --git a/app/assets/javascripts/projects.js b/app/assets/javascripts/projects.js
index 9f9d08f..36e1c25 100644
--- a/app/assets/javascripts/projects.js
+++ b/app/assets/javascripts/projects.js
@@ -13,3 +13,37 @@ document.addEventListener("turbolinks:load", function() {
document.addEventListener("turbolinks:before-render", function() {
window.printCalled = false;
});
+
+document.addEventListener('turbolinks:load', function () {
+ var sortDirectionField = document.getElementById('sort_direction');
+ var sortButton = document.getElementById('ascdesctoggle');
+ var upIcon = document.getElementById('up-icon');
+ var downIcon = document.getElementById('down-icon');
+
+ if (sortDirectionField && sortButton && upIcon && downIcon) {
+ var toggleState = sortDirectionField.value || 'desc';
+
+ function updateIcons() {
+ if (toggleState === 'desc') {
+ upIcon.style.fill = 'rgba(100, 100, 100, 0.4)';
+ downIcon.style.fill = 'rgb(56, 94, 255)';
+ } else {
+ upIcon.style.fill = 'rgb(56, 94, 255)';
+ downIcon.style.fill = 'rgba(100, 100, 100, 0.4)';
+ }
+ }
+
+ updateIcons();
+
+ sortButton.addEventListener('click', function (event) {
+ event.preventDefault();
+
+ toggleState = (toggleState === 'desc') ? 'asc' : 'desc';
+ sortDirectionField.value = toggleState;
+
+ updateIcons();
+
+ document.getElementById('form').submit();
+ });
+ }
+});
\ No newline at end of file
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 82b5d3c..1d98e9c 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -6,7 +6,15 @@
@import 'projects';
@import 'static';
-
+@import 'schedule';
+@import 'statuses';
+@import 'navbar';
+@import 'footer';
+
+h1, h2, h3, h4, h5, h6 {
+ font-family: $headings-font-family;
+ font-weight: $font-weight-bold;
+}
.btn-outline-primary:hover {
border-color: $link-hover-color;
@@ -14,48 +22,15 @@
background-color: transparent;
}
-.navbar {
- @include media-breakpoint-up(md) {
- .navbar-brand img {
- width: 240px;
- }
- }
-}
-
-.container {
- max-width: 1024px;
+.table-title-item, .table-price {
+ font-family: $headings-font-family;
+ font-size: large;
+ color: $black;
}
-footer {
- background-color: $gray-dark;
- color: $white;
-
- a {
- color: $white;
- &:hover {
- color: $white;
- }
- }
-
- .fa-inverse {
- color: $gray-dark;
- }
-
- .icons {
- a:hover {
- text-decoration: none;
- }
- }
+#filterForm.collapsing {
+ transition: height 0.5s ease-out;
- .newsletter {
- .legal {
- font-size: $small-font-size;
-
- a {
- text-decoration: underline;
- }
- }
- }
}
h1 .badge-alpha {
diff --git a/app/assets/stylesheets/footer.scss b/app/assets/stylesheets/footer.scss
new file mode 100644
index 0000000..ce3155b
--- /dev/null
+++ b/app/assets/stylesheets/footer.scss
@@ -0,0 +1,85 @@
+footer {
+ background-color: $light_black;
+ color: $white;
+
+ a {
+ color: $white;
+ &:hover {
+ color: $white;
+ }
+ }
+
+ .fa-inverse {
+ color: $gray-dark;
+ }
+
+ .icons {
+ a:hover {
+ text-decoration: none;
+ }
+ }
+
+ .newsletter {
+ .legal {
+ font-size: $small-font-size;
+
+ a {
+ text-decoration: underline;
+ }
+ }
+ }
+}
+
+.footer-border {
+ border-left: 1px solid;
+ padding-left: 3rem;
+}
+
+.footer-sk {
+ padding-right: 3rem;
+}
+
+.pontis-logo {
+ width: 125px;
+}
+
+.us-logo, .acf-logo {
+ width: 200px;
+}
+@media (max-width: 1200px) {
+ .pontis-logo {
+ width: 100px;
+ }
+ .us-logo, .acf-logo {
+ width: 175px;
+ }
+}
+@media (max-width: 992px) {
+ .pontis-logo {
+ width: 75px;
+ }
+ .us-logo, .acf-logo {
+ width: 150px;
+ }
+}
+@media (max-width: 768px) {
+ .responsive-img {
+ width: 125px;
+ }
+ .footer-border {
+ border-top: 1px solid;
+ border-left: none;
+ padding-left: 2rem;
+ padding-right: 2rem;
+ padding-top: 3rem;
+ }
+ .footer-sk {
+ padding-right: 0;
+ padding-bottom: 3rem;
+ }
+}
+@media (max-width: 576px) {
+ .responsive-img {
+ width: 100px;
+ }
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/google-fonts.scss b/app/assets/stylesheets/google-fonts.scss
index 44c13bf..749a37f 100644
--- a/app/assets/stylesheets/google-fonts.scss
+++ b/app/assets/stylesheets/google-fonts.scss
@@ -1,3 +1,5 @@
+@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');
+
/* cyrillic-ext */
@font-face {
font-family: 'Merriweather';
diff --git a/app/assets/stylesheets/navbar.scss b/app/assets/stylesheets/navbar.scss
new file mode 100644
index 0000000..d047c9e
--- /dev/null
+++ b/app/assets/stylesheets/navbar.scss
@@ -0,0 +1,29 @@
+.navbar {
+ @include media-breakpoint-up(md) {
+ .navbar-brand img {
+ width: 100%;
+ }
+ }
+}
+
+.navbar-light .navbar-nav .nav-link {
+ font-weight: $font-weight-semi-bold;
+ transition: 0.2s ease-in-out;
+ color: $black;
+}
+
+.navbar-light .navbar-nav .nav-link:focus {
+ color: $black;
+}
+
+.navbar-light .navbar-nav .nav-link:hover {
+ color: $blue;
+}
+
+.navbar-light .navbar-nav .nav-link:active {
+ color: $blue;
+}
+
+.navbar-light .navbar-nav .nav-item.active .nav-link {
+ color: $blue;
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/projects.scss b/app/assets/stylesheets/projects.scss
index 52293af..2c8acfe 100644
--- a/app/assets/stylesheets/projects.scss
+++ b/app/assets/stylesheets/projects.scss
@@ -1,6 +1,6 @@
#project-show {
@include media-breakpoint-up(md) {
- padding-top: 6rem;
+ padding-top: 3rem;
}
.full-description {
@@ -109,13 +109,12 @@ img.emoji {
}
.accordion:after {
- font-family: 'Merriweather';
- font-size: $h3-font-size;
- content: "-";
+ color: $black;
+ font-size: medium;
+ text-decoration: underline;
+ content: "Schovať hodnotené kritériá";
}
.accordion.collapsed:after {
- font-family: 'Merriweather';
- font-size: $h3-font-size;
- content: "+";
+ content: "Zobraziť hodnotené kritériá";
}
diff --git a/app/assets/stylesheets/schedule.scss b/app/assets/stylesheets/schedule.scss
new file mode 100644
index 0000000..dc40fe4
--- /dev/null
+++ b/app/assets/stylesheets/schedule.scss
@@ -0,0 +1,100 @@
+.card {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ min-width: 100%;
+ word-wrap: break-word;
+}
+
+.card-body {
+ flex: 1 1 auto;
+ padding: 1.25rem;
+}
+.vertical-timeline {
+ width: 100%;
+ position: relative;
+ padding: 1.5rem 0 1rem;
+}
+
+.vertical-timeline::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 67px;
+ height: 100%;
+ width: 4px;
+ background: #e9ecef;
+}
+
+.vertical-timeline-element {
+ position: relative;
+ margin: 0 0 1rem;
+}
+
+.vertical-timeline--animate .vertical-timeline-element-icon.bounce-in {
+ visibility: visible;
+ animation: cd-bounce-1 .8s;
+}
+.vertical-timeline-element-icon {
+ position: absolute;
+ top: 0;
+ left: 60px;
+}
+
+.vertical-timeline-element-icon .badge-dot-xl {
+ box-shadow: 0 0 0 5px #fff;
+}
+
+.badge-dot-xl {
+ width: 18px;
+ height: 18px;
+ position: relative;
+}
+.badge:empty {
+ display: none;
+}
+
+
+.badge-dot-xl::before {
+ content: '';
+ width: 10px;
+ height: 10px;
+ border-radius: .25rem;
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ margin: -5px 0 0 -5px;
+ background: #fff;
+}
+
+.vertical-timeline-element-content {
+ position: relative;
+ margin-left: 90px;
+ font-size: .8rem;
+}
+
+.vertical-timeline-element-content .timeline-title {
+ font-size: .8rem;
+ text-transform: uppercase;
+ margin: 0 0 .5rem;
+ padding: 2px 0 0;
+ font-weight: bold;
+}
+
+.vertical-timeline-element-content .vertical-timeline-element-date {
+ display: block;
+ position: absolute;
+ left: -100px;
+ top: 0;
+ padding-right: 10px;
+ text-align: right;
+ color: #000000;
+ font-size: .7619rem;
+ white-space: nowrap;
+}
+
+.vertical-timeline-element-content:after {
+ content: "";
+ display: table;
+ clear: both;
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/statuses.scss b/app/assets/stylesheets/statuses.scss
new file mode 100644
index 0000000..5bb3566
--- /dev/null
+++ b/app/assets/stylesheets/statuses.scss
@@ -0,0 +1,85 @@
+.state-badge {
+ border: 1px solid;
+ border-radius: 5px;
+ padding: 2px 10px;
+ font-size: 0.75rem;
+ font-weight: 500;
+}
+
+.status-1 {
+ background-color: rgb(254, 242, 242);
+ color: rgb(185, 28, 28);
+ border-color: rgba(220, 38, 38, 0.25);
+}
+
+.status-2 {
+ background-color: rgb(240, 253, 244);
+ color: rgb(21, 128, 61);
+ border-color: rgba(22, 163, 74, 0.25);
+}
+
+.status-3 {
+ background-color: rgb(239, 246, 255);
+ color: rgb(29, 78, 216);
+ border-color: rgba(29, 78, 216, 0.25);
+}
+
+.status-4 {
+ background-color: rgb(254, 252, 232);
+ color: rgb(133, 77, 14);
+ border-color: rgba(202, 138, 4, 0.25);
+}
+
+.status-5 {
+ background-color: rgb(238, 242, 255);
+ color: rgb(67, 56, 202);
+ border-color: rgba(67, 56, 202, 0.25);
+}
+
+.status-6 {
+ background-color: rgb(250, 245, 255);
+ color: rgb(126, 34, 206);
+ border-color: rgba(126, 34, 206, 0.25);
+}
+
+.status-7 {
+ background-color: rgb(253, 242, 248);
+ color: rgb(190, 24, 93);
+ border-color: rgba(190, 24, 93, 0.25);
+}
+
+.status-8 {
+ background-color: rgb(255, 247, 237);
+ color: rgb(194, 65, 12);
+ border-color: rgba(194, 65, 12, 0.25);
+}
+
+.status-9 {
+ background-color: rgb(240, 253, 250);
+ color: rgb(20, 184, 166);
+ border-color: rgba(20, 184, 166, 0.25);
+}
+
+.status-10 {
+ background-color: rgb(236, 254, 255);
+ color: rgb(6, 182, 212);
+ border-color: rgba(6, 182, 212, 0.25);
+}
+
+.status-11 {
+ background-color: rgb(253, 244, 255);
+ color: rgb(162, 28, 175);
+ border-color: rgba(162, 28, 175, 0.25);
+}
+
+.status-12 {
+ background-color: rgb(255, 241, 242);
+ color: rgb(225, 29, 72);
+ border-color: rgba(225, 29, 72, 0.25);
+}
+
+.status-13 {
+ background-color: rgb(247, 254, 231);
+ color: rgb(132, 204, 22);
+ border-color: rgba(132, 204, 22, 0.25);
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/variables.scss b/app/assets/stylesheets/variables.scss
index 6a1f64a..6e55215 100644
--- a/app/assets/stylesheets/variables.scss
+++ b/app/assets/stylesheets/variables.scss
@@ -1,3 +1,5 @@
+$black: #000000;
+$light_black: #141617;
$gray-dark: #373a3c;
$gray: #55595c;
$gray-light: #818a91;
@@ -5,7 +7,7 @@ $gray-lighter: #c4c3ca;
$gray-lightest: #f4f4f4;
$color-highlight: lightgoldenrodyellow;
-$blue: #3a67e8;
+$blue: #385EFF;
$orange: #ffac33;
$theme-colors: (
@@ -19,10 +21,12 @@ $theme-colors: (
dark: $gray-dark
);
-$headings-font-family: 'Merriweather', 'Georgia', 'Times New Roman', serif;
+$headings-font-family: 'Roboto Mono', 'Georgia', 'Times New Roman', serif;
$font-family-sans-serif: 'Roboto', sans-serif;
$font-weight-normal: 300;
+$font-weight-semi-bold: 500;
$font-weight-bold: 700;
+$font-weight-black: 900;
$modal-backdrop-bg: white;
$modal-backdrop-opacity: .7;
diff --git a/app/controllers/admin/metais/project_origins_controller.rb b/app/controllers/admin/metais/project_origins_controller.rb
new file mode 100644
index 0000000..fc6351e
--- /dev/null
+++ b/app/controllers/admin/metais/project_origins_controller.rb
@@ -0,0 +1,202 @@
+class Admin::Metais::ProjectOriginsController < ApplicationController
+ before_action :set_project, only: [:craete, :edit, :update, :add_event, :add_supplier, :add_link, :add_document, :remove_event, :remove_supplier, :remove_link, :remove_document, :update_group_order]
+
+ def create
+ @project_origin = @project.project_origins.build(project_origin_params)
+ if @project_origin.save
+ redirect_to admin_metais_project_path(@project), notice: 'Projekt bol úspešne vytvorený.'
+ else
+ render :new
+ end
+ end
+
+ def edit
+ @project_info = @project.get_project_origin_info
+
+ @project_origins = @project.project_origins
+ @project_origin = @project_origins.find(params[:id])
+
+ @assumption_events = @project_origins.flat_map { |project_origin| project_origin.events.assumpted }
+ @real_events = @project_origins.flat_map { |project_origin| project_origin.events.real }
+
+ @combined_suppliers = @project_origins.flat_map(&:suppliers).sort_by { |supplier| supplier.date || Time.zone.parse('2999-12-31') }
+ @combined_links = @project_origins.flat_map(&:links)
+ @combined_documents = @project_origins.flat_map(&:documents)
+
+ @grouped_documents = @combined_documents.group_by(&:description).sort_by { |description, docs| docs.first.group_order || Float::INFINITY }
+ end
+
+ def update
+ @project_origin = @project.project_origins.find(params[:id])
+ current_project_info = @project.get_project_origin_info
+
+ changed_params = detect_changes(current_project_info, project_origin_params.to_h)
+
+ if changed_params.any? && @project_origin.update(changed_params)
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), notice: 'Projekt bol úspešne aktualizovaný.'
+ else
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), notice: 'Žiadne nové informácie v projekte.'
+ end
+ end
+
+ def add_event
+ @project_origin = @project.project_origins.find(params[:project_origin_id])
+ @event = @project_origin.events.build(event_params)
+
+ if @event.save
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), notice: 'Aktivita bola úspešné pridaná.'
+ else
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), alert: "Nastala chyba pri vytváraní aktivity: #{@event.errors.full_messages.to_sentence}"
+ end
+ end
+
+ def add_supplier
+ @project_origin = @project.project_origins.find(params[:project_origin_id])
+ @supplier = @project_origin.suppliers.build(supplier_params)
+
+ if @supplier.save
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), notice: 'Dodávateľ bol úspešné pridaný.'
+ else
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), alert: "Nastala chyba pri vytváraní dodávateľa: #{@event.errors.full_messages.to_sentence}"
+ end
+ end
+
+ def add_link
+ @project_origin = @project.project_origins.find(params[:project_origin_id])
+ @link = @project_origin.links.build(link_params)
+
+ if @link.save
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), notice: 'Link bol úspešné pridaný.'
+ else
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), alert: "Nastala chyba pri vytváraní linku: #{@event.errors.full_messages.to_sentence}"
+ end
+ end
+
+ def add_document
+ @project_origin = @project.project_origins.find(params[:project_origin_id])
+ @document = @project_origin.documents.build(document_params)
+
+ if @document.save
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), notice: 'Dokument bol úspešné pridaný.'
+ else
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), alert: "Nastala chyba pri vytváraní dokumentu: #{@event.errors.full_messages.to_sentence}"
+ end
+ end
+
+ def remove_event
+ @project_origin = @project.project_origins.find(params[:project_origin_id])
+ @project_event = @project_origin.events.find(params[:event_id])
+
+ if @project_event.destroy
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), notice: 'Aktivita bola úspešne odstránená z harmonogramu.'
+ else
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), alert: 'Nastala chyba pri odstraňovaní aktivity z harmonogramu.'
+ end
+ end
+
+ def remove_supplier
+ @project_origin = @project.project_origins.find(params[:project_origin_id])
+ @project_supplier = @project_origin.suppliers.find(params[:supplier_id])
+
+ if @project_supplier.destroy
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), notice: 'Dodávateľ bol úspešne odstránený.'
+ else
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), alert: 'Nastala chyba pri odstraňovaní dodávateľa.'
+ end
+ end
+
+ def remove_link
+ @project_origin = @project.project_origins.find(params[:project_origin_id])
+ @project_link = @project_origin.links.find(params[:link_id])
+
+ if @project_link.destroy
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), notice: 'Link bol úspešne odstránený.'
+ else
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), alert: 'Nastala chyba pri odstraňovaní linku.'
+ end
+ end
+
+ def remove_document
+ @project_origin = @project.project_origins.find(params[:project_origin_id])
+ @project_document = @project_origin.documents.find(params[:document_id])
+
+ if @project_document.destroy
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), notice: 'Dokument bol úspešne odstránený.'
+ else
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin), alert: 'Nastala chyba pri odstraňovaní dokumentu.'
+ end
+ end
+
+ def update_group_order
+ @project_origin = @project.project_origins.find(params[:project_origin_id])
+
+ description = params[:description]
+ group_order = params[:group_order].to_i
+
+ Metais::ProjectDocument.where(description: description).update_all(group_order: group_order)
+
+ flash[:notice] = "Zoskupenie dokumentov bolo úspešne updatované."
+ redirect_to edit_admin_metais_project_project_origin_path(@project, @project_origin)
+ end
+
+ private
+
+ def set_project
+ @project = Metais::Project.find(params[:project_id])
+ end
+
+ def project_origin_params
+ params.require(:metais_project_origin).permit(:title, :description, :guarantor, :project_manager, :start_date, :end_date, :status, :phase, :finance_source, :investment, :operation, :approved_investment, :approved_operation, :supplier, :supplier_cin, :targets_text, :documents_text, :links_text, project_events: [:name, :value, :date])
+ end
+
+ def event_params
+ origin_type = Metais::OriginType.find_by(name: params[:event][:origin_type])
+ event_type = Metais::ProjectEventType.find_by(name: params[:event][:event_type])
+ params.require(:event).permit(:name, :value, :date).merge(origin_type: origin_type, event_type: event_type)
+ end
+
+ def supplier_params
+ origin_type = Metais::OriginType.find_by(name: params[:supplier][:origin_type])
+ supplier_type = Metais::SupplierType.find(params[:supplier][:supplier_type])
+ params.require(:supplier).permit(:name, :value).merge(name: params[:supplier][:value], origin_type: origin_type, supplier_type: supplier_type)
+ end
+
+ def link_params
+ origin_type = Metais::OriginType.find_by(name: params[:link][:origin_type])
+ params.require(:link).permit(:name, :value).merge(origin_type: origin_type)
+ end
+
+ def document_params
+ origin_type = Metais::OriginType.find_by(name: params[:document][:origin_type])
+ params.require(:document).permit(:name, :value, :description).merge(origin_type: origin_type)
+ end
+
+ def detect_changes(current_project_info, new_params)
+ finance_source_mappings = Metais::Project::FINANCE_SOURCE_MAPPINGS
+ changed_params = {}
+
+ new_params.each do |field, new_value|
+ current_value = current_project_info.send(field)
+
+ if field == 'finance_source'
+ current_value_mapped = finance_source_mappings[current_value] || current_value
+ new_value_mapped = finance_source_mappings[new_value] || new_value
+
+ if new_value.present? && new_value_mapped.to_s.strip != current_value_mapped.to_s.strip
+ changed_params[field] = new_value
+ end
+ else
+ if %w[end_date start_date].include?(field)
+ new_value = new_value.present? ? Date.parse(new_value.to_s) : nil
+ current_value = current_value.present? ? current_value.to_date : nil
+ end
+
+ if new_value.present? && new_value.to_s.strip != current_value.to_s.strip
+ changed_params[field] = new_value
+ end
+ end
+ end
+
+ changed_params
+ end
+end
diff --git a/app/controllers/admin/metais/projects_controller.rb b/app/controllers/admin/metais/projects_controller.rb
new file mode 100644
index 0000000..d40a9b0
--- /dev/null
+++ b/app/controllers/admin/metais/projects_controller.rb
@@ -0,0 +1,31 @@
+class Admin::Metais::ProjectsController < AdminController
+ def index
+ @guarantor_counts = Metais::ProjectOrigin.guarantor_counts
+ @unique_guarantors = Metais::ProjectOrigin.unique_guarantors
+ @status_counts = Metais::ProjectOrigin.status_counts
+ @unique_statuses = Metais::ProjectOrigin.unique_statuses
+ @evaluation_counts = Metais::Project.evaluation_counts
+
+ @projects = Metais::Project.filtered_and_sorted_projects(params)
+ end
+
+ def create_human_origin
+ @project = Metais::Project.find(params[:id])
+ origin_type = Metais::OriginType.find_by(name: 'Human')
+ fail 'OriginType not found' unless origin_type
+
+ human_origin = Metais::ProjectOrigin.find_or_initialize_by(project: @project, origin_type: origin_type)
+ if human_origin.new_record?
+ human_origin.title = @project.project_origins.first.title
+ human_origin.save!
+ end
+ redirect_to edit_admin_metais_project_project_origin_path(@project, human_origin)
+ end
+
+ def run_ai_extraction
+ @project = Metais::Project.find(params[:id])
+ Metais::ProjectDataExtractionJob.perform_later(@project.uuid)
+
+ redirect_to admin_metais_projects_path, notice: 'Projekt bol zaradený na spracovanie.'
+ end
+end
diff --git a/app/controllers/admin/pages_controller.rb b/app/controllers/admin/pages_controller.rb
index d6ae40c..8de3e6a 100644
--- a/app/controllers/admin/pages_controller.rb
+++ b/app/controllers/admin/pages_controller.rb
@@ -20,6 +20,7 @@ def preview
@phase_revision = @phase.revisions.find_by(revision: @page.revisions.find_by(version: params['version']))
end
@ratings_by_type = @phase_revision.ratings.index_by(&:rating_type)
+ @project = @phase.project
else
if params['version'] == 'latest'
diff --git a/app/controllers/metais/projects_controller.rb b/app/controllers/metais/projects_controller.rb
new file mode 100644
index 0000000..9440d59
--- /dev/null
+++ b/app/controllers/metais/projects_controller.rb
@@ -0,0 +1,27 @@
+class Metais::ProjectsController < ApplicationController
+
+ def index
+ @guarantor_counts = Metais::ProjectOrigin.guarantor_counts
+ @unique_guarantors = Metais::ProjectOrigin.unique_guarantors
+ @status_counts = Metais::ProjectOrigin.status_counts
+ @unique_statuses = Metais::ProjectOrigin.unique_statuses
+ @evaluation_counts = Metais::Project.evaluation_counts
+
+ @projects = Metais::Project.filtered_and_sorted_projects(params)
+ end
+
+ def show
+ @project = Metais::Project.find(params[:id])
+ @project_info = @project.get_project_origin_info
+ @project_origins = @project.project_origins
+
+ @assumption_events = @project_origins.flat_map { |project_origin| project_origin.events.assumpted }
+ @real_events = @project_origins.flat_map { |project_origin| project_origin.events.real }
+
+ @combined_suppliers = @project_origins.flat_map(&:suppliers).sort_by { |supplier| supplier.date || Time.zone.parse('2999-12-31') }
+ @combined_links = @project_origins.flat_map(&:links)
+ @combined_documents = @project_origins.flat_map(&:documents)
+
+ @grouped_documents = @combined_documents.group_by(&:description).sort_by { |description, docs| docs.first.group_order || Float::INFINITY }
+ end
+end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 83b39cd..31f91de 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -1,7 +1,7 @@
class ProjectsController < ApplicationController
-
+
def index
- @selected_tag = params[:tag]
- @projects = Project.filtered_projects(@selected_tag, params[:sort])
+ @projects = Project.filtered_and_sorted_projects(params)
end
+
end
diff --git a/app/controllers/static_controller.rb b/app/controllers/static_controller.rb
index 4cd8d44..b540e4f 100644
--- a/app/controllers/static_controller.rb
+++ b/app/controllers/static_controller.rb
@@ -2,8 +2,8 @@ class StaticController < ApplicationController
def index
top_project_revisions = PhaseRevision.where(published: true).joins(:revision).limit(5)
- @good_projects = top_project_revisions.where('phase_revisions.redflags_count = ?', 0).order('phase_revisions.total_score::float / phase_revisions.maximum_score DESC')
- @bad_projects = top_project_revisions.order('phase_revisions.redflags_count DESC, phase_revisions.total_score::float / phase_revisions.maximum_score ASC')
+ @good_projects = top_project_revisions.where('phase_revisions.redflags_count = ? AND phase_revisions.maximum_score > 0', 0).order('phase_revisions.total_score::float / phase_revisions.maximum_score DESC')
+ @bad_projects = top_project_revisions.where('phase_revisions.maximum_score > 0').order('phase_revisions.redflags_count DESC, phase_revisions.total_score::float / phase_revisions.maximum_score ASC')
end
def about
diff --git a/app/helpers/metais/projects_helper.rb b/app/helpers/metais/projects_helper.rb
new file mode 100644
index 0000000..280499c
--- /dev/null
+++ b/app/helpers/metais/projects_helper.rb
@@ -0,0 +1,33 @@
+module Metais::ProjectsHelper
+ def origin_type_logo(origin_type)
+ if origin_type.is_a?(Integer)
+ case origin_type
+ when 3
+ ['icons/sd_logo.png', 'Ručne vyplnený údaj od Slovensko.Digital']
+ when 2
+ ['icons/ai_logo.png', 'Údaj spracovaný naším AI extraktorom']
+ else
+ ['icons/metais_logo.png', 'Údaj pochádzajúci z MetaIS portálu']
+ end
+ else
+ case origin_type.name
+ when 'Human'
+ ['icons/sd_logo.png', 'Ručne vyplnený údaj od Slovensko.Digital']
+ when 'AI'
+ ['icons/ai_logo.png', 'Údaj spracovaný naším AI extraktorom']
+ else
+ ['icons/metais_logo.png', 'Údaj pochádzajúci z MetaIS portálu']
+ end
+ end
+end
+
+ def convert_to_list(text)
+ items = text.split('\n')
+
+ list_items = items.map do |item|
+ ActionController::Base.helpers.sanitize("
#{item} ")
+ end
+
+ ActionController::Base.helpers.sanitize("")
+ end
+end
diff --git a/app/jobs/link_metais_projects_and_evaluations_job.rb b/app/jobs/link_metais_projects_and_evaluations_job.rb
new file mode 100644
index 0000000..4d1e355
--- /dev/null
+++ b/app/jobs/link_metais_projects_and_evaluations_job.rb
@@ -0,0 +1,21 @@
+class LinkMetaisProjectsAndEvaluationsJob < ApplicationJob
+ queue_as :default
+
+ def perform
+ Project.find_each do |project|
+ link_metais_project(project)
+ end
+ end
+
+ private
+
+ def link_metais_project(project)
+ code = project.metais_code
+ metais_project = Metais::Project.find_by(code: code)
+ return unless metais_project
+
+ unless project.metais_projects.exists?(metais_project.id)
+ project.metais_projects << metais_project
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/jobs/metais/daily_sync_projects_job.rb b/app/jobs/metais/daily_sync_projects_job.rb
new file mode 100644
index 0000000..895bd0a
--- /dev/null
+++ b/app/jobs/metais/daily_sync_projects_job.rb
@@ -0,0 +1,13 @@
+class Metais::DailySyncProjectsJob < ApplicationJob
+ queue_as :metais
+
+ def perform
+ Datahub::Metais::Project.where('updated_at > ?', Time.now - 1.day).find_each do |metais_project|
+ project = Metais::Project.find_or_initialize_by(code: metais_project.latest_version.kod_metais,
+ uuid: metais_project.uuid)
+ project.save!
+
+ Metais::SyncProjectJob.perform_later(project, metais_project)
+ end
+ end
+end
diff --git a/app/jobs/metais/initial_sync_projects_job.rb b/app/jobs/metais/initial_sync_projects_job.rb
new file mode 100644
index 0000000..884145d
--- /dev/null
+++ b/app/jobs/metais/initial_sync_projects_job.rb
@@ -0,0 +1,13 @@
+class Metais::InitialSyncProjectsJob < ApplicationJob
+ queue_as :metais
+
+ def perform
+ Datahub::Metais::Project.find_each do |metais_project|
+ project = Metais::Project.find_or_initialize_by(code: metais_project.latest_version.kod_metais,
+ uuid: metais_project.uuid)
+ project.save!
+
+ Metais::SyncProjectJob.perform_later(project, metais_project)
+ end
+ end
+end
diff --git a/app/jobs/metais/project_data_extraction_delete_job.rb b/app/jobs/metais/project_data_extraction_delete_job.rb
new file mode 100644
index 0000000..3461df8
--- /dev/null
+++ b/app/jobs/metais/project_data_extraction_delete_job.rb
@@ -0,0 +1,21 @@
+require 'net/http'
+require 'uri'
+
+class Metais::ProjectDataExtractionDeleteJob < ApplicationJob
+ queue_as :metais_data_extraction
+
+ def perform(project_uuid)
+ url = "#{ENV.fetch('API_URL')}/projects/#{project_uuid}"
+ uri = URI(url)
+
+ req = Net::HTTP::Delete.new(uri)
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
+ http.request(req)
+ end
+
+ unless res.is_a?(Net::HTTPSuccess)
+ error_message = "Failed to delete project: #{res.code}, body: #{res.body}"
+ raise RuntimeError, error_message
+ end
+ end
+end
diff --git a/app/jobs/metais/project_data_extraction_job.rb b/app/jobs/metais/project_data_extraction_job.rb
new file mode 100644
index 0000000..227c3c9
--- /dev/null
+++ b/app/jobs/metais/project_data_extraction_job.rb
@@ -0,0 +1,24 @@
+require 'net/http'
+require 'uri'
+
+class Metais::ProjectDataExtractionJob < ApplicationJob
+ queue_as :metais_data_extraction
+
+ def perform(project_uuid)
+ url = "#{ENV.fetch('API_URL')}/projects/#{project_uuid}"
+ response = Net::HTTP.post(URI(url), '')
+
+ unless response.is_a?(Net::HTTPAccepted)
+ error_message = "Response status is #{response.code}. Message: #{response.body}"
+ raise RuntimeError, error_message
+ end
+
+ location = response['Location']
+ if location.nil?
+ error_message = "Expected 'Location' header not found in the response."
+ raise RuntimeError, error_message
+ end
+
+ Metais::ProjectDataExtractionStatusJob.perform_later(project_uuid, location)
+ end
+end
diff --git a/app/jobs/metais/project_data_extraction_result_job.rb b/app/jobs/metais/project_data_extraction_result_job.rb
new file mode 100644
index 0000000..d3c3944
--- /dev/null
+++ b/app/jobs/metais/project_data_extraction_result_job.rb
@@ -0,0 +1,111 @@
+require 'net/http'
+require 'uri'
+require 'json'
+
+class Metais::ProjectDataExtractionResultJob < ApplicationJob
+ queue_as :metais_data_extraction
+
+ def perform(project_uuid, location_header)
+ url = "#{ENV.fetch('API_URL')}/projects/#{project_uuid}"
+ response = Net::HTTP.get_response(URI(url))
+
+ handle_response_errors(response)
+
+ body = parse_json(response.body)
+ raise RuntimeError.new('Result status is not "Done"') unless body['status'] == 'Done'
+
+ result = body['result']
+
+ unless ['No documents', 'No documents for project plan'].include? result['detail']
+ metais_project = find_metais_project(project_uuid)
+ project_origin = find_or_initialize_project_origin(metais_project)
+
+ update_project_origin(project_origin, result)
+ process_harmonogram(result['harmonogram'], project_origin)
+ end
+
+ Metais::ProjectDataExtractionDeleteJob.perform_later(project_uuid)
+ end
+
+ private
+
+ def handle_response_errors(response)
+ case response.code.to_i
+ when 200, 202
+ else
+ error_message = "Unexpected response status: #{response.code}, body: #{response.body}"
+ raise RuntimeError, error_message
+ end
+ end
+
+ def parse_json(body)
+ JSON.parse(body)
+ rescue JSON::ParserError => e
+ error_message = "Failed to parse JSON response: #{e.message}"
+ raise RuntimeError, error_message
+ end
+
+ def find_metais_project(project_uuid)
+ Metais::Project.find_by(uuid: project_uuid).tap do |project|
+ unless project
+ error_message = "Couldn't find MetaIS project with 'uuid'=#{project_uuid}"
+ raise RuntimeError, error_message
+ end
+ end
+ end
+
+ def find_or_initialize_project_origin(metais_project)
+ metais_project.project_origins.find_or_initialize_by(title: metais_project.project_origins.first.title,
+ project: metais_project,
+ origin_type: Metais::OriginType.find_by(name: 'AI'))
+ end
+
+ def update_project_origin(project_origin, result)
+ project_origin.project_manager = "#{result['responsible']['first_name']} #{result['responsible']['surname']}"
+ project_origin.approved_investment = result['capex'] unless result['capex'].zero?
+ project_origin.approved_operation = result['opex'] unless result['opex'].zero?
+ project_origin.benefits = result['declared'] unless result['declared'].zero?
+ project_origin.targets_text = format_targets_text(result) unless format_targets_text(result).empty?
+ project_origin.save!
+ end
+
+ def format_targets_text(result)
+ kpis = result['kpis'].join("\n") unless result['kpis'].empty?
+ goals = result['goals'].join("\n") unless result['goals'].empty?
+ [kpis, goals].compact.join("\n")
+ end
+
+ def process_harmonogram(harmonogram, project_origin)
+ origin_type = Metais::OriginType.find_by(name: 'AI')
+ event_type = Metais::ProjectEventType.find_by(name: 'Predpoklad')
+
+ harmonogram.each do |event_data|
+ event_start_date = event_data['start_date'] || 'N/A'
+ event_end_date = event_data['end_date'] || 'N/A'
+ event_date = parse_event_date(event_data['start_date'])
+ event_name = event_data['item_name'] unless event_data['item_name'].empty?
+ event_value = "Projektu bude v stave #{event_data['item_name'].downcase} od #{event_start_date} do #{event_end_date}"
+
+ project_event = Metais::ProjectEvent.find_or_initialize_by(
+ project_origin: project_origin,
+ origin_type: origin_type,
+ event_type: event_type,
+ name: event_name,
+ value: event_value,
+ date: event_date
+ )
+
+ unless project_event.save
+ error_message = "Error encountered while creating an event: #{project_event.errors.full_messages.to_sentence}"
+ raise RuntimeError, error_message
+ end
+ end
+ end
+
+ def parse_event_date(start_date)
+ return if start_date.blank?
+ Date.strptime(start_date, "%m/%Y")
+ rescue ArgumentError => e
+ raise RuntimeError, e
+ end
+end
diff --git a/app/jobs/metais/project_data_extraction_status_job.rb b/app/jobs/metais/project_data_extraction_status_job.rb
new file mode 100644
index 0000000..0827951
--- /dev/null
+++ b/app/jobs/metais/project_data_extraction_status_job.rb
@@ -0,0 +1,17 @@
+class Metais::ProjectDataExtractionStatusJob < ApplicationJob
+ queue_as :metais_data_extraction
+
+ def perform(project_uuid, location_header)
+ url = "#{ENV.fetch('API_URL')}/projects/#{project_uuid}/status"
+ response = Net::HTTP.get_response(URI(url))
+
+ if response.key?('Retry-After')
+ Metais::ProjectDataExtractionStatusJob.set(wait: response['Retry-After'].to_i.seconds).perform_later(project_uuid, location_header)
+ else
+ location = response['Location']
+ raise "Location header missing in response" unless location
+
+ Metais::ProjectDataExtractionResultJob.perform_later(project_uuid, location)
+ end
+ end
+end
diff --git a/app/jobs/metais/sync_project_documents_job.rb b/app/jobs/metais/sync_project_documents_job.rb
new file mode 100644
index 0000000..8b68a17
--- /dev/null
+++ b/app/jobs/metais/sync_project_documents_job.rb
@@ -0,0 +1,17 @@
+class Metais::SyncProjectDocumentsJob < ApplicationJob
+ queue_as :metais
+
+ def perform(project_origin, metais_project)
+ metais_project.documents.each do |document|
+ project_document = Metais::ProjectDocument.find_or_initialize_by(uuid: document.uuid,
+ project_origin: project_origin,
+ origin_type: Metais::OriginType.find_by(name: 'MetaIS'))
+
+ project_document.name = document.latest_version.nazov
+ project_document.filename = document.latest_version.filename
+ project_document.value = 'https://metais.vicepremier.gov.sk/dms/file/' + document.uuid
+ project_document.description = "MetaIS"
+ project_document.save!
+ end
+ end
+end
diff --git a/app/jobs/metais/sync_project_events_job.rb b/app/jobs/metais/sync_project_events_job.rb
new file mode 100644
index 0000000..0f49aa3
--- /dev/null
+++ b/app/jobs/metais/sync_project_events_job.rb
@@ -0,0 +1,43 @@
+class Metais::SyncProjectEventsJob < ApplicationJob
+ queue_as :metais
+
+ def perform(project_origin, metais_project)
+ origin_type = Metais::OriginType.find_by(name: 'MetaIS')
+ event_type = Metais::ProjectEventType.find_by(name: 'Realita')
+ project_changes = Datahub::Metais::ProjectChange.where(project_version: metais_project.latest_version)
+
+ project_changes.each do |change|
+ if change.field == 'status'
+ event_date = change.created_at
+ event_name = Datahub::Metais::CodelistProjectState.find_by(code: change.new_value)&.nazov || change.new_value
+ event_value = "Stav projektu bol zmenený z
+ #{Datahub::Metais::CodelistProjectState.find_by(code: change.old_value)&.nazov.downcase || change.new_value.downcase} na
+ #{Datahub::Metais::CodelistProjectState.find_by(code: change.new_value)&.nazov.downcase || change.new_value.downcase}"
+
+ project_event = Metais::ProjectEvent.find_or_initialize_by(project_origin: project_origin,
+ origin_type: origin_type,
+ event_type: event_type,
+ name: event_name,
+ value: event_value,
+ date: event_date)
+ project_event.save!
+
+ elsif change.field == 'faza_projektu'
+ event_date = change.created_at
+ event_name = Datahub::Metais::CodelistProjectPhase.find_by(code: change.new_value)&.nazov || change.new_value
+ event_value = "Fáza projektu bola zmenená z
+ #{Datahub::Metais::CodelistProjectPhase.find_by(code: change.old_value)&.nazov.downcase || change.old_value.downcase} na
+ #{Datahub::Metais::CodelistProjectPhase.find_by(code: change.new_value)&.nazov.downcase || change.new_value.downcase}"
+
+ project_event = Metais::ProjectEvent.find_or_initialize_by(project_origin: project_origin,
+ origin_type: origin_type,
+ event_type: event_type,
+ name: event_name,
+ value: event_value,
+ date: event_date)
+ project_event.save!
+
+ end
+ end
+ end
+end
diff --git a/app/jobs/metais/sync_project_job.rb b/app/jobs/metais/sync_project_job.rb
new file mode 100644
index 0000000..eb28e60
--- /dev/null
+++ b/app/jobs/metais/sync_project_job.rb
@@ -0,0 +1,32 @@
+class Metais::SyncProjectJob < ApplicationJob
+ queue_as :metais
+
+ def perform(project, metais_project)
+ project_origin = Metais::ProjectOrigin.find_or_initialize_by(project: project,
+ origin_type: Metais::OriginType.find_by(name: 'MetaIS'))
+
+ project_origin.title = metais_project.latest_version.nazov
+ project_origin.description = metais_project.latest_version.popis
+ project_origin.status = Datahub::Metais::CodelistProjectState.find_by(code: metais_project.latest_version.status)&.nazov || metais_project.latest_version.status
+ project_origin.phase = Datahub::Metais::CodelistProjectPhase.find_by(code: metais_project.latest_version.faza_projektu)&.nazov || metais_project.latest_version.faza_projektu
+ project_origin.guarantor = metais_project.latest_version.prijimatel
+
+ project_origin.metais_created_at = metais_project.latest_version.metais_created_at
+ project_origin.start_date = metais_project.latest_version.datum_zacatia
+ project_origin.end_date = metais_project.latest_version.termin_ukoncenia
+ project_origin.status_change_date = metais_project.latest_version.zmena_stavu
+
+ project_origin.finance_source = Datahub::Metais::CodelistProgram.find_by(uuid: metais_project.latest_version.program)&.nazov || metais_project.latest_version.program
+ project_origin.investment = metais_project.latest_version.suma_vydavkov
+ project_origin.operation = metais_project.latest_version.rocne_naklady
+ project_origin.approved_investment = metais_project.latest_version.schvaleny_rozpocet
+ project_origin.approved_operation = metais_project.latest_version.schvalene_rocne_naklady
+
+ project_origin.save!
+
+ Metais::ProjectDataExtractionJob.set(wait: 5.minutes).perform_later(metais_project.uuid)
+ Metais::SyncProjectSuppliersJob.perform_later(project_origin, metais_project)
+ Metais::SyncProjectDocumentsJob.perform_later(project_origin, metais_project)
+ Metais::SyncProjectEventsJob.perform_later(project_origin, metais_project)
+ end
+end
diff --git a/app/jobs/metais/sync_project_suppliers_job.rb b/app/jobs/metais/sync_project_suppliers_job.rb
new file mode 100644
index 0000000..d7d4fb5
--- /dev/null
+++ b/app/jobs/metais/sync_project_suppliers_job.rb
@@ -0,0 +1,117 @@
+require 'open-uri'
+require 'nokogiri'
+
+class Metais::SyncProjectSuppliersJob < ApplicationJob
+ queue_as :metais
+
+ def perform(project_origin, metais_project)
+ origin_type = Metais::OriginType.find_by(name: 'MetaIS')
+
+ if metais_project.latest_version.link_nfp.present?
+ supplier_type = Metais::SupplierType.find_by(name: "NFP")
+
+ link = metais_project.latest_version.link_nfp
+
+ links = extract_links(link)
+ links.each do |url|
+ if valid_url?(url)
+ project_supplier = Metais::ProjectSupplier.find_or_initialize_by(
+ name: url,
+ value: url,
+ date: metais_project.latest_version.datum_nfp,
+ project_origin: project_origin,
+ origin_type: origin_type,
+ supplier_type: supplier_type)
+
+ project_supplier.save!
+ end
+ end
+ end
+
+ if metais_project.latest_version.vo.present?
+ supplier_type = Metais::SupplierType.find_by(name: "VO")
+
+ link = metais_project.latest_version.vo
+
+ links = extract_links(link)
+ links.each do |url|
+ if valid_url?(url)
+ project_supplier = Metais::ProjectSupplier.find_or_initialize_by(
+ name: url,
+ value: url,
+ date: metais_project.latest_version.vyhlasenie_vo,
+ project_origin: project_origin,
+ origin_type: origin_type,
+ supplier_type: supplier_type)
+
+ project_supplier.save!
+ end
+ end
+ end
+
+ if metais_project.latest_version.zmluva_o_dielo_crz.present?
+ supplier_type = Metais::SupplierType.find_by(name: "CRZ")
+
+ link = metais_project.latest_version.zmluva_o_dielo_crz
+
+ document = Nokogiri::HTML(open(link).read.force_encoding('UTF-8'))
+ crz_data = parse_crz_document(document)
+
+ if crz_data[:supplier] && crz_data[:cin]
+ project_origin.supplier = crz_data[:supplier]
+ project_origin.supplier_cin = crz_data[:cin]
+ project_origin.save!
+ end
+
+ links = extract_links(link)
+ links.each do |url|
+ if valid_url?(url)
+ project_supplier = Metais::ProjectSupplier.find_or_initialize_by(
+ name: url,
+ value: url,
+ date: metais_project.latest_version.zmluva_o_dielo,
+ project_origin: project_origin,
+ origin_type: origin_type,
+ supplier_type: supplier_type)
+
+ project_supplier.save!
+ end
+ end
+
+ end
+ end
+
+ private
+
+ def valid_url?(url)
+ uri = URI.parse(url)
+ uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS)
+ rescue URI::InvalidURIError
+ false
+ end
+
+ def extract_links(str)
+ uri_regexp = /\b(?:https?|ftp):\/\/\S+\b/
+ str.to_s.scan(uri_regexp)
+ end
+
+ def parse_crz_document(document)
+ li_elements = document.css('li.py-2.border-top')
+ supplier_info = nil
+ cin = nil
+
+ li_elements.each_with_index do |li_element, i|
+ supplier_label = li_element.at_css('strong.col-sm-3.text-sm-end')&.text&.strip
+ if supplier_label == 'Dodávateľ:'
+ supplier_info = li_element.at_css('span.col-sm-9')&.text&.strip
+ cin_label = li_elements[i + 1].at_css('strong.col-sm-3.text-sm-end')&.text&.strip
+ if cin_label == 'IČO:'
+ cin = li_elements[i + 1].at_css('span.col-sm-9')&.text&.strip
+ break if supplier_info && cin
+ end
+ end
+ end
+
+ { supplier: supplier_info, cin: cin }
+ end
+end
diff --git a/app/jobs/set_metais_codes_for_projects_job.rb b/app/jobs/set_metais_codes_for_projects_job.rb
new file mode 100644
index 0000000..44ef198
--- /dev/null
+++ b/app/jobs/set_metais_codes_for_projects_job.rb
@@ -0,0 +1,93 @@
+class SetMetaisCodesForProjectsJob < ApplicationJob
+ queue_as :default
+
+ MAPPING = {
+ 9241 => 2477,
+ 8848 => 1734,
+ 8989 => nil,
+ 4284 => 326,
+ 7705 => 1097,
+ 8467 => 1840,
+ 4228 => 1552,
+ 6349 => 1616,
+ 6175 => 320,
+ 6054 => 515,
+ 8024 => 1247,
+ 8972 => 2263,
+ 6257 => 329,
+ 8119 => 1450,
+ 6258 => 491,
+ 4288 => 692,
+ 7713 => 1154,
+ 5599 => 476,
+ 4211 => 487,
+ 5645 => 471,
+ 5909 => nil,
+ 5033 => 346,
+ 6215 => 530,
+ 4493 => 347,
+ 4196 => 327,
+ 7712 => 1060,
+ 5048 => 376,
+ 4929 => 368,
+ 6309 => 578,
+ 4237 => 2545,
+ 6018 => 514,
+ 6350 => 565,
+ 4576 => 307,
+ 4599 => nil,
+ 6040 => 489,
+ 4283 => nil,
+ 8026 => 991,
+ 6216 => nil,
+ 5637 => 457,
+ 6308 => nil,
+ 6352 => nil,
+ 6017 => 483,
+ 5916 => 508,
+ 8255 => 1594,
+ 4898 => 473,
+ 5641 => 380,
+ 4431 => 361,
+ 4287 => nil,
+ 5640 => 1159,
+ 4035 => 611,
+ 4263 => 490,
+ 4262 => nil,
+ 8320 => 1760,
+ 5931 => 350,
+ 5928 => nil,
+ 5912 => 464,
+ 6346 => nil,
+ 8419 => 1479,
+ 8971 => 1774,
+ 5703 => 462,
+ 5911 => 488,
+ 4494 => nil,
+ 6256 => 536,
+ 6048 => 381,
+ 8462 => 2055,
+ 4229 => nil,
+ 4248 => 369,
+ 6347 => 778,
+ 8431 => 2054,
+ 8420 => 1934,
+ 4334 => 359,
+ 4329 => nil,
+ 4362 => 315,
+ 9041 => 2269,
+ 5402 => nil,
+ 4260 => 1704,
+ 6344 => 702,
+ }
+
+ def perform
+ MAPPING.each do |project_id, metais_code|
+ project = Project.find_by_id(project_id)
+ if project
+ project.metais_code = "projekt_#{metais_code}" if metais_code
+ project.save
+ end
+ end
+ end
+end
diff --git a/app/jobs/sync_all_topics_job.rb b/app/jobs/sync_all_topics_job.rb
index a111851..895806b 100644
--- a/app/jobs/sync_all_topics_job.rb
+++ b/app/jobs/sync_all_topics_job.rb
@@ -1,7 +1,7 @@
class SyncAllTopicsJob < ApplicationJob
queue_as :default
- COLUMN_NAMES = ['Projekt', 'Projekt ID', 'Platforma', 'ID draft prípravy', 'ID prípravy', 'ID draft produktu', 'ID produktu'].freeze
+ COLUMN_NAMES = ['Projekt', 'Projekt ID', 'MetaIS', 'Platforma', 'ID draft prípravy', 'ID prípravy', 'ID draft produktu', 'ID produktu'].freeze
def perform(sync_all: true)
sheets_service = GoogleApiService.get_sheets_service
@@ -23,12 +23,20 @@ def find_indices(header_row)
def process_row(row, indices, sync_all)
project_name = row[indices["Projekt"]]
project_id = row[indices["Projekt ID"]]
+ project_metais_code = row[indices["MetaIS"]]
platform_link = row[indices["Platforma"]]
preparation_document_id = row[indices["ID draft prípravy"]]
preparation_page_id = row[indices["ID prípravy"]]
product_document_id = row[indices["ID draft produktu"]]
product_page_id = row[indices["ID produktu"]]
+
+ if project_metais_code.present?
+ project = Project.find_by(id: project_id)
+ project.metais_code = project_metais_code
+ project.save!
+ end
+
if sync_all
process_row_for_sync_all(project_name, project_id, platform_link, preparation_document_id, preparation_page_id, product_document_id, product_page_id)
else
diff --git a/app/models/datahub/metais/codelist_program.rb b/app/models/datahub/metais/codelist_program.rb
new file mode 100644
index 0000000..c85f957
--- /dev/null
+++ b/app/models/datahub/metais/codelist_program.rb
@@ -0,0 +1,3 @@
+class Datahub::Metais::CodelistProgram < DatahubRecord
+ self.table_name = 'metais.codelist_program'
+end
\ No newline at end of file
diff --git a/app/models/datahub/metais/codelist_project_phase.rb b/app/models/datahub/metais/codelist_project_phase.rb
new file mode 100644
index 0000000..42ec601
--- /dev/null
+++ b/app/models/datahub/metais/codelist_project_phase.rb
@@ -0,0 +1,3 @@
+class Datahub::Metais::CodelistProjectPhase < DatahubRecord
+ self.table_name = 'metais.codelist_project_phase'
+end
\ No newline at end of file
diff --git a/app/models/datahub/metais/codelist_project_state.rb b/app/models/datahub/metais/codelist_project_state.rb
new file mode 100644
index 0000000..6063468
--- /dev/null
+++ b/app/models/datahub/metais/codelist_project_state.rb
@@ -0,0 +1,3 @@
+class Datahub::Metais::CodelistProjectState < DatahubRecord
+ self.table_name = 'metais.codelist_project_state'
+end
\ No newline at end of file
diff --git a/app/models/datahub/metais/project.rb b/app/models/datahub/metais/project.rb
new file mode 100644
index 0000000..3fa5394
--- /dev/null
+++ b/app/models/datahub/metais/project.rb
@@ -0,0 +1,7 @@
+class Datahub::Metais::Project < DatahubRecord
+ self.table_name = "metais.projects"
+
+ has_many :documents, class_name: 'Datahub::Metais::ProjectDocument', foreign_key: 'project_id'
+ has_many :versions, class_name: 'Datahub::Metais::ProjectVersion', foreign_key: 'project_id'
+ belongs_to :latest_version, class_name: 'Datahub::Metais::ProjectVersion', foreign_key: 'latest_version_id', optional: true
+end
\ No newline at end of file
diff --git a/app/models/datahub/metais/project_change.rb b/app/models/datahub/metais/project_change.rb
new file mode 100644
index 0000000..6effb8a
--- /dev/null
+++ b/app/models/datahub/metais/project_change.rb
@@ -0,0 +1,5 @@
+class Datahub::Metais::ProjectChange < DatahubRecord
+ self.table_name = "metais.project_changes"
+
+ belongs_to :project_version, class_name: 'Datahub::Metais::ProjectVersion'
+end
\ No newline at end of file
diff --git a/app/models/datahub/metais/project_document.rb b/app/models/datahub/metais/project_document.rb
new file mode 100644
index 0000000..6a6f12d
--- /dev/null
+++ b/app/models/datahub/metais/project_document.rb
@@ -0,0 +1,7 @@
+class Datahub::Metais::ProjectDocument < DatahubRecord
+ self.table_name = "metais.project_documents"
+
+ belongs_to :project, class_name: 'Datahub::Metais::Project', foreign_key: 'project_id'
+ has_many :versions, class_name: 'Datahub::Metais::ProjectDocumentVersion'
+ belongs_to :latest_version, class_name: 'Datahub::Metais::ProjectDocumentVersion', foreign_key: 'latest_version_id', optional: true
+end
\ No newline at end of file
diff --git a/app/models/datahub/metais/project_document_version.rb b/app/models/datahub/metais/project_document_version.rb
new file mode 100644
index 0000000..57343ad
--- /dev/null
+++ b/app/models/datahub/metais/project_document_version.rb
@@ -0,0 +1,5 @@
+class Datahub::Metais::ProjectDocumentVersion < DatahubRecord
+ self.table_name = "metais.project_document_versions"
+
+ belongs_to :document, class_name: 'Datahub::Metais::ProjectDocument', foreign_key: 'document_id'
+end
\ No newline at end of file
diff --git a/app/models/datahub/metais/project_version.rb b/app/models/datahub/metais/project_version.rb
new file mode 100644
index 0000000..2593ec2
--- /dev/null
+++ b/app/models/datahub/metais/project_version.rb
@@ -0,0 +1,5 @@
+class Datahub::Metais::ProjectVersion < DatahubRecord
+ self.table_name = "metais.project_versions"
+
+ belongs_to :project, class_name: 'Datahub::Metais::Project', foreign_key: 'project_id'
+end
\ No newline at end of file
diff --git a/app/models/datahub_record.rb b/app/models/datahub_record.rb
new file mode 100644
index 0000000..d96032a
--- /dev/null
+++ b/app/models/datahub_record.rb
@@ -0,0 +1,4 @@
+class DatahubRecord < ActiveRecord::Base
+ self.abstract_class = true
+ establish_connection :"#{Rails.env}_datahub"
+end
diff --git a/app/models/metais.rb b/app/models/metais.rb
new file mode 100644
index 0000000..ccdd2d7
--- /dev/null
+++ b/app/models/metais.rb
@@ -0,0 +1,5 @@
+module Metais
+ def self.table_name_prefix
+ "metais."
+ end
+end
\ No newline at end of file
diff --git a/app/models/metais/origin_type.rb b/app/models/metais/origin_type.rb
new file mode 100644
index 0000000..bb910ac
--- /dev/null
+++ b/app/models/metais/origin_type.rb
@@ -0,0 +1,11 @@
+# == Schema Information
+#
+# Table name: metais.origin_types
+#
+# id :integer not null, primary key
+# name :string not null
+# created_at :datetime not null
+# updated_at :datetime not null
+
+class Metais::OriginType < ApplicationRecord
+end
diff --git a/app/models/metais/project.rb b/app/models/metais/project.rb
new file mode 100644
index 0000000..a7725ff
--- /dev/null
+++ b/app/models/metais/project.rb
@@ -0,0 +1,113 @@
+# == Schema Information
+#
+# Table name: metais.projects
+#
+# id :integer not null, primary key
+# code :string not null
+# created_at :datetime not null
+# updated_at :datetime not null
+
+class Metais::Project < ApplicationRecord
+ has_many :project_origins, :class_name => 'Metais::ProjectOrigin'
+ has_and_belongs_to_many :projects, class_name: 'Project',
+ join_table: 'public.projects_metais_projects',
+ foreign_key: 'metais_project_id',
+ association_foreign_key: 'project_id'
+
+ FINANCE_SOURCE_MAPPINGS = { "Medzirezortný program 0EK Informačné technológie financované zo štátneho rozpočtu" => "Štátny rozpočet" }
+
+ def evaluations
+ ::Project.joins('''
+ INNER JOIN "public"."projects_metais_projects" ON "public"."projects_metais_projects"."project_id" = "projects"."id"
+ INNER JOIN "metais"."projects" AS "metais_projects" ON "metais_projects"."id" = "public"."projects_metais_projects"."metais_project_id"
+ ''').where('projects_metais_projects.metais_project_id = ?', self.id)
+ end
+
+ def get_project_origin_info
+ @project_origin_info ||= load_project_origin_info
+ end
+
+ def get_ai_project_origin
+ @ai_project_origin ||= project_origins.joins(:origin_type).find_by(origin_types: { name: 'AI' })
+ end
+
+ def self.evaluation_counts
+ total_count = Metais::Project.count
+ yes_count = Metais::Project
+ .joins('INNER JOIN public.projects_metais_projects ON public.projects_metais_projects.metais_project_id = metais.projects.id')
+ .distinct
+ .count('metais.projects.id')
+ no_count = total_count - yes_count
+
+ { yes: yes_count, no: no_count }
+ end
+
+ def self.filtered_and_sorted_projects(params)
+ per_page = 25
+ page = params[:page] || 1
+
+ ordered_project_origins = Metais::ProjectOrigin
+ .select('project_id,
+ COALESCE(NULLIF(max(approved_investment) FILTER (WHERE approved_investment IS NOT NULL), 0),
+ NULLIF(max(investment) FILTER (WHERE investment IS NOT NULL), 0)) AS final_investment,
+ max(title) FILTER (WHERE title IS NOT NULL) AS final_title,
+ max(status) FILTER (WHERE status IS NOT NULL) AS final_status,
+ max(guarantor) FILTER (WHERE guarantor IS NOT NULL) AS final_guarantor')
+ .group('project_id')
+
+ projects = Metais::Project.joins("INNER JOIN (#{ordered_project_origins.to_sql}) project_origins ON metais.projects.id = project_origins.project_id")
+
+ projects = projects.where('project_origins.final_guarantor = ?', params[:guarantor]) if params[:guarantor].present?
+ projects = projects.where('project_origins.final_status = ?', params[:status]) if params[:status].present?
+ projects = projects.where('code ILIKE ?', "%#{params[:code]}%") if params[:code].present?
+ projects = projects.where('project_origins.final_title ILIKE ?', "%#{params[:title]}%") if params[:title].present?
+ projects = projects.where('project_origins.final_investment >= ?', params[:min_price].to_f) if params[:min_price].present?
+ projects = projects.where('project_origins.final_investment <= ?', params[:max_price].to_f) if params[:max_price].present?
+
+ if params[:has_evaluation].present?
+ projects = projects.select { |project|
+ params[:has_evaluation] == 'yes' ? project.evaluations.exists? : project.evaluations.empty?
+ }
+ projects = projects.map(&:id)
+ projects = Metais::Project.where(id: projects)
+ end
+
+ params[:sort] = 'date' unless params[:sort].present?
+ sort_direction = params[:sort_direction]&.upcase == 'ASC' ? 'ASC' : 'DESC'
+ projects = case params[:sort]
+ when 'alpha'
+ projects.order("LOWER(project_origins.final_title) #{sort_direction}")
+ when 'date'
+ projects.order("metais.projects.updated_at #{sort_direction}")
+ when 'price'
+ projects.order("project_origins.final_investment #{sort_direction} NULLS #{sort_direction == 'ASC' ? 'FIRST' : 'LAST'}")
+ end
+
+ projects.page(page).per(per_page)
+ end
+
+ private
+
+ def load_project_origin_info
+ fields = %w[title status phase description guarantor project_manager start_date end_date
+ finance_source investment operation approved_investment approved_operation
+ supplier supplier_cin targets_text events_text documents_text links_text updated_at]
+
+ origins = self.project_origins.sort_by { |origin| -origin.origin_type_id }
+
+ project_info = OpenStruct.new
+ fields.each do |field|
+ origin = origins.detect { |origin| !origin.send(field).nil? }
+ value = origin&.send(field)
+
+ if field == 'finance_source' && value
+ value = FINANCE_SOURCE_MAPPINGS[value] || value
+ end
+ if value
+ project_info.send("#{field}=", Metais::ValueWithOrigin.new(value, origin.origin_type_id))
+ end
+ end
+
+ project_info
+ end
+end
diff --git a/app/models/metais/project_document.rb b/app/models/metais/project_document.rb
new file mode 100644
index 0000000..effbf3e
--- /dev/null
+++ b/app/models/metais/project_document.rb
@@ -0,0 +1,14 @@
+# == Schema Information
+#
+# Table name: metais.project_documents
+#
+# id :integer not null, primary key
+# name :string not null
+# value :string not null
+# created_at :datetime not null
+# updated_at :datetime not null
+
+class Metais::ProjectDocument < ApplicationRecord
+ belongs_to :project_origin, class_name: 'Metais::ProjectOrigin'
+ belongs_to :origin_type, class_name: 'Metais::OriginType'
+end
diff --git a/app/models/metais/project_event.rb b/app/models/metais/project_event.rb
new file mode 100644
index 0000000..39ffc71
--- /dev/null
+++ b/app/models/metais/project_event.rb
@@ -0,0 +1,24 @@
+# == Schema Information
+#
+# Table name: metais.project_events
+#
+# id :integer not null, primary key
+# name :string not null
+# value :string not null
+# date :datetime not null
+# created_at :datetime not null
+# updated_at :datetime not null
+
+class Metais::ProjectEvent < ApplicationRecord
+ belongs_to :project_origin, class_name: 'Metais::ProjectOrigin'
+ belongs_to :origin_type, class_name: 'Metais::OriginType'
+ belongs_to :event_type, class_name: 'Metais::ProjectEventType'
+
+ scope :assumpted, -> {
+ where(event_type: Metais::ProjectEventType.find_by(name: 'Predpoklad')).order(:date)
+ }
+
+ scope :real, -> {
+ where(event_type: Metais::ProjectEventType.find_by(name: 'Realita')).order(:date)
+ }
+end
diff --git a/app/models/metais/project_event_type.rb b/app/models/metais/project_event_type.rb
new file mode 100644
index 0000000..ea4a15b
--- /dev/null
+++ b/app/models/metais/project_event_type.rb
@@ -0,0 +1,3 @@
+class Metais::ProjectEventType < ApplicationRecord
+ has_many :project_events, class_name: 'Metais::ProjectEvent'
+end
diff --git a/app/models/metais/project_link.rb b/app/models/metais/project_link.rb
new file mode 100644
index 0000000..771a260
--- /dev/null
+++ b/app/models/metais/project_link.rb
@@ -0,0 +1,14 @@
+# == Schema Information
+#
+# Table name: metais.project_links
+#
+# id :integer not null, primary key
+# name :string not null
+# value :string not null
+# created_at :datetime not null
+# updated_at :datetime not null
+
+class Metais::ProjectLink < ApplicationRecord
+ belongs_to :project_origin, class_name: 'Metais::ProjectOrigin'
+ belongs_to :origin_type, class_name: 'Metais::OriginType'
+end
diff --git a/app/models/metais/project_origin.rb b/app/models/metais/project_origin.rb
new file mode 100644
index 0000000..76c4a37
--- /dev/null
+++ b/app/models/metais/project_origin.rb
@@ -0,0 +1,65 @@
+# == Schema Information
+#
+# Table name: metais.project_origins
+#
+# id :integer not null, primary key
+# project_id :integer not null
+# origin_type_id :integer not null
+#
+# title :string not null
+# status :string
+# description :text
+# guarantor :string
+# project_manager :string
+# start_date :datetime
+# end_date :datetime
+#
+# source :string
+# investment :decimal(15,2)
+# operation :decimal(15,2)
+#
+# supplier :string
+#
+# targets_text :text
+# events_text :text
+# documents_text :text
+# links_text :text
+#
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_metais_project_origins_on_project_id (project_id)
+# index_metais_project_origins_on_origin_type_id (origin_type_id)
+#
+# Foreign key constraints
+#
+# fk_metais_project_origins_project_id (project_id => metais.projects.id)
+# fk_metais_project_origins_origin_type_id (origin_type_id => metais.origin_types.id)
+
+class Metais::ProjectOrigin < ApplicationRecord
+ belongs_to :project, :class_name => 'Metais::Project'
+ belongs_to :origin_type, :class_name => 'Metais::OriginType'
+
+ has_many :documents, class_name: 'Metais::ProjectDocument', foreign_key: 'project_origin_id'
+ has_many :suppliers, class_name: 'Metais::ProjectSupplier', foreign_key: 'project_origin_id'
+ has_many :events, class_name: 'Metais::ProjectEvent', foreign_key: 'project_origin_id'
+ has_many :links, class_name: 'Metais::ProjectLink', foreign_key: 'project_origin_id'
+
+ def self.guarantor_counts
+ group(:guarantor).count
+ end
+
+ def self.unique_guarantors
+ pluck(:guarantor).reject(&:blank?).uniq
+ end
+
+ def self.status_counts
+ group(:status).count
+ end
+
+ def self.unique_statuses
+ pluck(:status).reject(&:blank?).uniq
+ end
+end
diff --git a/app/models/metais/project_supplier.rb b/app/models/metais/project_supplier.rb
new file mode 100644
index 0000000..5742e89
--- /dev/null
+++ b/app/models/metais/project_supplier.rb
@@ -0,0 +1,15 @@
+# == Schema Information
+#
+# Table name: metais.project_suppliers
+#
+# id :integer not null, primary key
+# name :string not null
+# value :string not null
+# created_at :datetime not null
+# updated_at :datetime not null
+
+class Metais::ProjectSupplier < ApplicationRecord
+ belongs_to :project_origin, class_name: 'Metais::ProjectOrigin'
+ belongs_to :origin_type, class_name: 'Metais::OriginType'
+ belongs_to :supplier_type, class_name: 'Metais::SupplierType'
+end
diff --git a/app/models/metais/supplier_type.rb b/app/models/metais/supplier_type.rb
new file mode 100644
index 0000000..cda5380
--- /dev/null
+++ b/app/models/metais/supplier_type.rb
@@ -0,0 +1,11 @@
+# == Schema Information
+#
+# Table name: metais.supplier_types
+#
+# id :integer not null, primary key
+# name :string not null
+# created_at :datetime not null
+# updated_at :datetime not null
+
+class Metais::SupplierType < ApplicationRecord
+end
diff --git a/app/models/metais/value_with_origin.rb b/app/models/metais/value_with_origin.rb
new file mode 100644
index 0000000..3f37339
--- /dev/null
+++ b/app/models/metais/value_with_origin.rb
@@ -0,0 +1,12 @@
+class Metais::ValueWithOrigin < BasicObject
+ attr_reader :origin
+
+ def initialize(value, origin)
+ @value = value
+ @origin = origin
+ end
+
+ def method_missing(name, *args)
+ @value.send(name, *args)
+ end
+end
\ No newline at end of file
diff --git a/app/models/project.rb b/app/models/project.rb
index 241b1c4..b35e2c4 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -9,77 +9,74 @@
class Project < ApplicationRecord
has_many :phases
+ has_and_belongs_to_many :metais_projects, class_name: 'Metais::Project',
+ join_table: 'public.projects_metais_projects',
+ foreign_key: 'project_id',
+ association_foreign_key: 'metais_project_id'
def has_published_phases?
phases.any? { |phase| phase.published_revision.present? }
end
- def self.filtered_projects(selected_tag, sort_param)
+ def self.filtered_and_sorted_projects(params)
+ per_page = 25
+ page = params[:page] || 1
- case sort_param
- when 'newest'
- projects = Project.joins(phases: :published_revision)
- .select('projects.*, MAX(phase_revisions.published_at) AS newest_published_at')
- .group('projects.id')
- .order('newest_published_at DESC')
- when 'oldest'
- projects = Project.joins(phases: :published_revision)
- .select('projects.*, MIN(phase_revisions.published_at) AS oldest_published_at')
- .group('projects.id')
- .order('oldest_published_at')
- when 'alpha', 'alpha_reverse'
- projects = Project.joins(phases: :published_revision)
- .select('DISTINCT ON (projects.id) projects.*, LOWER(phase_revisions.title) AS alpha_title')
- .order('projects.id, alpha_title')
- projects = projects.sort_by(&:alpha_title)
- projects = projects.reverse if sort_param == 'alpha_reverse'
- when 'preparation_lowest'
- projects = Project.joins(phases: :published_revision)
- .where(phases: { phase_type: PhaseType.find_by(name: 'Prípravná fáza') })
- .distinct
- projects = projects.sort_by do |project|
- phase_prep = project.phases.find { |p| p.phase_type.name == 'Prípravná fáza' }
- phase_prep ? (phase_prep.published_revision.aggregated_rating) : 0
- end.reverse
- when 'preparation_highest'
- projects = Project.joins(phases: :published_revision)
- .where(phases: { phase_type: PhaseType.find_by(name: 'Prípravná fáza') })
- .distinct
- projects = projects.sort_by do |project|
- phase_prep = project.phases.find { |p| p.phase_type.name == 'Prípravná fáza' }
- phase_prep ? (phase_prep.published_revision.aggregated_rating) : 0
- end
- when 'product_lowest'
- projects = Project.joins(phases: :published_revision)
- .where(phases: { phase_type: PhaseType.find_by(name: 'Fáza produkt') })
- .distinct
- projects = projects.sort_by do |project|
- phase_prep = project.phases.find { |p| p.phase_type.name == 'Fáza produkt' }
- phase_prep ? (phase_prep.published_revision.aggregated_rating) : 0
- end.reverse
- when 'product_highest'
- projects = Project.joins(phases: :published_revision)
- .where(phases: { phase_type: PhaseType.find_by(name: 'Fáza produkt') })
- .distinct
- projects = projects.sort_by do |project|
- phase_prep = project.phases.find { |p| p.phase_type.name == 'Fáza produkt' }
- phase_prep ? (phase_prep.published_revision.aggregated_rating) : 0
- end
- else
- projects = Project.joins(phases: :published_revision)
- .distinct
+ projects = Project.joins(phases: :published_revision)
- if ProjectsHelper::ALLOWED_TAGS.keys.include?(selected_tag)
- projects = Project.joins(phases: :published_revision)
- .where(phase_revisions: { tags: selected_tag })
- end
-
- projects = projects.sort_by do |project|
- max_rating = project.phases.map { |p| p.published_revision&.aggregated_rating }.compact.max
- max_rating
- end
+ if params[:title].present?
+ projects = projects.where('phase_revisions.title ILIKE ?', "%#{params[:title]}%")
end
+ params[:sort] = 'date' unless params[:sort].present?
+ sort_direction = params[:sort_direction]&.upcase == 'ASC' ? 'ASC' : 'DESC'
+ projects = case params[:sort]
+ when 'alpha'
+ projects = projects.select('DISTINCT ON (projects.id) projects.*, LOWER(phase_revisions.title) AS alpha_title')
+ .order("projects.id, alpha_title")
+ projects = projects.sort_by(&:alpha_title)
+ projects = projects.reverse if sort_direction == 'DESC'
+ projects
+ when 'date'
+ if sort_direction == 'ASC'
+ projects = projects.select("projects.*, MIN(phase_revisions.published_at) AS oldest_published_at").group("projects.id")
+ .order("oldest_published_at")
+ elsif sort_direction == 'DESC'
+ projects = projects.select('projects.*, MAX(phase_revisions.published_at) AS newest_published_at').group('projects.id')
+ .order('newest_published_at DESC')
+ end
+ when 'preparation'
+ if sort_direction == 'ASC'
+ projects = projects.where(phases: { phase_type: PhaseType.find_by(name: 'Prípravná fáza') }).distinct
+ projects = projects.sort_by do |project|
+ phase_prep = project.phases.find { |p| p.phase_type.name == 'Prípravná fáza' }
+ phase_prep ? (phase_prep.published_revision.aggregated_rating) : 0
+ end.reverse
+ elsif sort_direction == 'DESC'
+ projects = projects.where(phases: { phase_type: PhaseType.find_by(name: 'Prípravná fáza') }).distinct
+ projects = projects.sort_by do |project|
+ phase_prep = project.phases.find { |p| p.phase_type.name == 'Prípravná fáza' }
+ phase_prep ? (phase_prep.published_revision.aggregated_rating) : 0
+ end
+ end
+ when 'product'
+ if sort_direction == 'ASC'
+ projects = projects.where(phases: { phase_type: PhaseType.find_by(name: 'Fáza produkt') }).distinct
+ projects = projects.sort_by do |project|
+ phase_prep = project.phases.find { |p| p.phase_type.name == 'Fáza produkt' }
+ phase_prep ? (phase_prep.published_revision.aggregated_rating) : 0
+ end.reverse
+
+ elsif sort_direction == 'DESC'
+ projects = projects.where(phases: { phase_type: PhaseType.find_by(name: 'Fáza produkt') }).distinct
+ projects = projects.sort_by do |project|
+ phase_prep = project.phases.find { |p| p.phase_type.name == 'Fáza produkt' }
+ phase_prep ? (phase_prep.published_revision.aggregated_rating) : 0
+ end
+ end
+ end
+
+ projects = Kaminari.paginate_array(projects).page(page).per(per_page)
projects
end
end
diff --git a/app/views/admin/metais/project_origins/edit.html.erb b/app/views/admin/metais/project_origins/edit.html.erb
new file mode 100644
index 0000000..a1c9c97
--- /dev/null
+++ b/app/views/admin/metais/project_origins/edit.html.erb
@@ -0,0 +1,443 @@
+<%= form_with(model: [@project, @project_origin], url: admin_metais_project_project_origin_path(@project, @project_origin), method: :patch, local: true) do |form| %>
+
+
+
+ Projekt updatovaný v dátume: <%= @project_info&.updated_at&.in_time_zone('Europe/Bratislava')&.strftime('%H:%M %d.%m.%Y') %>.
+ AI extrakcia v dátume: <%= @project.get_ai_project_origin&.updated_at&.in_time_zone('Europe/Bratislava')&.strftime('%H:%M %d.%m.%Y') || '-' %>
+
+
+ <%= link_to 'Zobraziť', metais_project_path(@project), class: "btn btn-link", role: "button" %>
+ <%= form.submit 'Uložiť', class: 'btn btn-primary' %>
+
+
+
+
+
<%= form.label :title, "Názov projektu" %>
+ <% if @project_info.title.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.title.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.text_field :title, value: @project_info.title, class: "form-control" %>
+
+
+
<%= form.label :description, "Popis" %>
+ <% if @project_info.description.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.description.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.text_area :description, value: @project_info.description, class: "form-control", rows: 12 %>
+
+
+
+
+
+
<%= form.label :guarantor, "Garant" %>
+ <% if @project_info.guarantor.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.guarantor.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.text_field :guarantor, value: @project_info.guarantor, class: "form-control" %>
+
+
+
<%= form.label :project_manager, "Projektový manažér" %>
+ <% if @project_info.project_manager.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.project_manager.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.text_field :project_manager, value: @project_info.project_manager, class: "form-control" %>
+
+
+
+
<%= form.label :finance_source, "Zdroj financovania" %>
+ <% if @project_info.finance_source.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.finance_source.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <% finance_source_mappings = Metais::Project::FINANCE_SOURCE_MAPPINGS %>
+ <%= form.select :finance_source,
+ Metais::ProjectOrigin.pluck(:finance_source).uniq.reject(&:blank?).map { |fs| [finance_source_mappings[fs] || fs, fs] }, { selected: @project_info.finance_source }, class: "form-control" %>
+
+
+
<%= form.label :status, "Stav" %>
+ <% if @project_info.status.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.status.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.select :status, Metais::ProjectOrigin.pluck(:status).uniq.reject(&:blank?), { selected: @project_info.status }, { class: "form-control" } %>
+ <%= form.label :phase, "Fáza" %>
+ <% if @project_info.phase.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.phase.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.select :phase, Metais::ProjectOrigin.pluck(:phase).uniq.reject(&:blank?), { selected: @project_info.phase }, { class: "form-control" } %>
+
+
+
+
<%= form.label :start_date, "Dátum začiatku" %>
+ <% if @project_info.start_date.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.start_date.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.date_field :start_date,
+ value: (@project_info.start_date.present? ? @project_info.start_date.strftime("%F") : Date.today.strftime("%F")),
+ class: "form-control datepicker" %>
+
+
+
<%= form.label :end_date, "Termín ukončenia " %>
+ <% if @project_info.end_date.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.end_date.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.date_field :end_date,
+ value: (@project_info.end_date.present? ? @project_info.end_date.strftime("%F") : Date.today.strftime("%F")),
+ class: "form-control datepicker" %>
+
+
+
+
Rozpočet projektu
+
+
+
<%= form.label :investment, "Investícia" %>
+ <% if @project_info.investment.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.investment.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.number_field :investment, value: @project_info.investment, step: 0.1, class: "form-control" %>
+ <%= form.label :operation, "Ročné náklady" %>
+ <% if @project_info.operation.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.operation.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.number_field :operation, value: @project_info.operation, step: 0.1, class: "form-control" %>
+
+
+
<%= form.label :approved_investment, "Schválená investícia" %>
+ <% if @project_info.approved_investment.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.approved_investment.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.number_field :approved_investment, value: @project_info.approved_investment, step: 0.1, class: "form-control" %>
+ <%= form.label :approved_operation, "Schválené ročné náklady" %>
+ <% if @project_info.approved_operation.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.approved_operation.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.number_field :approved_operation, value: @project_info.approved_operation, step: 0.1, class: "form-control" %>
+
+
+
+
+
+
+
+
+
+
<%= form.label :targets_text, "Ciele a merateľné ukazovatele" %>
+ <% if @project_info.targets_text.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.targets_text.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.text_area :targets_text, value: @project_info.targets_text, class: "form-control", rows: 5 %>
+
+
+
+
+
+
<%= form.label :documents_text, "Dokumenty" %>
+ <% if @project_info.documents_text.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.documents_text.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.text_area :documents_text, value: @project_info.documents_text, class: "form-control", rows: 2 %>
+
+
+
+
+
+
<%= form.label :links_text, "Linky" %>
+ <% if @project_info.links_text.present? %>
+ <% logo_path, tooltip_text = origin_type_logo(@project_info.links_text.origin) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <% end %>
+
+ <%= form.text_area :links_text, value: @project_info.links_text, class: "form-control", rows: 2 %>
+
+
+
+
+
+
Dodávateľ projektu
+
+ <%= form.label :supplier, "Dodávateľ" %>
+ <%= form.text_field :supplier, value: @project_info.supplier, class: "form-control", id: "supplier-input" %>
+
+ <%= form.label :supplier_cin, "IČO" %>
+ <%= form.text_field :supplier_cin, value: @project_info.supplier_cin, class: "form-control", id: "supplier-cin-input" %>
+
+
+
+
+
+
+ <%= form.submit 'Uložiť', class: 'btn btn-primary' %>
+
+
+<% end %>
+
+
+
+
+
+
+
Priebeh projektu
+
Predpokladaný priebeh projektu
+
+
+
+ Názov
+ Popis
+ Dátum
+ Akcia
+
+
+
+ <% @assumption_events.each do |event| %>
+
+
+ <% logo_path, tooltip_text = origin_type_logo(event.origin_type) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <%= event.name %>
+
+ <%= event.value %>
+ <%= event.date.in_time_zone('Europe/Bratislava').strftime('%d.%m.%Y') %>
+
+ <%= link_to 'Odstrániť', admin_metais_project_project_origin_remove_event_path(project_id: @project.id, project_origin_id: @project_origin.id, event_id: event.id), method: :delete, data: { confirm: 'Naozaj chcete aktivitu odstrániť?' } %>
+
+
+ <% end %>
+
+ <%= form_with(url: admin_metais_project_project_origin_add_event_path(project_id: @project.id, project_origin_id: @project_origin.id), method: :post, local: true) do |f| %>
+ <%= f.text_field 'event[name]', class: "w-100 form-control" %>
+ <%= f.text_field 'event[value]', class: "w-100 form-control" %>
+ <%= f.date_field 'event[date]', class: "form-control" %>
+ <%= hidden_field_tag 'event[origin_type]', 'Human' %>
+ <%= hidden_field_tag 'event[event_type]', 'Predpoklad' %>
+ <%= f.submit 'Pridať', class: 'btn btn-primary' %>
+ <% end %>
+
+
+
+
+
+
Reálný priebeh projektu
+
+
+
+ Názov
+ Popis
+ Dátum
+ Akcia
+
+
+
+ <% @real_events.each do |event| %>
+
+
+ <% logo_path, tooltip_text = origin_type_logo(event.origin_type) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <%= event.name %>
+
+ <%= event.value %>
+ <%= event.date.in_time_zone('Europe/Bratislava').strftime('%d.%m.%Y') %>
+
+ <%= link_to 'Odstrániť', admin_metais_project_project_origin_remove_event_path(project_id: @project.id, project_origin_id: @project_origin.id, event_id: event.id), method: :delete, data: { confirm: 'Naozaj chcete aktivitu odstrániť?' } %>
+
+
+ <% end %>
+
+ <%= form_with(url: admin_metais_project_project_origin_add_event_path(project_id: @project.id, project_origin_id: @project_origin.id), method: :post, local: true) do |f| %>
+ <%= f.text_field 'event[name]', class: "w-100 form-control" %>
+ <%= f.text_field 'event[value]', class: "w-100 form-control" %>
+ <%= f.date_field 'event[date]', class: "form-control" %>
+ <%= hidden_field_tag 'event[origin_type]', 'Human' %>
+ <%= hidden_field_tag 'event[event_type]', 'Realita' %>
+ <%= f.submit 'Pridať', class: 'btn btn-primary' %>
+ <% end %>
+
+
+
+
+
+
+
+
+
+
Verejné obstarávanie, Dodávateľská zmúva, Zmlúva o NFP
+
+
+
+ Typ
+ Link
+ Akcia
+
+
+
+ <% @combined_suppliers.each do |supplier| %>
+
+
+ <% logo_path, tooltip_text = origin_type_logo(supplier.origin_type) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <%= supplier.supplier_type.name if supplier.supplier_type.present? %>
+
+ <%= link_to supplier.name, supplier.value, class: "w-100" %>
+
+ <%= link_to 'Odstrániť', admin_metais_project_project_origin_remove_supplier_path(project_id: @project.id, project_origin_id: @project_origin.id, supplier_id: supplier.id), method: :delete, data: { confirm: 'Naozaj chcete item odstrániť?' } %>
+
+
+ <% end %>
+
+ <%= form_with(url: admin_metais_project_project_origin_add_supplier_path(project_id: @project.id, project_origin_id: @project_origin.id), method: :post, local: true) do |f| %>
+ <%= f.select 'supplier[supplier_type]', Metais::SupplierType.pluck(:name, :id), {}, {class: 'form-control'} %>
+ <%= f.text_field 'supplier[value]', class: "w-100 form-control" %>
+ <%= hidden_field_tag 'supplier[origin_type]', 'Human' %>
+ <%= f.submit 'Pridať', class: 'btn btn-primary' %>
+ <% end %>
+
+
+
+
+
+
+
+
+
Dokumenty
+
+
+
+ Názov skupiny
+ Priorita
+ Akcia
+
+
+
+ <% @grouped_documents.each do |description, documents| %>
+ <%= form_with(url: admin_metais_project_project_origin_update_group_order_path(project_id: @project.id, project_origin_id: @project_origin.id, description: description), method: :patch, local: true) do |f| %>
+
+ <%= description %>
+ <%= f.number_field 'group_order', value: documents.first.group_order, class: "form-control", min: 1 %>
+ <%= f.submit 'Aktualizovať poradie', class: 'btn btn-primary' %>
+
+ <% end %>
+ <% end %>
+
+
+
+
+
+
+ Názov
+ Link
+ Popis
+ Akcia
+
+
+
+ <% @combined_documents.each do |document| %>
+
+
+ <% logo_path, tooltip_text = origin_type_logo(document.origin_type) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <%= document.name %>
+
+ <%= link_to document.value, document.value %>
+ <%= document.description %>
+
+ <%= link_to 'Odstrániť', admin_metais_project_project_origin_remove_document_path(project_id: @project.id, project_origin_id: @project_origin.id, document_id: document.id), method: :delete, data: { confirm: 'Naozaj chcete item odstrániť?' } %>
+
+
+ <% end %>
+
+ <%= form_with(url: admin_metais_project_project_origin_add_document_path(project_id: @project.id, project_origin_id: @project_origin.id), method: :post, local: true) do |f| %>
+ <%= f.text_field 'document[name]', class: "form-control" %>
+ <%= f.text_field 'document[value]', class: "form-control" %>
+ <%= f.text_field 'document[description]', class: "form-control" %>
+ <%= hidden_field_tag 'document[origin_type]', 'Human' %>
+ <%= f.submit 'Pridať', class: 'btn btn-primary' %>
+ <% end %>
+
+
+
+
+
+
+
+
+
Linky
+
+
+
+ Názov
+ Link
+ Akcia
+
+
+
+ <% @combined_links.each do |link| %>
+
+
+ <% logo_path, tooltip_text = origin_type_logo(link.origin_type) %>
+ <%= image_tag logo_path, alt: tooltip_text, title: tooltip_text, style: "width: 16px; height: 16px;", class:"tag-tooltip" %>
+ <%= link.name %>
+
+ <%= link_to link.value, link.value %>
+
+ <%= link_to 'Odstrániť', admin_metais_project_project_origin_remove_link_path(project_id: @project.id, project_origin_id: @project_origin.id, link_id: link.id), method: :delete, data: { confirm: 'Naozaj chcete item odstrániť?' } %>
+
+
+ <% end %>
+
+ <%= form_with(url: admin_metais_project_project_origin_add_link_path(project_id: @project.id, project_origin_id: @project_origin.id), method: :post, local: true) do |f| %>
+ <%= f.text_field 'link[name]', class: "w-100 form-control" %>
+ <%= f.text_field 'link[value]', class: "w-100 form-control" %>
+ <%= hidden_field_tag 'link[origin_type]', 'Human' %>
+ <%= f.submit 'Pridať', class: 'btn btn-primary' %>
+ <% end %>
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/views/admin/metais/projects/index.html.erb b/app/views/admin/metais/projects/index.html.erb
new file mode 100644
index 0000000..ebcdcb4
--- /dev/null
+++ b/app/views/admin/metais/projects/index.html.erb
@@ -0,0 +1,250 @@
+Admin
+
+
+
MetaIS projekty
+
+
+
+ <%= form_with url: admin_metais_projects_path, method: :get, class: 'form my-2 my-lg-0', local: true, id: "form" do |form| %>
+ <%= hidden_field_tag :sort, params[:sort] %>
+ <%= hidden_field_tag :sort_direction, params[:sort_direction] %>
+ <%= hidden_field_tag :status, params[:status] %>
+ <%= hidden_field_tag :guarantor, params[:guarantor] %>
+ <%= hidden_field_tag :has_evaluation, params[:has_evaluation] %>
+
+
+
+
+
+ <%= text_field_tag :title, params[:title], class: 'form-control mr-1', placeholder: "Meno projektu" %>
+ Hľadať
+
+
+
+
+
+ <% end %>
+
+
+
+
+
+ ID
+ Názov
+ Origins
+ Dokumenty
+ Dodávatelia
+ Aktivity
+ Linky
+ AI dátum
+ Akcie
+
+
+
+ <% @projects.each do |project| %>
+
+ <%= project.id %>
+
+ <%= link_to project.project_origins.first.title, metais_project_path(project) %>
+
+
+ <%= project.project_origins.count %>
+
+
+ <%= project.project_origins.joins(:documents).count %>
+
+
+ <%= project.project_origins.joins(:suppliers).count %>
+
+
+ <%= project.project_origins.joins(:events).count %>
+
+
+ <%= project.project_origins.joins(:links).count %>
+
+
+ <%= project.get_ai_project_origin&.updated_at&.in_time_zone('Europe/Bratislava')&.strftime('%H:%M %d.%m.%Y') || '-' %>
+
+
+ <%= link_to 'Upraviť', create_human_origin_admin_metais_project_path(project), method: :post, class: 'btn btn-secondary btn-sm' %>
+ <%= link_to 'Spracovať AI', run_ai_extraction_admin_metais_project_path(project), method: :post, class: 'btn btn-priamry btn-sm' %>
+
+
+ <% end %>
+
+
+
+ <%= paginate @projects %>
+
\ No newline at end of file
diff --git a/app/views/admin/pages/index.html.erb b/app/views/admin/pages/index.html.erb
index 4577ebc..6091ad3 100644
--- a/app/views/admin/pages/index.html.erb
+++ b/app/views/admin/pages/index.html.erb
@@ -7,6 +7,9 @@
<%= link_to 'Synchronize', sync_admin_pages_path, class: 'btn btn-primary btn-sm', method: :put %>
<%= link_to 'Synchronize Google drafts', sync_google_admin_pages_path, class: 'btn btn-primary btn-sm', method: :put %>
+
+ <%= link_to 'MetaIS Admin', admin_metais_projects_path, class: 'btn btn-secondary btn-sm' %>
+
Flags
diff --git a/app/views/admin/pages/preview.html.erb b/app/views/admin/pages/preview.html.erb
index 3e8294c..b07710f 100644
--- a/app/views/admin/pages/preview.html.erb
+++ b/app/views/admin/pages/preview.html.erb
@@ -12,7 +12,6 @@
<% if @phase.present? %>
- <% @project = @phase_revision %>
<%= render file: 'phase_revision/show' %>
<% else %>
<% @page = @phase_revision %>
diff --git a/app/views/components/_footer.html.erb b/app/views/components/_footer.html.erb
new file mode 100644
index 0000000..5b6daa9
--- /dev/null
+++ b/app/views/components/_footer.html.erb
@@ -0,0 +1,92 @@
+
\ No newline at end of file
diff --git a/app/views/components/_navbar.html.erb b/app/views/components/_navbar.html.erb
new file mode 100644
index 0000000..aa5cdcd
--- /dev/null
+++ b/app/views/components/_navbar.html.erb
@@ -0,0 +1,45 @@
+
+
+
+ <%= image_tag 'temp_logo.svg', width: 200 %>
+
+
+
+
+
+
+
+
+
+
+ ">
+ <%= link_to 'Štátne IT projekty', metais_projects_path, class: 'nav-link' %>
+
+ ">
+ <%= link_to 'Hodnotenia Red Flags', projects_path, class: 'nav-link' %>
+
+ ">
+ <%= link_to 'Čo je Red Flags', about_path, class: 'nav-link' %>
+
+ ">
+ <%= link_to 'Ako sa zapojiť', contribute_path, class: 'nav-link' %>
+
+ ">
+ <%= link_to 'Ako hodnotíme', about_rating_path, class: 'nav-link' %>
+
+
+
+ O projekte
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index fb4a916..0a8a332 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -46,110 +46,13 @@
-
-
-
- <%= image_tag 'temp_logo.svg', width: '200' %>
-
-
-
-
-
-
- ">
- <%= link_to 'Projekty', projects_path, class: 'nav-link' %>
-
- ">
- <%= link_to 'Štátne IT v číslach', stats_path, class: 'nav-link' %>
-
- ">
- <%= link_to 'Čo je Red Flags', about_path, class: 'nav-link' %>
-
- ">
- <%= link_to 'Ako sa zapojiť', contribute_path, class: 'nav-link' %>
-
- ">
- <%= link_to 'Ako hodnotíme', about_rating_path, class: 'nav-link' %>
-
-
+ <%= render 'components/navbar' %>
+ <% flash.each do |message_type, message| %>
+
+ <%= message %>
-
-
-
+ <% end %>
<%= yield %>
-
+ <%= render 'components/footer' %>