From bd9476544caac7cbb66132e93916ebd83c41af66 Mon Sep 17 00:00:00 2001 From: Bryce Date: Tue, 17 Oct 2023 13:03:48 -0700 Subject: [PATCH] Makes pnl work for companies with parenthesis --- resources/public/output.css | 14 ++ src/clj/auto_ap/datomic/accounts.clj | 10 - src/clj/auto_ap/graphql.clj | 10 +- src/clj/auto_ap/graphql/jobs.clj | 161 ------------ src/clj/auto_ap/pdf/ledger.clj | 11 +- src/clj/auto_ap/ssr/admin/background_jobs.clj | 237 ++++++++++++++++++ src/clj/auto_ap/ssr/components/aside.clj | 2 +- src/clj/auto_ap/ssr/core.clj | 4 +- src/cljc/auto_ap/client_routes.cljc | 1 - src/cljc/auto_ap/ssr_routes.cljc | 5 + src/cljs/auto_ap/views/main.cljs | 5 - src/cljs/auto_ap/views/pages/admin/jobs.cljs | 224 ----------------- .../auto_ap/views/pages/admin/jobs/table.cljs | 72 ------ 13 files changed, 269 insertions(+), 487 deletions(-) delete mode 100644 src/clj/auto_ap/graphql/jobs.clj create mode 100644 src/clj/auto_ap/ssr/admin/background_jobs.clj delete mode 100644 src/cljs/auto_ap/views/pages/admin/jobs.cljs delete mode 100644 src/cljs/auto_ap/views/pages/admin/jobs/table.cljs diff --git a/resources/public/output.css b/resources/public/output.css index a87f6a67..19070cfe 100644 --- a/resources/public/output.css +++ b/resources/public/output.css @@ -1248,6 +1248,10 @@ input:checked + .toggle-bg { margin-top: 1.25rem; } +.mr-1 { + margin-right: 0.25rem; +} + .block { display: block; } @@ -1634,6 +1638,10 @@ input:checked + .toggle-bg { gap: 2rem; } +.gap-3 { + gap: 0.75rem; +} + .gap-x-4 { -moz-column-gap: 1rem; column-gap: 1rem; @@ -1699,6 +1707,12 @@ input:checked + .toggle-bg { margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); } +.space-x-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.75rem * var(--tw-space-x-reverse)); + margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); +} + .divide-y > :not([hidden]) ~ :not([hidden]) { --tw-divide-y-reverse: 0; border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); diff --git a/src/clj/auto_ap/datomic/accounts.clj b/src/clj/auto_ap/datomic/accounts.clj index 8122c02a..64b44fe6 100644 --- a/src/clj/auto_ap/datomic/accounts.clj +++ b/src/clj/auto_ap/datomic/accounts.clj @@ -52,16 +52,6 @@ (map first) (map <-datomic))))) -(defn get-by-id [id] - (->> - (dc/q {:find [(list 'pull '?e default-read)] - :in ['$ '?e]} - (dc/db conn ) - id) - (map first) - (map <-datomic) - first)) - (defn get-for-vendor [vendor-id client-id] (if client-id (->> diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index a3501c02..0a5856ae 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -9,13 +9,12 @@ [auto-ap.graphql.import-batch :as gq-import-batches] [auto-ap.graphql.intuit-bank-accounts :as gq-intuit-bank-accounts] [auto-ap.graphql.invoices :as gq-invoices] - [auto-ap.graphql.jobs :as gq-jobs] [auto-ap.graphql.ledger :as gq-ledger] [auto-ap.graphql.plaid :as gq-plaid] [auto-ap.graphql.sales-orders :as gq-sales-orders] [auto-ap.graphql.transaction-rules :as gq-transaction-rules] [auto-ap.graphql.transactions :as gq-transactions] - [auto-ap.graphql.utils :refer [assert-admin attach-tracing-resolvers]] + [auto-ap.graphql.utils :refer [attach-tracing-resolvers]] [auto-ap.graphql.vendors :as gq-vendors] [auto-ap.graphql.yodlee-merchants :as ym] [auto-ap.logging :as alog :refer [error-event info-event warn-event]] @@ -23,8 +22,6 @@ [clj-time.coerce :as coerce] [clj-time.core :as t] [clojure.string :as str] - [config.core :refer [env]] - [clojure.tools.logging :as log] [clojure.walk :as walk] [com.brunobonacci.mulog :as mu] [com.unbounce.dogstatsd.core :as statsd] @@ -33,9 +30,7 @@ [com.walmartlabs.lacinia.schema :as schema] [datomic.api :as dc] [unilog.context :as lc] - [yang.time :refer [time-it]] - [auto-ap.routes.auth :as auth] - [buddy.sign.jwt :as jwt]) + [yang.time :refer [time-it]]) (:import (clojure.lang IPersistentMap))) @@ -782,7 +777,6 @@ gq-invoices/attach gq-clients/attach gq-sales-orders/attach - gq-jobs/attach schema/compile)) diff --git a/src/clj/auto_ap/graphql/jobs.clj b/src/clj/auto_ap/graphql/jobs.clj deleted file mode 100644 index 9b5bec0f..00000000 --- a/src/clj/auto_ap/graphql/jobs.clj +++ /dev/null @@ -1,161 +0,0 @@ -(ns auto-ap.graphql.jobs - (:require - [amazonica.aws.ecs :as ecs] - [auto-ap.graphql.utils - :refer [assert-admin assert-failure result->page]] - [clj-time.coerce :as coerce] - [clj-time.core :as time] - [clojure.string :as str] - [config.core :refer [env]] - [clojure.tools.logging :as log] - [com.walmartlabs.lacinia.util :refer [attach-resolvers]] - [clojure.edn :as edn] - [auto-ap.graphql.utils :refer [attach-tracing-resolvers]]) - (:import - (com.amazonaws.services.ecs.model AssignPublicIp))) - -(defn get-ecs-tasks [] - (->> - (concat (:task-arns (ecs/list-tasks :max-results 50)) (:task-arns (ecs/list-tasks :desired-status "STOPPED" :max-results 50))) - (ecs/describe-tasks :include [] :tasks) - :tasks - (map #(assoc % :task-definition (:task-definition (ecs/describe-task-definition :task-definition (:task-definition-arn %))))) - (sort-by :created-at) - reverse)) - -(defn is-background-job? [task] - (->> task - :task-definition - :container-definitions - (mapcat :environment) - (filter (comp #{"INTEGREAT_JOB"} :name)) - seq)) - -(defn task-definition->job-name [task-definition] - (->> (:container-definitions task-definition) - (mapcat :environment) - (filter (comp #{"INTEGREAT_JOB"} :name)) - (map :value) - first)) - -(defn job-exited-successfully? [task] - (if (= 0 (->> task - :containers - (filter (comp #{"integreat-app" } :name)) - (first) - :exit-code)) - true - false)) - -(defn ecs-task->job [task] - - {:status (condp = (:last-status task) - "RUNNING" :running - "PENDING" :pending - "PROVISIONING" :pending - "DEPROVISIONING" :running - "STOPPED" (if (job-exited-successfully? task) - :succeeded - :failed)) - :name (task-definition->job-name (:task-definition task)) - :end-date (some-> (:stopped-at task) coerce/to-date-time (time/to-time-zone (time/time-zone-for-offset 0))) - :start-date (some-> (:created-at task) coerce/to-date-time (time/to-time-zone (time/time-zone-for-offset 0)))}) - -(defn get-jobs-page [context args _] - (assert-admin (:id context)) - (let [args (assoc (:filters args) :id (:id context)) - jobs (->> (get-ecs-tasks) - (filter is-background-job?) - (map ecs-task->job))] - (result->page - jobs - (count jobs) - :data - args))) - - -(defn currently-running-jobs [] - (->> (get-ecs-tasks) - (filter is-background-job?) - (map ecs-task->job) - (filter (comp #{:pending :running} :status)) - (map :name) - set)) - -(defn run-task [task args] - (log/info "running job" task) - (ecs/run-task (cond-> {:capacity-provider-strategy [{:base 1 :weight 1 :capacity-provider "FARGATE_SPOT"}] - :count 1 - :cluster "default" - :enable-ecs-managed-tags true - :task-definition task - :network-configuration {:aws-vpc-configuration {:subnets ["subnet-5e675761" "subnet-8519fde2" "subnet-89bab8d4"] - :security-groups ["sg-004e5855310c453a3" "sg-02d167406b1082698"] - :assign-public-ip AssignPublicIp/ENABLED}}} - args (assoc-in [:overrides :container-overrides ] [{:name "integreat-app" :environment [{:name "args" :value (pr-str args)}]}])))) - -(defn request-job [context value _] - (assert-admin (:id context)) - (if (not (get (currently-running-jobs) (:which value))) - (let [new-job (run-task - (-> (:which value) - (str/replace #"-" "_") - (str/replace #":" "") - (str "_" (:dd-env env))) - (some-> (:args value) edn/read-string))] - {:message (str "task " (str new-job) " started.")}) - (assert-failure "This job is already running"))) - - -(def objects - {:job {:fields {:name {:type 'String} - :start_date {:type :iso_date_time} - :end_date {:type :iso_date_time} - :status {:type :job_status}}} - - :jobs_page {:fields {:data {:type '(list :job)} - :count {:type 'Int} - :total {:type 'Int} - :start {:type 'Int} - :end {:type 'Int}}}}) - -(def input-objects - {:jobs_filters {:fields {:sort {:type '(list :sort_item)} - :start {:type 'Int} - :per_page {:type 'Int}}}}) - -(def queries - {:jobs_page {:type :jobs_page - :args {:filters {:type :jobs_filters} - :sort {:type '(list :sort_item)} - :start {:type 'Int} - :per_page {:type 'Int}} - :resolve :get-jobs-page}}) - -(def enums - {:job_status {:values [{:enum-value :pending} - {:enum-value :running} - {:enum-value :succeeded} - {:enum-value :failed}]} }) - -(def resolvers - {:get-jobs-page get-jobs-page - :mutation/request-job request-job}) - -(def mutations - {:request_job - {:type :message - :args {:which {:type 'String} - :args {:type 'String}} - :resolve :mutation/request-job}}) - -(defn attach [schema] - (-> - (merge-with merge schema - {:objects objects - :input-objects input-objects - :queries queries - :enums enums - :mutations mutations}) - (attach-tracing-resolvers resolvers))) - diff --git a/src/clj/auto_ap/pdf/ledger.clj b/src/clj/auto_ap/pdf/ledger.clj index a4070730..b492eb94 100644 --- a/src/clj/auto_ap/pdf/ledger.clj +++ b/src/clj/auto_ap/pdf/ledger.clj @@ -293,6 +293,9 @@ output-stream) (.toByteArray output-stream))) +(defn join-names [client-ids] + (str/replace (->> client-ids (pull-many (dc/db conn) [:client/name]) (map :client/name) (str/join "-")) #"[^\w]" "_" )) + (defn pnl-args->name [args] (let [min-date (atime/unparse-local (->> args :periods (map :start) first) @@ -300,7 +303,7 @@ max-date (atime/unparse-local (->> args :periods (map :end) last) atime/iso-date) - names (str/replace (->> args :client_ids (pull-many (dc/db conn) [:client/name]) (map :client/name) (str/join "-")) #" " "_" )] + names (->> args :client_ids join-names)] (format "Profit-and-loss-%s-to-%s-for-%s" min-date max-date names))) (defn cash-flows-args->name [args] @@ -310,7 +313,7 @@ max-date (atime/unparse-local (->> args :periods (map :end) last) atime/iso-date) - names (str/replace (->> args :client_ids (pull-many (dc/db conn) [:client/name]) (map :client/name) (str/join "-")) #" " "_" )] + names (->> args :client_ids join-names)] (format "Cash-flows-%s-to-%s-for-%s" min-date max-date names))) (defn journal-detail-args->name [args] @@ -320,14 +323,14 @@ max-date (atime/unparse-local (->> args :date_range :end) atime/iso-date) - names (str/replace (->> args :client_ids (pull-many (dc/db conn) [:client/name]) (map :client/name) (str/join "-")) #" " "_" )] + names (->> args :client_ids join-names)] (format "Profit-and-loss-%s-to-%s-for-%s" min-date max-date names))) (defn balance-sheet-args->name [args] (let [date (atime/unparse-local (:date args) atime/iso-date) - name (str/replace (->> args :client_ids (pull-many (dc/db conn) [:client/name]) (map :client/name) (str/join "-")) #" " "_" )] + name (->> args :client_ids join-names)] (format "Balance-sheet-%s-for-%s" date name))) (defn print-pnl [user args data] diff --git a/src/clj/auto_ap/ssr/admin/background_jobs.clj b/src/clj/auto_ap/ssr/admin/background_jobs.clj new file mode 100644 index 00000000..d468010f --- /dev/null +++ b/src/clj/auto_ap/ssr/admin/background_jobs.clj @@ -0,0 +1,237 @@ +(ns auto-ap.ssr.admin.background-jobs + (:require + [amazonica.aws.ecs :as ecs] + [auto-ap.logging :as alog] + [auto-ap.routes.utils + :refer [wrap-admin wrap-client-redirect-unauthenticated]] + [auto-ap.ssr-routes :as ssr-routes] + [auto-ap.ssr.components :as com] + [auto-ap.ssr.grid-page-helper :as helper] + [auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]] + [auto-ap.ssr.utils + :refer [apply-middleware-to-all-handlers + html-response + validation-error + wrap-form-4xx + wrap-schema-decode]] + [auto-ap.time :as atime] + [bidi.bidi :as bidi] + [clj-time.coerce :as coerce] + [clj-time.core :as time] + [clojure.string :as str] + [config.core :refer [env]]) + (:import + (com.amazonaws.services.ecs.model AssignPublicIp))) + +(defn get-ecs-tasks [] + (->> + (concat (:task-arns (ecs/list-tasks :max-results 50)) (:task-arns (ecs/list-tasks :desired-status "STOPPED" :max-results 50))) + (ecs/describe-tasks :include [] :tasks) + :tasks + (map #(assoc % :task-definition (:task-definition (ecs/describe-task-definition :task-definition (:task-definition-arn %))))) + (sort-by :created-at) + reverse)) + +(defn is-background-job? [task] + (->> task + :task-definition + :container-definitions + (mapcat :environment) + (filter (comp #{"INTEGREAT_JOB"} :name)) + seq)) + +(defn task-definition->job-name [task-definition] + (->> (:container-definitions task-definition) + (mapcat :environment) + (filter (comp #{"INTEGREAT_JOB"} :name)) + (map :value) + first)) + +(defn job-exited-successfully? [task] + (if (= 0 (->> task + :containers + (filter (comp #{"integreat-app" } :name)) + (first) + :exit-code)) + true + false)) + +(defn ecs-task->job [task] + + {:status (condp = (:last-status task) + "RUNNING" :running + "PENDING" :pending + "PROVISIONING" :pending + "DEPROVISIONING" :running + "STOPPED" (if (job-exited-successfully? task) + :succeeded + :failed)) + :name (task-definition->job-name (:task-definition task)) + :end-date (some-> (:stopped-at task) coerce/to-date-time (time/to-time-zone (time/time-zone-for-offset 0))) + :start-date (some-> (:created-at task) coerce/to-date-time (time/to-time-zone (time/time-zone-for-offset 0)))}) + +(defn fetch-page [request] + (let [jobs (->> (get-ecs-tasks) + (filter is-background-job?) + (map ecs-task->job))] + [jobs (count jobs)])) + +(def grid-page + (helper/build {:id "job-table" + :nav (com/admin-aside-nav) + :fetch-page fetch-page + :action-buttons (fn [request] + [(com/button {:hx-get (str (bidi/path-for ssr-routes/only-routes + :admin-job-start-dialog)) + :hx-target "#modal-holder" + :hx-swap "outerHTML" + :color :primary} + "Run job")]) + :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes + :admin)} + "Admin"] + + [:a {:href (bidi/path-for ssr-routes/only-routes + :admin-jobs)} + "Background Jobs"]] + :title "Jobs" + :entity-name "Job" + :route :admin-job-table + :headers [ + {:key "start" + :name "Start" + :render #(some-> % :start-date (atime/unparse-local atime/standard-time))} + {:key "end" + :name "End" + :render #(some-> % :end-date (atime/unparse-local atime/standard-time))} + {:key "duration" + :name "Duration" + :render (fn [e] + (when (and (:start-date e) + (:end-date e)) + (str (time/in-minutes (time/interval + (:start-date e) + (:end-date e))) " minutes")))} + {:key "name" + :name "Name" + :render :name} + {:key "status" + :name "Status" + :render :status}]})) + +(def row* (partial helper/row* grid-page)) +(def table* (partial helper/table* grid-page)) + +(defn currently-running-jobs [] + (->> (get-ecs-tasks) + (filter is-background-job?) + (map ecs-task->job) + (filter (comp #{:pending :running} :status)) + (map :name) + set)) + +(defn run-task [task args] + (alog/info ::starting :task task) + (ecs/run-task (cond-> {:capacity-provider-strategy [{:base 1 :weight 1 :capacity-provider "FARGATE_SPOT"}] + :count 1 + :cluster "default" + :enable-ecs-managed-tags true + :task-definition task + :network-configuration {:aws-vpc-configuration {:subnets ["subnet-5e675761" "subnet-8519fde2" "subnet-89bab8d4"] + :security-groups ["sg-004e5855310c453a3" "sg-02d167406b1082698"] + :assign-public-ip AssignPublicIp/ENABLED}}} + args (assoc-in [:overrides :container-overrides ] [{:name "integreat-app" :environment [{:name "args" :value (pr-str args)}]}])))) + +(defn job-start [{:keys [form-params]}] + (if (not (get (currently-running-jobs) (:name form-params))) + (let [new-job (run-task + (-> (:name form-params) + (str/replace #"-" "_") + (str/replace #":" "") + (str "_" (:dd-env env))) + (dissoc form-params :name))] + {:message (str "task " (str new-job) " started.")}) + (validation-error "This job is already running"))) + +(defn subform [{{:strs [name]} :query-params }] + (html-response (cond (= "bulk-journal-import" name) + [:div (com/field {:label "Url"} + [:div.flex.place-items-center.gap-2 + [:pre.text-xs.mr-1 "s3://data.prod.app.integreatconsult.com/bulk-import/"] + (com/text-input {:placeholder "ledger-data.csv" + :name "ledger-url"} )])] + (= "register-invoice-import" name) + [:div (com/field {:label "Url"} + [:div.flex.place-items-center.gap-2 + [:pre.text-xs.mr-1 "s3://data.prod.app.integreatconsult.com/bulk-import/"] + (com/text-input {:placeholder "invoice-data.csv" + :name "invoice-url"} )])] + (= "load-historical-sales" name) + [:div + (com/field {:label "Client"} + (com/typeahead {:name "client" + :placeholder "Search..." + :url (bidi/path-for ssr-routes/only-routes + :company-search) + :id (str "client-search")})) + (com/field {:label "Days to load"} + (com/text-input {:placeholder "60" + :name "days"} ))] + :else [:div])) + + ) + +(defn job-start-dialog [_] + (html-response (com/modal + {:modal-class "max-w-4xl"} + [:form#edit-form {:hx-ext "response-targets" + :hx-post (bidi/path-for ssr-routes/only-routes :admin-job-start + ) + :hx-swap "outerHTML swap:300ms" + :hx-target-400 "#form-errors .error-content"} + [:fieldset {:class "hx-disable"} + (com/modal-card + {} + [:div.flex [:div.p-2 "New job"] ] + [:div.space-y-6 + + (com/field {:label "Job"} + (com/select {:name "name" + :class "w-64" + :options [["" ""] + ["yodlee2" "Yodlee Import"] + ["yodlee2-accounts" "Yodlee Account Import"] + ["intuit" "Intuit import"] + ["plaid" "Plaid import"] + ["bulk-journal-import" "Bulk Journal Import"] + ["square2-import-job" "Square2 Import"] + ["register-invoice-import" "Register Invoice Import "] + ["ezcater-upsert" "Upsert recent ezcater orders"] + ["load-historical-sales" "Load Historical Square Sales"] + ["export-backup" "Export Backup"]] + :hx-get (bidi/path-for ssr-routes/only-routes + :admin-job-subform) + :hx-target "#sub-form" + :hx-swap "innerHTML"})) + + [:div#sub-form] + [:div#form-errors [:span.error-content]] + (com/button {:color :primary :form "edit-form" :type "submit"} + "Run")] + [:div])]]))) + +(def key->handler + (apply-middleware-to-all-handlers + (->> + {:admin-jobs (helper/page-route grid-page) + :admin-job-table (helper/table-route grid-page) + :admin-job-subform (-> subform (wrap-schema-decode :query-schema [:map [:name :string]])) + :admin-job-start (-> job-start + (wrap-schema-decode :form-schema [:map [:name :string]]) + (wrap-nested-form-params) + (wrap-form-4xx)) + :admin-job-start-dialog job-start-dialog}) + (fn [h] + (-> h + (wrap-admin) + (wrap-client-redirect-unauthenticated))))) diff --git a/src/clj/auto_ap/ssr/components/aside.clj b/src/clj/auto_ap/ssr/components/aside.clj index 24c76639..4410027e 100644 --- a/src/clj/auto_ap/ssr/components/aside.clj +++ b/src/clj/auto_ap/ssr/components/aside.clj @@ -323,7 +323,7 @@ [:li (menu-button- {:icon svg/rabbit - :href (bidi/path-for client-routes/routes + :href (bidi/path-for ssr-routes/only-routes :admin-jobs)} "Background Jobs")] [:li diff --git a/src/clj/auto_ap/ssr/core.clj b/src/clj/auto_ap/ssr/core.clj index 6c6d3343..230bf309 100644 --- a/src/clj/auto_ap/ssr/core.clj +++ b/src/clj/auto_ap/ssr/core.clj @@ -4,6 +4,7 @@ [auto-ap.routes.utils :refer [wrap-admin wrap-client-redirect-unauthenticated wrap-secure]] [auto-ap.ssr.admin.history :as history] + [auto-ap.ssr.admin.background-jobs :as admin-jobs] [auto-ap.ssr.auth :as auth] [auto-ap.ssr.company :as company] [auto-ap.ssr.company-dropdown :as company-dropdown] @@ -73,5 +74,6 @@ (into pos-cash-drawer-shifts/key->handler) (into pos-refunds/key->handler) (into users/key->handler) - (into admin-accounts/key->handler))) + (into admin-accounts/key->handler) + (into admin-jobs/key->handler))) diff --git a/src/cljc/auto_ap/client_routes.cljc b/src/cljc/auto_ap/client_routes.cljc index a0857e8b..c15ccf6b 100644 --- a/src/cljc/auto_ap/client_routes.cljc +++ b/src/cljc/auto_ap/client_routes.cljc @@ -12,7 +12,6 @@ "/bank-accounts/" {[:bank-account] :admin-specific-bank-account}}} "rules" :admin-rules "import-batches" :admin-import-batches - "jobs" :admin-jobs "vendors" :admin-vendors "excel-import" :admin-excel-import } diff --git a/src/cljc/auto_ap/ssr_routes.cljc b/src/cljc/auto_ap/ssr_routes.cljc index faef1cd7..30708ecb 100644 --- a/src/cljc/auto_ap/ssr_routes.cljc +++ b/src/cljc/auto_ap/ssr_routes.cljc @@ -25,6 +25,11 @@ "/new" {:get :admin-account-new-dialog} [[#"\d+" :db/id] "/edit"] :admin-account-edit-dialog "/override/new" :admin-account-client-override-new} + "/background-job" {"" {:get :admin-jobs + :post :admin-job-start} + "/table" :admin-job-table + "/new" {:get :admin-job-start-dialog} + "/subform" :admin-job-subform} "/ezcater-xls" :admin-ezcater-xls} "transaction" {"/insights" {"" :transaction-insights "/table" :transaction-insight-table diff --git a/src/cljs/auto_ap/views/main.cljs b/src/cljs/auto_ap/views/main.cljs index e0963e87..cb2399ce 100644 --- a/src/cljs/auto_ap/views/main.cljs +++ b/src/cljs/auto_ap/views/main.cljs @@ -11,7 +11,6 @@ [auto-ap.views.pages.reports :refer [reports-page]] [auto-ap.views.pages.error :refer [error-page]] [auto-ap.views.pages.ledger.balance-sheet :refer [balance-sheet-page]] - [auto-ap.views.pages.admin.jobs :refer [jobs-page]] [auto-ap.views.pages.ledger.external-import :refer [external-import-page]] [auto-ap.views.pages.ledger.external-ledger :refer [external-ledger-page]] [auto-ap.views.pages.ledger.profit-and-loss :refer [profit-and-loss-page]] @@ -112,10 +111,6 @@ (defmethod page :admin-import-batches [_] [import-batches-page]) -(defmethod page :admin-jobs [_] - [jobs-page]) - - (defmethod page :company-other [_] (company-other/company-other-page)) diff --git a/src/cljs/auto_ap/views/pages/admin/jobs.cljs b/src/cljs/auto_ap/views/pages/admin/jobs.cljs deleted file mode 100644 index cbb5c274..00000000 --- a/src/cljs/auto_ap/views/pages/admin/jobs.cljs +++ /dev/null @@ -1,224 +0,0 @@ -(ns auto-ap.views.pages.admin.jobs - (:require - [auto-ap.status :as status] - [auto-ap.subs :as subs] - [auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]] - [auto-ap.views.components.layouts :refer [side-bar-layout]] - [auto-ap.views.pages.admin.jobs.table :as table] - [auto-ap.views.components.modal :as modal] - [auto-ap.views.components :as com] - [auto-ap.views.pages.data-page :as data-page] - [auto-ap.views.utils :refer [dispatch-event with-user]] - [clojure.set :as set] - [re-frame.core :as re-frame] - [vimsical.re-frame.fx.track :as track] - [auto-ap.forms.builder :as form-builder] - [vimsical.re-frame.cofx.inject :as inject] - [auto-ap.forms :as forms] - [clojure.string :as str])) - -(def a 1) - - -(def default-read [:name :start-date :end-date :status]) - -(re-frame/reg-event-fx - ::params-change - [with-user ] - (fn [{:keys [user]} [_ params]] - {:graphql {:token user - :owns-state {:single [::data-page/page ::page]} - :query-obj {:venia/queries [{:query/data [:jobs_page - {:filters {:sort (:sort params) - :start (:start params 0) - :per-page (:per-page params)}} - [[:data default-read] - :total - :start - :end]] - :query/alias :result}]} - :on-success (fn [result] - [::data-page/received ::page - (set/rename-keys (:result result) - {:jobs :data})])}})) - -(re-frame/reg-sub - ::msg - (fn [db] - (::msg db))) - -(re-frame/reg-event-fx - ::success - (fn [{:keys [db]} [_ n]] - {:db (assoc db ::msg (:message (:request-job n))) - :dispatch-n [[::params-change] - [::modal/modal-closed]]})) - -(re-frame/reg-event-fx - ::saving - [with-user - (re-frame/inject-cofx ::inject/sub [::forms/form ::form]) - ] - (fn [{:keys [user] ::forms/keys [form]} [_ which]] - (let [which (:job (:data form))] - - {:graphql {:token user - :owns-state {:single which} - :query-obj - {:venia/operation {:operation/type :mutation - :operation/name "RequestJob"} - :venia/queries [{:query/data - [:request-job - {:which (str which) - :args (-> (cond (= which :bulk-journal-import) - (select-keys (:data form) [:ledger-url]) - - (= which :register-invoice-import) - (select-keys (:data form) [:invoice-url]) - - (= which :load-historical-sales) - {:days (-> form :data :days) - :client (-> form :data :client :id)} - - :else - nil - ) - pr-str - (str/escape {\" "\\\""}))} - [:message]]}]} - :on-success [::success]}}))) - -(re-frame/reg-event-fx - ::mounted - (fn [_] - {::track/register {:id ::params - :subscription [::data-page/params ::page] - :event-fn (fn [params] - [::params-change params])}})) - -(re-frame/reg-event-fx - ::unmounted - (fn [{:keys [db]}] - {:dispatch-n [[::data-page/dispose ::page]] - ::track/dispose {:id ::params} - :db (dissoc db ::msg)})) - -(defn job-button [{:keys [which]} content] - (let [status @(re-frame/subscribe [::status/single which])] - (println status) - [:button.button.is-primary.is-small {:type "button" - :on-click (dispatch-event [::request which]) - :disabled (status/disabled-for status) - :class (status/class-for status)} - content])) - -(defn notification [] - (let [state @(re-frame/subscribe [::status/single ::form])] - [:<> - (when-let [msg @(re-frame/subscribe [::msg])] - [:div.notification.is-info.is-light msg]) - [:div - (cond (:info state) - [:div.notification.is-info.is-light (:info state)] - - (seq (:error state)) - [:div.notification.is-info.is-warning.is-light (:message (first (:error state)))] - - :else - nil)]])) - -(defn new-job-form [] - (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form]) - clients @(re-frame/subscribe [::subs/clients])] - [:<> - [form-builder/builder {:submit-event [::saving] - :id ::form} - [form-builder/field-v2 {:required? true - :field :job} - "Job Type" - [com/select-field {:options [[:yodlee2 "Yodlee Import"] - [:yodlee2-accounts "Yodlee Account Import"] - [:intuit "Intuit import"] - [:plaid "Plaid import"] - [:bulk-journal-import "Bulk Journal Import"] - [:square2-import-job "Square2 Import"] - [:register-invoice-import "Register Invoice Import "] - [:ezcater-upsert "Upsert recent ezcater orders"] - [:load-historical-sales "Load Historical Square Sales"] - [:export-backup "Export Backup"]] - :allow-nil? true - :keywordize? true}]] - (cond (= :bulk-journal-import (:job data)) - [:<> - [:label.label "Url"] - [:p.field.has-addons - [:p.control - [:a.button.is-static - "s3://data.prod.app.integreatconsult.com/bulk-import/"]] - [:p.control - [form-builder/raw-field-v2 {:field :ledger-url} - [:input.input {:placeholder "ledger-data.csv"}]]]]] - - (= :register-invoice-import (:job data)) - [:<> - [:label.label "Url"] - [:p.field.has-addons - [:p.control - [:a.button.is-static - "s3://data.prod.app.integreatconsult.com/bulk-import/"]] - [:p.control - [form-builder/raw-field-v2 {:field :invoice-url} - [:input.input {:placeholder "invoice-data.csv"}]]]]] - - (= :load-historical-sales (:job data)) - [:<> - [form-builder/field-v2 {:required? true - :field [:client]} - "Client" - [com/entity-typeahead {:entities @(re-frame/subscribe [::subs/clients]) - :entity->text :name}]] - - [form-builder/field-v2 {:field :days} - "Days to load (recommended 60)" - [com/number-input {:placeholder "60" - :style {:width "7em"}}]]] - :else - nil) - - [form-builder/hidden-submit-button]]])) - - - -;; VIEWS -(def jobs-content - (with-meta - (fn [] - (let [user @(re-frame/subscribe [::subs/user])] - [:div - [:h1.title "Jobs"] - [notification] - - (when (= "admin" (:user/role user)) - [:div - [:div.is-pulled-right - [:button.button.is-primary.is-small {:type "button" - :on-click (dispatch-event [::modal/modal-requested - {:title "Start a new job" - :body [new-job-form] - :cancel? false - :confirm {:value "Submit" - :status-from [::status/single ::form] - :class "is-primary" - :on-click (dispatch-event [::saving]) - :close-event [::status/completed ::form]}}])} - "Run new job"] - ] - [table/table {:id :jobs - :data-page ::page}]])])) - {:component-did-mount (dispatch-event [::mounted ]) - :component-will-unmount #(re-frame/dispatch-sync [::unmounted])})) - -(defn jobs-page [] - [side-bar-layout {:side-bar [admin-side-bar {}] - :main [jobs-content]}]) - diff --git a/src/cljs/auto_ap/views/pages/admin/jobs/table.cljs b/src/cljs/auto_ap/views/pages/admin/jobs/table.cljs deleted file mode 100644 index 94408eeb..00000000 --- a/src/cljs/auto_ap/views/pages/admin/jobs/table.cljs +++ /dev/null @@ -1,72 +0,0 @@ -(ns auto-ap.views.pages.admin.jobs.table - (:require - [auto-ap.status :as status] - [auto-ap.subs :as subs] - [auto-ap.views.components.grid :as grid] - [auto-ap.views.pages.data-page :as data-page] - [auto-ap.views.utils - :refer [action-cell-width date->str dispatch-event pretty-long]] - [cljs-time.core :as time] - [re-frame.core :as re-frame] - [reagent.core :as r])) - -(re-frame/reg-sub - ::specific-table-params - (fn [db] - (::table-params db))) - - -(re-frame/reg-event-db - ::unmounted - (fn [db] - (status/reset-multi db ::run))) - -(re-frame/reg-sub - ::table-params - :<- [::specific-table-params] - :<- [::subs/query-params] - (fn [[specific-table-params query-params]] - (merge (select-keys query-params #{:start :sort}) specific-table-params ))) - -(defn table* [{:keys [data-page]}] - (let [{:keys [data]} @(re-frame/subscribe [::data-page/page data-page])] - [grid/grid {:data-page data-page - :column-count 6} - [grid/controls data] - [grid/table {:fullwidth true} - [grid/header - [grid/row {} - [grid/header-cell {} - "Start Date"] - - [grid/header-cell - "End Date"] - - [grid/header-cell - "Duration"] - [grid/header-cell - "Job Name"] - - [grid/header-cell {} - "Status"] - - [grid/header-cell {:style {:width (action-cell-width 3)}}]]] - [grid/body - (for [{:keys [start-date end-date status name] :as r} (:data data)] - ^{:key (str start-date)} - [grid/row {:class (:class r) :id (str start-date)} - [grid/cell {} (some-> start-date (date->str pretty-long))] - [grid/cell {} (some-> end-date (date->str pretty-long))] - [grid/cell {} (when (and start-date end-date) - (str (time/in-minutes (time/interval start-date end-date)) " minutes"))] - [grid/cell {} name] - [grid/cell {} status] - - [grid/button-cell {}]])]] - - [grid/bottom-paginator data]])) - -(defn table [] - (r/create-class {:component-will-unmount (dispatch-event [::unmounted]) - :reagent-render (fn [params] - [table* params])}))