commit be500fde33b5c6fce257244c450c276339eb9a9c Author: Martin Donnelly Date: Fri Apr 26 17:13:07 2024 +0100 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39d6fa7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,181 @@ +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Go template +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +### GoLand template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + diff --git a/db/jobs.db b/db/jobs.db new file mode 100644 index 0000000..8bdf3f4 Binary files /dev/null and b/db/jobs.db differ diff --git a/dist/build/bundle.css b/dist/build/bundle.css new file mode 100644 index 0000000..a4b3e42 --- /dev/null +++ b/dist/build/bundle.css @@ -0,0 +1,6 @@ +@import url("https://fonts.googleapis.com/css?family=Roboto+Condensed");:root{--primary-color:#607D8B;--dark-color:#294c5d;--light-color:#CFD8DC;--danger-color:#dc3545;--success-color:#28a745;--highlight-color:#dcc894;--highlight-color2:#dca394;--navbar-height:4rem}*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Roboto Condensed', sans-serif;font-size:1rem;line-height:1.6;background-color:#fff;color:#333;padding:0}a{color:var(--primary-color);text-decoration:none}a:hover{color:#666}a.active{color:var(--highlight-color)}ul{list-style:none}img{width:100%}.dataRow{cursor:pointer}.container{max-width:1100px;margin:auto;overflow:hidden;padding:0 2rem}.fullWidth{max-width:100vw;padding:0 0}.x-large{font-size:4rem;line-height:1.2;margin-bottom:1rem}.large{font-size:3rem;line-height:1.2;margin-bottom:1rem}.lead{font-size:1.5rem;margin-bottom:1rem}.text-center{text-align:center}.text-primary{color:var(--primary-color)}.text-dark{color:var(--dark-color)}.text-success{color:var(--success-color)}.text-danger{color:var(--danger-color)}.text-highlight{color:var(--highlight-color)}.text-highlight2{color:var(--highlight-color2)}.text-center{text-align:center}.text-right{text-align:right}.text-left{text-align:left}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-title,h3{font-weight:400;font-size:20px;line-height:28px}.text-subhead,h4{font-weight:400;font-size:16px;line-height:24px}.text-body2,h5{font-weight:500;font-size:14px;line-height:24px}.text-body1{font-weight:400;font-size:14px;line-height:20px}.text-caption{font-weight:400;font-size:12px;line-height:16px}.all-center{display:flex;flex-direction:column;width:100%;margin:auto;justify-content:center;align-items:center;text-align:center}.card{padding:1rem;border:#ccc 1px dotted;margin:0.7rem 0}.list{margin:0.5rem 0}.list li{padding-bottom:0.3rem}.p{padding:0.5rem}.p-1{padding:1rem}.p-2{padding:2rem}.p-3{padding:3rem}.py{padding:0.5rem 0}.py-1{padding:1rem 0}.py-2{padding:2rem 0}.py-3{padding:3rem 0}.m{margin:0.5rem}.m-1{margin:1rem}.mb-1{margin-bottom:.2rem !important}.ml-1{margin-left:.2rem !important}.mr-1{margin-right:.2rem !important}.mt-1{margin-top:.2rem !important}.mx-1{margin-left:1rem !important;margin-right:1rem !important}.m-2{margin:2rem}.mb-2{margin-bottom:.4rem !important}.ml-2{margin-left:.4rem !important}.mr-2{margin-right:.4rem !important}.mt-2{margin-top:.4rem !important}.mx-2{margin-left:2rem !important;margin-right:2rem !important}.m-3{margin:3rem}.my{margin:0.5rem 0}.my-1{margin:1rem 0}.my-2{margin:2rem 0}.my-3{margin:3rem 0}.grid{display:flex;display:-ms-flexbox;flex-wrap:wrap}.grid-1{display:grid;grid-template-columns:repeat(1, 1fr);grid-gap:1rem}.grid-2{display:grid;grid-template-columns:repeat(2, 1fr);grid-gap:1rem}.grid-3{display:grid;grid-template-columns:repeat(3, 1fr);grid-gap:1rem}.grid-4{display:grid;grid-template-columns:repeat(4, 1fr);grid-gap:1rem}.row{margin-left:-15px;margin-right:-15px}.row:before,.row:after{content:" ";display:table}.row:after{clear:both}.column,.columns{margin-left:4%}.column:first-child,.columns:first-child{margin-left:0}.col-1{width:8.33333%}.col-2{width:16.66667%}.col-3{width:25%}.col-4{width:33.33333%}.col-5{width:41.66667%}.col-6{width:50%}.col-7{width:58.33333%}.col-8{width:66.66667%}.col-9{width:75%}.col-10{width:83.33333%}.col-11{width:91.66667%}.col-12{width:100%;margin-left:0}.col-1-3rd{width:32.666667%}.col-2-3rd{width:65.3333333333%}.col-half{width:48%}.offset-1-col{margin-left:8.66666666667%}.offset-2-col{margin-left:17.3333333333%}.offset-3-col{margin-left:26%}.offset-4-col{margin-left:34.6666666667%}.offset-5-col{margin-left:43.3333333333%}.offset-6-col{margin-left:52%}.offset-7-col{margin-left:60.6666666667%}.offset-8-col{margin-left:69.3333333333%}.offset-9-col{margin-left:78.0%}.offset-10-col{margin-left:86.6666666667%}.offset-11-col{margin-left:95.3333333333%}.offset-1-3rd-col{margin-left:34.6666666667%}.offset-2-3rd-col{margin-left:69.3333333333%}.offset-half-col{margin-left:52%}.btn{display:inline-block;background:var(--light-color);color:#333;padding:0.4rem 1.3rem;font-size:1rem;border:none;cursor:pointer;margin-right:0.5rem;transition:opacity 0.2s ease-in;outline:none}.btn-link{background:none;padding:0;margin:0}.btn-block{display:block;width:100%}.btn-sm{font-size:0.8rem;padding:0.3rem 1rem;margin-right:0.2rem}.badge{display:inline-block;font-size:0.6rem;padding:0.1rem 0.4rem;text-align:center;margin:0.3rem;background:var(--light-color);color:#333;border-radius:3px}.alert{padding:0.7rem;margin:1rem 0;opacity:0.9;background:var(--light-color);color:#333}.btn-primary,.bg-primary,.badge-primary,.alert-primary{background:var(--primary-color);color:#fff}.btn-light,.bg-light,.badge-light,.alert-light{background:var(--light-color);color:#333}.btn-dark,.bg-dark,.badge-dark,.alert-dark{background:var(--dark-color);color:#fff}.btn-danger,.bg-danger,.badge-danger,.alert-danger{background:var(--danger-color);color:#fff}.btn-success,.bg-success,.badge-success,.alert-success{background:var(--success-color);color:#fff}.btn-white,.bg-white,.badge-white,.alert-white{background:#fff;color:#333;border:#ccc solid 1px}.btn:disabled{cursor:not-allowed;pointer-events:none;opacity:0.60;box-shadow:none}.btn:enabled:hover{opacity:0.8}.bg-light,.badge-light{border:#ccc solid 1px}.round-img{border-radius:50%}input{margin:.2rem 0}.form-text{display:block;margin-top:0.3rem;color:#888}input[type='text'],input[type='email'],input[type='password'],input[type='date'],select,textarea{display:block;width:100%;padding:0.1rem;border:1px solid #ccc}input[type='submit'],button{font:inherit}label,legend{display:block;margin-bottom:.1rem;font-weight:600}input[type="checkbox"],input[type="radio"]{display:inline}label > .label-body{display:inline-block;margin-left:.5rem;font-weight:normal;background-color:#dcc894}table th,table td{padding:1rem;text-align:left}table th{background:var(--light-color)}.navbar{position:fixed;display:flex;justify-content:space-between;align-items:center;padding:0.7rem 0rem;z-index:2;width:100%;opacity:0.9;margin-bottom:1rem;min-height:var(--navbar-height)}.navbar ul{display:flex}.navbar a{color:#fff;padding:0.45rem;margin:0 0.25rem}.navbar a:hover{color:var(--light-color)}.navbar .welcome span{margin-right:0.6rem}.navbar .navbar-section{align-items:center;display:flex;display:-ms-flexbox;flex:1 0 0;-ms-flex-align:center}.navbar .navbar-section:not(:first-child):last-child{justify-content:flex-end}.navbar .navbar-brand{font-size:125%;font-weight:bold}@media(max-width: 700px){.hide-sm{display:none}.grid-2,.grid-3,.grid-4{grid-template-columns:1fr}.x-large{font-size:3rem}.large{font-size:2rem}.lead{font-size:1rem}.navbar{display:flex;text-align:center}.navbar ul{text-align:center;justify-content:center}}.table-responsive{display:block;overflow-x:auto;width:100%}.cardV2{border-radius:4px;background-color:#fff;box-shadow:0 0 4px 0 rgba(0, 0, 0, 0.14), 0 3px 4px 0 rgba(0, 0, 0, 0.12), 0 1px 5px 0 rgba(0, 0, 0, 0.2);min-width:0}table{max-width:100%;width:100%;border:0;margin-bottom:1rem;border-collapse:collapse}tr{border-top:1px solid #ccc}tbody tr:nth-of-type(odd){background-color:rgba(0, 0, 0, 0.04)}tbody td{border-top:1px solid #e1e1e1}hr{margin-top:2.2rem;margin-bottom:2rem;border-width:0;border-top:1px solid var(--dark-color)}.modalWindow{position:fixed;top:0;right:0;bottom:0;left:0;background:rgba(0, 0, 0, 0.2);z-index:99999;opacity:0;pointer-events:none;text-align:center}.modalWindow:target{opacity:1;pointer-events:auto}.modalWindow > div{width:500px;position:relative;margin:10% auto;background:#fff}header + div.container{position:relative;top:var(--navbar-height);min-height:calc(100vh - var(--navbar-height));max-height:calc(100vh - var(--navbar-height))}.mui-dropdown{display:inline-block;position:relative}[data-mui-toggle="dropdown"]{outline:0}.mui-dropdown__menu{position:absolute;display:none;min-width:160px;padding:5px 3px;margin:2px 0 0;list-style:none;font-size:14px;text-align:left;background-color:#FFF;border-radius:2px;z-index:1;background-clip:padding-box;border:1px solid var(--light-color);box-shadow:0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12)}@media all and (-ms-high-contrast: none), (-ms-high-contrast: active){.mui-dropdown__menu{border-top:1px solid rgba(0, 0, 0, 0.12);border-left:1px solid rgba(0, 0, 0, 0.12)}}@supports (-ms-ime-align: auto){.mui-dropdown__menu{border-top:1px solid rgba(0, 0, 0, 0.12);border-left:1px solid rgba(0, 0, 0, 0.12)}}.mui-dropdown__menu.mui--is-open{display:block}.mui-dropdown__menu > li > a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.429;color:rgba(0, 0, 0, 0.87);text-decoration:none;white-space:nowrap}.mui-dropdown__menu > li > a:hover,.mui-dropdown__menu > li > a:focus{text-decoration:none;color:rgba(0, 0, 0, 0.87);background-color:#EEEEEE}.mui-dropdown__menu > .mui--is-disabled > a,.mui-dropdown__menu > .mui--is-disabled > a:hover,.mui-dropdown__menu > .mui--is-disabled > a:focus{color:#EEEEEE}.mui-dropdown__menu > .mui--is-disabled > a:hover,.mui-dropdown__menu > .mui--is-disabled > a:focus{text-decoration:none;background-color:transparent;background-image:none;cursor:not-allowed}.mui-dropdown__menu--right{left:auto;right:0}.mui-dropdown--up > .mui-dropdown__menu{margin:0 0 2px}.mui-dropdown--right > .mui-dropdown__menu{margin:0 0 0 2px}.mui-dropdown--left > .mui-dropdown__menu{margin:0 2px 0 0}.alias{cursor:alias}.all-scroll{cursor:all-scroll}.auto{cursor:auto}.cell{cursor:cell}.context-menu{cursor:context-menu}.col-resize{cursor:col-resize}.copy{cursor:copy}.crosshair{cursor:crosshair}.default{cursor:default}.e-resize{cursor:e-resize}.ew-resize{cursor:ew-resize}.grab{cursor:grab}.grabbing{cursor:grabbing}.help{cursor:help}.move{cursor:move}.n-resize{cursor:n-resize}.ne-resize{cursor:ne-resize}.nesw-resize{cursor:nesw-resize}.ns-resize{cursor:ns-resize}.nw-resize{cursor:nw-resize}.nwse-resize{cursor:nwse-resize}.no-drop{cursor:no-drop}.none{cursor:none}.not-allowed{cursor:not-allowed}.pointer{cursor:pointer}.progress{cursor:progress}.row-resize{cursor:row-resize}.s-resize{cursor:s-resize}.se-resize{cursor:se-resize}.sw-resize{cursor:sw-resize}.text{cursor:text}.url{cursor:url(myBall.cur), auto}.w-resize{cursor:w-resize}.wait{cursor:wait}.zoom-in{cursor:zoom-in}.zoom-out{cursor:zoom-out}.hour27{border-left:10px solid #50b5dd}.hour26{border-left:10px solid #4eb2ce}.hour25{border-left:10px solid #4cb0be}.hour24{border-left:10px solid #49adaf}.hour23{border-left:10px solid #48ab9f}.hour22{border-left:10px solid #46a88e}.hour21{border-left:10px solid #44a67d}.hour20{border-left:10px solid #42a46c}.hour19{border-left:10px solid #66ad5e}.hour18{border-left:10px solid #87be40}.hour17{border-left:10px solid #b3cc1a}.hour16{border-left:10px solid #d6d51c}.hour15{border-left:10px solid #f9ca03}.hour14{border-left:10px solid #f6b503}.hour13{border-left:10px solid #f4961a}.hour12{border-left:10px solid #ec6e05}.hour11{border-left:10px solid #ea5a24}.hour10{border-left:10px solid #e4572b}.hour09{border-left:10px solid #e14a29}.hour08{border-left:10px solid #e04127}.hour07{border-left:10px solid #d9372b}.hour06{border-left:10px solid #d63129}.hour05{border-left:10px solid #d12b2b}.hour04{border-left:10px solid #cd282f}.hour03{border-left:10px solid #c82432}.hour02{border-left:10px solid #c32334}.hour01{border-left:10px solid #be2138}.hour00{border-left:10px solid #b9203b}.view{height:100vh} +.detail.svelte-r7f80i{width:100%;border-top:2px solid #e1e1e1;height:50vh;max-height:50vh;overflow-y:scroll}.detailHead.svelte-r7f80i{box-sizing:border-box;margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row.svelte-r7f80i{margin:1% 0;overflow:auto;display:flex}.applied.svelte-r7f80i{box-shadow:inset 0 0 5px 5px #f7de2f69} +.lister.svelte-15500pg{height:50vh;max-height:50vh;overflow-y:scroll}.hide.svelte-15500pg{display:none}.company.svelte-15500pg{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dataRow.svelte-15500pg{font-weight:bold;color:darkslategrey}.read.svelte-15500pg{font-weight:normal !important;color:dimgrey}.applied.svelte-15500pg{box-shadow:inset 0 0 5px 5px #f7de2f69}.dataRow.svelte-15500pg:hover{background-color:rgba(245, 238, 100, 0.54)}.small.svelte-15500pg{font-size:70%}.noOverflow.svelte-15500pg{white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.menu.svelte-s6mfqx{border-right:2px solid #e1e1e1;background-color:#f4f4f4;height:100vh} + +/*# sourceMappingURL=bundle.css.map */ \ No newline at end of file diff --git a/dist/build/bundle.css.map b/dist/build/bundle.css.map new file mode 100644 index 0000000..06a4257 --- /dev/null +++ b/dist/build/bundle.css.map @@ -0,0 +1,18 @@ +{ + "version": 3, + "file": "bundle.css", + "sources": [ + "../../App.svelte", + "../../Detail.svelte", + "../../Lister.svelte", + "../../Menu.svelte" + ], + "sourcesContent": [ + "\n\n\n\n
\n
\n
\n \n \n
\n
\n\n", + "\n\n\n\nDETAIL :: {visible}\n{#if visible}\n
\n
\n
\n
\n {$JobDetails.title}\n\n
\n\n {#if jobtype === 1}\n Contract\n {:else if jobtype === 2}\n Permanent\n {/if}\n\n
\n
\n
\n Company: {$JobDetails.company}\n
\n
\n Location: {$JobDetails.location}\n
\n
\n Applied:\n {#if !$JobDetails.a}\n \n {:else}\n Applied!\n {/if}\n\n
\n\n
\n\n
\n
\n Salary: {$JobDetails.salary}\n
\n
\n Scraped: {$JobDetails.date}\n
\n
\n Site: {$JobDetails.site}\n
\n\n
\n\n
\n
\n {#each $JobDetails.data.autoclass.words as word, _wid}\n {#if $JobDetails.data.autoclass.good.indexOf(word) !== -1}\n {word}\n {:else if $JobDetails.data.autoclass.bad.indexOf(word) !== -1}\n {word}\n {:else }\n {word}\n {/if}\n {/each}\n\n
\n
\n \n
\n
\n \n
\n\n
\n\n\n
\n
\n {@html $JobDetails.summary}\n
\n
\n\n{/if}\n", + "\n\n\n\n\n
\n \n \n \n \n \n \n \n \n \n \n {#each $JobList as item, _id}\n\n \n \n \n \n \n \n \n {/each}\n \n
\n Title\n \n Site\n \n Company\n \n Date\n
\n {item.title}\n \n {item.site}{ellipses(item.company)}{item.date}
\n
\n\n\n", + "\n\n\n\n
\n

Jobs

\n
\n
\n \n
\n\n
\n" + ], + "names": [], + "mappings": "AAO0B,QAAQ,IAAI,0DAA0D,CAAC,CAAC,AAE1F,KAAK,AAAE,CAAC,AACd,eAAe,CAAE,OAAO,CACxB,YAAY,CAAE,OAAO,CACrB,aAAa,CAAE,OAAO,CACtB,cAAc,CAAE,OAAO,CACvB,eAAe,CAAE,OAAO,CACxB,iBAAiB,CAAE,OAAO,CAC1B,kBAAkB,CAAE,OAAO,CAC3B,eAAe,CAAE,IAAI,AAAE,CAAC,AAElB,CAAC,AAAE,CAAC,AACV,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,CAAC,CACT,OAAO,CAAE,CAAC,AAAE,CAAC,AAEP,IAAI,AAAE,CAAC,AACb,WAAW,CAAE,kBAAkB,CAAC,CAAC,UAAU,CAC3C,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,GAAG,CAChB,gBAAgB,CAAE,IAAI,CACtB,KAAK,CAAE,IAAI,CACX,OAAO,CAAE,CAAC,AAAE,CAAC,AAEP,CAAC,AAAE,CAAC,AACV,KAAK,CAAE,IAAI,eAAe,CAAC,CAC3B,eAAe,CAAE,IAAI,AAAE,CAAC,AAElB,OAAO,AAAE,CAAC,AAChB,KAAK,CAAE,IAAI,AAAE,CAAC,AAER,QAAQ,AAAE,CAAC,AACjB,KAAK,CAAE,IAAI,iBAAiB,CAAC,AAAE,CAAC,AAE1B,EAAE,AAAE,CAAC,AACX,UAAU,CAAE,IAAI,AAAE,CAAC,AAEb,GAAG,AAAE,CAAC,AACZ,KAAK,CAAE,IAAI,AAAE,CAAC,AAER,QAAQ,AAAE,CAAC,AACjB,MAAM,CAAE,OAAO,AAAE,CAAC,AAGZ,UAAU,AAAE,CAAC,AACnB,SAAS,CAAE,MAAM,CACjB,MAAM,CAAE,IAAI,CACZ,QAAQ,CAAE,MAAM,CAChB,OAAO,CAAE,CAAC,CAAC,IAAI,AAAE,CAAC,AAEZ,UAAU,AAAE,CAAC,AACnB,SAAS,CAAE,KAAK,CAChB,OAAO,CAAE,CAAC,CAAC,CAAC,AAAE,CAAC,AAGT,QAAQ,AAAE,CAAC,AACjB,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,GAAG,CAChB,aAAa,CAAE,IAAI,AAAE,CAAC,AAEhB,MAAM,AAAE,CAAC,AACf,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,GAAG,CAChB,aAAa,CAAE,IAAI,AAAE,CAAC,AAEhB,KAAK,AAAE,CAAC,AACd,SAAS,CAAE,MAAM,CACjB,aAAa,CAAE,IAAI,AAAE,CAAC,AAEhB,YAAY,AAAE,CAAC,AACrB,UAAU,CAAE,MAAM,AAAE,CAAC,AAEf,aAAa,AAAE,CAAC,AACtB,KAAK,CAAE,IAAI,eAAe,CAAC,AAAE,CAAC,AAExB,UAAU,AAAE,CAAC,AACnB,KAAK,CAAE,IAAI,YAAY,CAAC,AAAE,CAAC,AAErB,aAAa,AAAE,CAAC,AACtB,KAAK,CAAE,IAAI,eAAe,CAAC,AAAE,CAAC,AAExB,YAAY,AAAE,CAAC,AACrB,KAAK,CAAE,IAAI,cAAc,CAAC,AAAE,CAAC,AAEvB,eAAe,AAAE,CAAC,AACxB,KAAK,CAAE,IAAI,iBAAiB,CAAC,AAAE,CAAC,AAE1B,gBAAgB,AAAE,CAAC,AACzB,KAAK,CAAE,IAAI,kBAAkB,CAAC,AAAE,CAAC,AAE3B,YAAY,AAAE,CAAC,AACrB,UAAU,CAAE,MAAM,AAAE,CAAC,AAEf,WAAW,AAAE,CAAC,AACpB,UAAU,CAAE,KAAK,AAAE,CAAC,AAEd,UAAU,AAAE,CAAC,AACnB,UAAU,CAAE,IAAI,AAAE,CAAC,AAEb,eAAe,AAAE,CAAC,AACxB,cAAc,CAAE,SAAS,AAAE,CAAC,AAEtB,eAAe,AAAE,CAAC,AACxB,cAAc,CAAE,SAAS,AAAE,CAAC,AAEtB,gBAAgB,AAAE,CAAC,AACzB,cAAc,CAAE,UAAU,AAAE,CAAC,AAEvB,WAAW,AAAC,CAAU,EAAE,AAAE,CAAC,AACjC,WAAW,CAAE,GAAG,CAChB,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,IAAI,AAAE,CAAC,AAEd,aAAa,AAAC,CAAU,EAAE,AAAE,CAAC,AACnC,WAAW,CAAE,GAAG,CAChB,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,IAAI,AAAE,CAAC,AAEd,WAAW,AAAC,CAAU,EAAE,AAAE,CAAC,AACjC,WAAW,CAAE,GAAG,CAChB,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,IAAI,AAAE,CAAC,AAEd,WAAW,AAAE,CAAC,AACpB,WAAW,CAAE,GAAG,CAChB,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,IAAI,AAAE,CAAC,AAEd,aAAa,AAAE,CAAC,AACtB,WAAW,CAAE,GAAG,CAChB,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,IAAI,AAAE,CAAC,AAGd,WAAW,AAAE,CAAC,AACpB,OAAO,CAAE,IAAI,CACb,cAAc,CAAE,MAAM,CACtB,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CACZ,eAAe,CAAE,MAAM,CACvB,WAAW,CAAE,MAAM,CACnB,UAAU,CAAE,MAAM,AAAE,CAAC,AAGf,KAAK,AAAE,CAAC,AACd,OAAO,CAAE,IAAI,CACb,MAAM,CAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CACvB,MAAM,CAAE,MAAM,CAAC,CAAC,AAAE,CAAC,AAGb,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,MAAM,CAAC,CAAC,AAAE,CAAC,AAEb,KAAK,AAAC,CAAC,AAAQ,EAAE,AAAE,CAAC,AAC1B,cAAc,CAAE,MAAM,AAAE,CAAC,AAGnB,EAAE,AAAE,CAAC,AACX,OAAO,CAAE,MAAM,AAAE,CAAC,AAEZ,IAAI,AAAE,CAAC,AACb,OAAO,CAAE,IAAI,AAAE,CAAC,AAEV,IAAI,AAAE,CAAC,AACb,OAAO,CAAE,IAAI,AAAE,CAAC,AAEV,IAAI,AAAE,CAAC,AACb,OAAO,CAAE,IAAI,AAAE,CAAC,AAEV,GAAG,AAAE,CAAC,AACZ,OAAO,CAAE,MAAM,CAAC,CAAC,AAAE,CAAC,AAEd,KAAK,AAAE,CAAC,AACd,OAAO,CAAE,IAAI,CAAC,CAAC,AAAE,CAAC,AAEZ,KAAK,AAAE,CAAC,AACd,OAAO,CAAE,IAAI,CAAC,CAAC,AAAE,CAAC,AAEZ,KAAK,AAAE,CAAC,AACd,OAAO,CAAE,IAAI,CAAC,CAAC,AAAE,CAAC,AAGZ,EAAE,AAAE,CAAC,AACX,MAAM,CAAE,MAAM,AAAE,CAAC,AAEX,IAAI,AAAE,CAAC,AACb,MAAM,CAAE,IAAI,AAAE,CAAC,AAET,KAAK,AAAE,CAAC,AACd,aAAa,CAAE,KAAK,CAAC,UAAU,AAAE,CAAC,AAE5B,KAAK,AAAE,CAAC,AACd,WAAW,CAAE,KAAK,CAAC,UAAU,AAAE,CAAC,AAE1B,KAAK,AAAE,CAAC,AACd,YAAY,CAAE,KAAK,CAAC,UAAU,AAAE,CAAC,AAE3B,KAAK,AAAE,CAAC,AACd,UAAU,CAAE,KAAK,CAAC,UAAU,AAAE,CAAC,AAEzB,KAAK,AAAE,CAAC,AACd,WAAW,CAAE,IAAI,CAAC,UAAU,CAC5B,YAAY,CAAE,IAAI,CAAC,UAAU,AAAE,CAAC,AAE1B,IAAI,AAAE,CAAC,AACb,MAAM,CAAE,IAAI,AAAE,CAAC,AAET,KAAK,AAAE,CAAC,AACd,aAAa,CAAE,KAAK,CAAC,UAAU,AAAE,CAAC,AAE5B,KAAK,AAAE,CAAC,AACd,WAAW,CAAE,KAAK,CAAC,UAAU,AAAE,CAAC,AAE1B,KAAK,AAAE,CAAC,AACd,YAAY,CAAE,KAAK,CAAC,UAAU,AAAE,CAAC,AAE3B,KAAK,AAAE,CAAC,AACd,UAAU,CAAE,KAAK,CAAC,UAAU,AAAE,CAAC,AAEzB,KAAK,AAAE,CAAC,AACd,WAAW,CAAE,IAAI,CAAC,UAAU,CAC5B,YAAY,CAAE,IAAI,CAAC,UAAU,AAAE,CAAC,AAE1B,IAAI,AAAE,CAAC,AACb,MAAM,CAAE,IAAI,AAAE,CAAC,AAET,GAAG,AAAE,CAAC,AACZ,MAAM,CAAE,MAAM,CAAC,CAAC,AAAE,CAAC,AAEb,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,IAAI,CAAC,CAAC,AAAE,CAAC,AAEX,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,IAAI,CAAC,CAAC,AAAE,CAAC,AAEX,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,IAAI,CAAC,CAAC,AAAE,CAAC,AAEX,KAAK,AAAE,CAAC,AACd,OAAO,CAAE,IAAI,CACb,OAAO,CAAE,WAAW,CACpB,SAAS,CAAE,IAAI,AAES,CAAC,AAGnB,OAAO,AAAE,CAAC,AAChB,OAAO,CAAE,IAAI,CACb,qBAAqB,CAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CACrC,QAAQ,CAAE,IAAI,AAAE,CAAC,AAEX,OAAO,AAAE,CAAC,AAChB,OAAO,CAAE,IAAI,CACb,qBAAqB,CAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CACrC,QAAQ,CAAE,IAAI,AAAE,CAAC,AAEX,OAAO,AAAE,CAAC,AAChB,OAAO,CAAE,IAAI,CACb,qBAAqB,CAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CACrC,QAAQ,CAAE,IAAI,AAAE,CAAC,AAEX,OAAO,AAAE,CAAC,AAChB,OAAO,CAAE,IAAI,CACb,qBAAqB,CAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CACrC,QAAQ,CAAE,IAAI,AAAE,CAAC,AAEX,IAAI,AAAE,CAAC,AACb,WAAW,CAAE,KAAK,CAClB,YAAY,CAAE,KAAK,AAAE,CAAC,AAEhB,WAAW,AAAC,CAAU,UAAU,AAAE,CAAC,AACzC,OAAO,CAAE,GAAG,CACZ,OAAO,CAAE,KAAK,AAAE,CAAC,AAEX,UAAU,AAAE,CAAC,AACnB,KAAK,CAAE,IAAI,AAAE,CAAC,AASR,OAAO,AAAC,CACR,QAAQ,AAAE,CAAC,AACjB,WAAW,CAAE,EAAE,AAAE,CAAC,AAEZ,mBAAmB,AAAC,CACpB,oBAAoB,AAAE,CAAC,AAC7B,WAAW,CAAE,CAAC,AAAE,CAAC,AAEX,MAAM,AAAE,CAAC,AACf,KAAK,CAAE,QAAQ,AAAE,CAAC,AAEZ,MAAM,AAAE,CAAC,AACf,KAAK,CAAE,SAAS,AAAE,CAAC,AAEb,MAAM,AAAE,CAAC,AACf,KAAK,CAAE,GAAG,AAAE,CAAC,AAEP,MAAM,AAAE,CAAC,AACf,KAAK,CAAE,SAAS,AAAE,CAAC,AAEb,MAAM,AAAE,CAAC,AACf,KAAK,CAAE,SAAS,AAAE,CAAC,AAEb,MAAM,AAAE,CAAC,AACf,KAAK,CAAE,GAAG,AAAE,CAAC,AAEP,MAAM,AAAE,CAAC,AACf,KAAK,CAAE,SAAS,AAAE,CAAC,AAEb,MAAM,AAAE,CAAC,AACf,KAAK,CAAE,SAAS,AAAE,CAAC,AAEb,MAAM,AAAE,CAAC,AACf,KAAK,CAAE,GAAG,AAAE,CAAC,AAEP,OAAO,AAAE,CAAC,AAChB,KAAK,CAAE,SAAS,AAAE,CAAC,AAEb,OAAO,AAAE,CAAC,AAChB,KAAK,CAAE,SAAS,AAAE,CAAC,AAEb,OAAO,AAAE,CAAC,AAChB,KAAK,CAAE,IAAI,CACX,WAAW,CAAE,CAAC,AAAE,CAAC,AAEX,UAAU,AAAE,CAAC,AACnB,KAAK,CAAE,UAAU,AAAE,CAAC,AAEd,UAAU,AAAE,CAAC,AACnB,KAAK,CAAE,cAAc,AAAE,CAAC,AAElB,SAAS,AAAE,CAAC,AAClB,KAAK,CAAE,GAAG,AAAE,CAAC,AAGP,aAAa,AAAE,CAAC,AACtB,WAAW,CAAE,cAAc,AAAE,CAAC,AAExB,aAAa,AAAE,CAAC,AACtB,WAAW,CAAE,cAAc,AAAE,CAAC,AAExB,aAAa,AAAE,CAAC,AACtB,WAAW,CAAE,GAAG,AAAE,CAAC,AAEb,aAAa,AAAE,CAAC,AACtB,WAAW,CAAE,cAAc,AAAE,CAAC,AAExB,aAAa,AAAE,CAAC,AACtB,WAAW,CAAE,cAAc,AAAE,CAAC,AAExB,aAAa,AAAE,CAAC,AACtB,WAAW,CAAE,GAAG,AAAE,CAAC,AAEb,aAAa,AAAE,CAAC,AACtB,WAAW,CAAE,cAAc,AAAE,CAAC,AAExB,aAAa,AAAE,CAAC,AACtB,WAAW,CAAE,cAAc,AAAE,CAAC,AAExB,aAAa,AAAE,CAAC,AACtB,WAAW,CAAE,KAAK,AAAE,CAAC,AAEf,cAAc,AAAE,CAAC,AACvB,WAAW,CAAE,cAAc,AAAE,CAAC,AAExB,cAAc,AAAE,CAAC,AACvB,WAAW,CAAE,cAAc,AAAE,CAAC,AAExB,iBAAiB,AAAE,CAAC,AAC1B,WAAW,CAAE,cAAc,AAAE,CAAC,AAExB,iBAAiB,AAAE,CAAC,AAC1B,WAAW,CAAE,cAAc,AAAE,CAAC,AAExB,gBAAgB,AAAE,CAAC,AACzB,WAAW,CAAE,GAAG,AAAE,CAAC,AAEb,IAAI,AAAE,CAAC,AACb,OAAO,CAAE,YAAY,CACrB,UAAU,CAAE,IAAI,aAAa,CAAC,CAC9B,KAAK,CAAE,IAAI,CACX,OAAO,CAAE,MAAM,CAAC,MAAM,CACtB,SAAS,CAAE,IAAI,CACf,MAAM,CAAE,IAAI,CACZ,MAAM,CAAE,OAAO,CACf,YAAY,CAAE,MAAM,CACpB,UAAU,CAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAChC,OAAO,CAAE,IAAI,AAAE,CAAC,AAEV,SAAS,AAAE,CAAC,AAClB,UAAU,CAAE,IAAI,CAChB,OAAO,CAAE,CAAC,CACV,MAAM,CAAE,CAAC,AAAE,CAAC,AAEN,UAAU,AAAE,CAAC,AACnB,OAAO,CAAE,KAAK,CACd,KAAK,CAAE,IAAI,AAAE,CAAC,AAER,OAAO,AAAE,CAAC,AAChB,SAAS,CAAE,MAAM,CACjB,OAAO,CAAE,MAAM,CAAC,IAAI,CACpB,YAAY,CAAE,MAAM,AAAE,CAAC,AAEjB,MAAM,AAAE,CAAC,AACf,OAAO,CAAE,YAAY,CACrB,SAAS,CAAE,MAAM,CACjB,OAAO,CAAE,MAAM,CAAC,MAAM,CACtB,UAAU,CAAE,MAAM,CAClB,MAAM,CAAE,MAAM,CACd,UAAU,CAAE,IAAI,aAAa,CAAC,CAC9B,KAAK,CAAE,IAAI,CACX,aAAa,CAAE,GAAG,AAAE,CAAC,AAEf,MAAM,AAAE,CAAC,AACf,OAAO,CAAE,MAAM,CACf,MAAM,CAAE,IAAI,CAAC,CAAC,CACd,OAAO,CAAE,GAAG,CACZ,UAAU,CAAE,IAAI,aAAa,CAAC,CAC9B,KAAK,CAAE,IAAI,AAAE,CAAC,AAER,YAAY,AAAC,CACb,WAAW,AAAC,CACZ,cAAc,AAAC,CACf,cAAc,AAAE,CAAC,AACvB,UAAU,CAAE,IAAI,eAAe,CAAC,CAChC,KAAK,CAAE,IAAI,AAAE,CAAC,AAER,UAAU,AAAC,CACX,SAAS,AAAC,CACV,YAAY,AAAC,CACb,YAAY,AAAE,CAAC,AACrB,UAAU,CAAE,IAAI,aAAa,CAAC,CAC9B,KAAK,CAAE,IAAI,AAAE,CAAC,AAER,SAAS,AAAC,CACV,QAAQ,AAAC,CACT,WAAW,AAAC,CACZ,WAAW,AAAE,CAAC,AACpB,UAAU,CAAE,IAAI,YAAY,CAAC,CAC7B,KAAK,CAAE,IAAI,AAAE,CAAC,AAER,WAAW,AAAC,CACZ,UAAU,AAAC,CACX,aAAa,AAAC,CACd,aAAa,AAAE,CAAC,AACtB,UAAU,CAAE,IAAI,cAAc,CAAC,CAC/B,KAAK,CAAE,IAAI,AAAE,CAAC,AAER,YAAY,AAAC,CACb,WAAW,AAAC,CACZ,cAAc,AAAC,CACf,cAAc,AAAE,CAAC,AACvB,UAAU,CAAE,IAAI,eAAe,CAAC,CAChC,KAAK,CAAE,IAAI,AAAE,CAAC,AAER,UAAU,AAAC,CACX,SAAS,AAAC,CACV,YAAY,AAAC,CACb,YAAY,AAAE,CAAC,AACrB,UAAU,CAAE,IAAI,CAChB,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CAAC,KAAK,CAAC,GAAG,AAAE,CAAC,AAEnB,aAAa,AAAE,CAAC,AACtB,MAAM,CAAE,WAAW,CACnB,cAAc,CAAE,IAAI,CACpB,OAAO,CAAE,IAAI,CACb,UAAU,CAAE,IAAI,AAAE,CAAC,AAEb,kBAAkB,AAAE,CAAC,AAC3B,OAAO,CAAE,GAAG,AAAE,CAAC,AAET,SAAS,AAAC,CACV,YAAY,AAAE,CAAC,AACrB,MAAM,CAAE,IAAI,CAAC,KAAK,CAAC,GAAG,AAAE,CAAC,AAEnB,UAAU,AAAE,CAAC,AACnB,aAAa,CAAE,GAAG,AAAE,CAAC,AAGf,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,KAAK,CAAC,CAAC,AAAE,CAAC,AAEZ,UAAU,AAAE,CAAC,AACnB,OAAO,CAAE,KAAK,CACd,UAAU,CAAE,MAAM,CAClB,KAAK,CAAE,IAAI,AAAE,CAAC,AAER,kBAAkB,AAAC,CACnB,mBAAmB,AAAC,CACpB,sBAAsB,AAAC,CACvB,kBAAkB,AAAC,CACnB,MAAM,AAAC,CACP,QAAQ,AAAE,CAAC,AACjB,OAAO,CAAE,KAAK,CACd,KAAK,CAAE,IAAI,CACX,OAAO,CAAE,MAAM,CAEf,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,AAAE,CAAC,AAEnB,oBAAoB,AAAC,CACrB,MAAM,AAAE,CAAC,AACf,IAAI,CAAE,OAAO,AAAE,CAAC,AAEV,KAAK,AAAC,CACN,MAAM,AAAE,CAAC,AACf,OAAO,CAAE,KAAK,CACd,aAAa,CAAE,KAAK,CACpB,WAAW,CAAE,GAAG,AAAE,CAAC,AAEb,sBAAsB,AAAC,CACvB,mBAAmB,AAAE,CAAC,AAC5B,OAAO,CAAE,MAAM,AAAE,CAAC,AAEZ,KAAK,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,WAAW,AAAE,CAAC,AAC9C,OAAO,CAAE,YAAY,CACrB,WAAW,CAAE,KAAK,CAClB,WAAW,CAAE,MAAM,CACnB,gBAAgB,CAAE,OAAO,AAAE,CAAC,AAEtB,KAAK,AAAC,CAAC,AAAQ,EAAE,AAAC,CAClB,KAAK,AAAC,CAAC,AAAQ,EAAE,AAAE,CAAC,AAC1B,OAAO,CAAE,IAAI,CACb,UAAU,CAAE,IAAI,AAAE,CAAC,AAEb,KAAK,AAAC,CAAC,AAAQ,EAAE,AAAE,CAAC,AAC1B,UAAU,CAAE,IAAI,aAAa,CAAC,AAAE,CAAC,AAG3B,OAAO,AAAE,CAAC,AAChB,QAAQ,CAAE,KAAK,CACf,OAAO,CAAE,IAAI,CACb,eAAe,CAAE,aAAa,CAC9B,WAAW,CAAE,MAAM,CACnB,OAAO,CAAE,MAAM,CAAC,IAAI,CACpB,OAAO,CAAE,CAAC,CACV,KAAK,CAAE,IAAI,CACX,OAAO,CAAE,GAAG,CACZ,aAAa,CAAE,IAAI,CACnB,UAAU,CAAE,IAAI,eAAe,CAAC,AAAE,CAAC,AAE7B,OAAO,AAAC,CAAC,AAAQ,EAAE,AAAE,CAAC,AAC5B,OAAO,CAAE,IAAI,AAAE,CAAC,AAEV,OAAO,AAAC,CAAC,AAAQ,CAAC,AAAE,CAAC,AAC3B,KAAK,CAAE,IAAI,CACX,OAAO,CAAE,OAAO,CAChB,MAAM,CAAE,CAAC,CAAC,OAAO,AAAE,CAAC,AAEd,OAAO,AAAC,CAAC,AAAQ,OAAO,AAAE,CAAC,AACjC,KAAK,CAAE,IAAI,aAAa,CAAC,AAAE,CAAC,AAEtB,OAAO,AAAC,CAAC,AAAQ,QAAQ,AAAC,CAAC,AAAQ,IAAI,AAAE,CAAC,AAChD,YAAY,CAAE,MAAM,AAAE,CAAC,AAEjB,OAAO,AAAC,CAAC,AAAQ,eAAe,AAAE,CAAC,AACzC,WAAW,CAAE,MAAM,CACnB,OAAO,CAAE,IAAI,CACb,OAAO,CAAE,WAAW,CACpB,IAAI,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CACX,cAAc,CAAE,MAAM,AAAE,CAAC,AAEnB,OAAO,AAAC,CAAC,AAAQ,4CAA4C,AAAE,CAAC,AACtE,eAAe,CAAE,QAAQ,AAAE,CAAC,AAEtB,OAAO,AAAC,CAAC,AAAQ,aAAa,AAAE,CAAC,AACvC,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,IAAI,AAAE,CAAC,AAGtB,MAAM,AAAC,YAAY,KAAK,CAAC,AAAC,CAAC,AACjB,QAAQ,AAAE,CAAC,AACjB,OAAO,CAAE,IAAI,AAAE,CAAC,AACV,OAAO,AAAC,CACR,OAAO,AAAC,CACR,OAAO,AAAE,CAAC,AAChB,qBAAqB,CAAE,GAAG,AAAE,CAAC,AAEvB,QAAQ,AAAE,CAAC,AACjB,SAAS,CAAE,IAAI,AAAE,CAAC,AACZ,MAAM,AAAE,CAAC,AACf,SAAS,CAAE,IAAI,AAAE,CAAC,AACZ,KAAK,AAAE,CAAC,AACd,SAAS,CAAE,IAAI,AAAE,CAAC,AAEZ,OAAO,AAAE,CAAC,AAChB,OAAO,CAAE,IAAI,CACb,UAAU,CAAE,MAAM,AAAE,CAAC,AACf,OAAO,AAAC,CAAC,AAAQ,EAAE,AAAE,CAAC,AAC5B,UAAU,CAAE,MAAM,CAClB,eAAe,CAAE,MAAM,AAAE,CAAC,AAAC,CAAC,AAUxB,iBAAiB,AAAE,CAAC,AAC1B,OAAO,CAAE,KAAK,CACd,UAAU,CAAE,IAAI,CAChB,KAAK,CAAE,IAAI,AAAE,CAAC,AAER,OAAO,AAAE,CAAC,AAChB,aAAa,CAAE,GAAG,CAClB,gBAAgB,CAAE,IAAI,CACtB,UAAU,CAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAG1G,SAAS,CAAE,CAAC,AAEa,CAAC,AAEpB,KAAK,AAAE,CAAC,AACd,SAAS,CAAE,IAAI,CACf,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,CAAC,CACT,aAAa,CAAE,IAAI,CACnB,eAAe,CAAE,QAAQ,AAAE,CAAC,AAEtB,EAAE,AAAE,CAAC,AACX,UAAU,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,AAAE,CAAC,AAEvB,KAAK,AAAC,CAAC,AAAQ,mBAAmB,AAAE,CAAC,AAC3C,gBAAgB,CAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,AAAE,CAAC,AAElC,KAAK,AAAC,CAAC,AAAQ,EAAE,AAAE,CAAC,AAC1B,UAAU,CAAE,GAAG,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE1B,EAAE,AAAE,CAAC,AACX,UAAU,CAAE,MAAM,CAClB,aAAa,CAAE,IAAI,CACnB,YAAY,CAAE,CAAC,CACf,UAAU,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,AAAE,CAAC,AAEpC,YAAY,AAAE,CAAC,AACrB,QAAQ,CAAE,KAAK,CACf,GAAG,CAAE,CAAC,CACN,KAAK,CAAE,CAAC,CACR,MAAM,CAAE,CAAC,CACT,IAAI,CAAE,CAAC,CACP,UAAU,CAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAC9B,OAAO,CAAE,KAAK,CACd,OAAO,CAAE,CAAC,CACV,cAAc,CAAE,IAAI,CACpB,UAAU,CAAE,MAAM,AAAE,CAAC,AAEf,mBAAmB,AAAE,CAAC,AAC5B,OAAO,CAAE,CAAC,CACV,cAAc,CAAE,IAAI,AAAE,CAAC,AAEjB,YAAY,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,GAAG,AAAE,CAAC,AAC7C,KAAK,CAAE,KAAK,CACZ,QAAQ,CAAE,QAAQ,CAClB,MAAM,CAAE,GAAG,CAAC,IAAI,CAChB,UAAU,CAAE,IAAI,AAAE,CAAC,AAEb,MAAM,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,aAAa,AAAE,CAAC,AACjD,QAAQ,CAAE,QAAQ,CAClB,GAAG,CAAE,IAAI,eAAe,CAAC,CACzB,UAAU,CAAE,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,CAC9C,UAAU,CAAE,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,AAAE,CAAC,AAK3C,aAAa,AAAE,CAAC,AACtB,OAAO,CAAE,YAAY,CACrB,QAAQ,CAAE,QAAQ,AAAE,CAAC,AAEf,4BAA4B,AAAE,CAAC,AACrC,OAAO,CAAE,CAAC,AAAE,CAAC,AAEP,mBAAmB,AAAE,CAAC,AAC5B,QAAQ,CAAE,QAAQ,CAClB,OAAO,CAAE,IAAI,CACb,SAAS,CAAE,KAAK,CAChB,OAAO,CAAE,GAAG,CAAC,GAAG,CAChB,MAAM,CAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CACf,UAAU,CAAE,IAAI,CAChB,SAAS,CAAE,IAAI,CACf,UAAU,CAAE,IAAI,CAChB,gBAAgB,CAAE,IAAI,CACtB,aAAa,CAAE,GAAG,CAClB,OAAO,CAAE,CAAC,CACV,eAAe,CAAE,WAAW,CAC5B,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,CACpC,UAAU,CAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,AAAE,CAAC,AAEpH,OAAO,GAAG,CAAC,GAAG,CAAC,oBAAoB,IAAI,CAAC,EAAE,oBAAoB,MAAM,CAAC,AAAC,CAAC,AAC7D,mBAAmB,AAAE,CAAC,AAC5B,UAAU,CAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CACzC,WAAW,CAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,AAAE,CAAC,AAAC,CAAC,AAEnD,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,AAAC,CAAC,AACvB,mBAAmB,AAAE,CAAC,AAC5B,UAAU,CAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CACzC,WAAW,CAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,AAAE,CAAC,AAAC,CAAC,AAE3C,gCAAgC,AAAE,CAAC,AACzC,OAAO,CAAE,KAAK,AAAE,CAAC,AAEX,mBAAmB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,EAAE,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,CAAC,AAAE,CAAC,AACzE,OAAO,CAAE,KAAK,CACd,OAAO,CAAE,GAAG,CAAC,IAAI,CACjB,KAAK,CAAE,IAAI,CACX,WAAW,CAAE,MAAM,CACnB,WAAW,CAAE,KAAK,CAClB,KAAK,CAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAC1B,eAAe,CAAE,IAAI,CACrB,WAAW,CAAE,MAAM,AAAE,CAAC,AAEhB,mBAAmB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,EAAE,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,OAAO,AAAC,CAAU,mBAAmB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,EAAE,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,OAAO,AAAE,CAAC,AAChK,eAAe,CAAE,IAAI,CACrB,KAAK,CAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAC1B,gBAAgB,CAAE,OAAO,AAAE,CAAC,AAEtB,mBAAmB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,iBAAiB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAU,mBAAmB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,iBAAiB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,OAAO,AAAC,CAAU,mBAAmB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,iBAAiB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,OAAO,AAAE,CAAC,AACxR,KAAK,CAAE,OAAO,AAAE,CAAC,AAEX,mBAAmB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,iBAAiB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,OAAO,AAAC,CAAU,mBAAmB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,iBAAiB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,OAAO,AAAE,CAAC,AAC9L,eAAe,CAAE,IAAI,CACrB,gBAAgB,CAAE,WAAW,CAC7B,gBAAgB,CAAE,IAAI,CACtB,MAAM,CAAE,WAAW,AAAE,CAAC,AAEhB,0BAA0B,AAAE,CAAC,AACnC,IAAI,CAAE,IAAI,CACV,KAAK,CAAE,CAAC,AAAE,CAAC,AAEL,iBAAiB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,mBAAmB,AAAE,CAAC,AAClE,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,GAAG,AAAE,CAAC,AAEZ,oBAAoB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,mBAAmB,AAAE,CAAC,AACrE,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,AAAE,CAAC,AAEd,mBAAmB,AAAC,CAAC,AAAQ,CAAC,AAAC,CAAC,AAAQ,mBAAmB,AAAE,CAAC,AACpE,MAAM,CAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,AAAE,CAAC,AAEd,MAAM,AAAE,CAAC,AACf,MAAM,CAAE,KAAK,AAAE,CAAC,AAEV,WAAW,AAAE,CAAC,AACpB,MAAM,CAAE,UAAU,AAAE,CAAC,AAEf,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,IAAI,AAAE,CAAC,AAET,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,IAAI,AAAE,CAAC,AAET,aAAa,AAAE,CAAC,AACtB,MAAM,CAAE,YAAY,AAAE,CAAC,AAEjB,WAAW,AAAE,CAAC,AACpB,MAAM,CAAE,UAAU,AAAE,CAAC,AAEf,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,IAAI,AAAE,CAAC,AAET,UAAU,AAAE,CAAC,AACnB,MAAM,CAAE,SAAS,AAAE,CAAC,AAEd,QAAQ,AAAE,CAAC,AACjB,MAAM,CAAE,OAAO,AAAE,CAAC,AAEZ,SAAS,AAAE,CAAC,AAClB,MAAM,CAAE,QAAQ,AAAE,CAAC,AAEb,UAAU,AAAE,CAAC,AACnB,MAAM,CAAE,SAAS,AAAE,CAAC,AAEd,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,IAAI,AAAE,CAAC,AAET,SAAS,AAAE,CAAC,AAClB,MAAM,CAAE,QAAQ,AAAE,CAAC,AAEb,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,IAAI,AAAE,CAAC,AAET,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,IAAI,AAAE,CAAC,AAET,SAAS,AAAE,CAAC,AAClB,MAAM,CAAE,QAAQ,AAAE,CAAC,AAEb,UAAU,AAAE,CAAC,AACnB,MAAM,CAAE,SAAS,AAAE,CAAC,AAEd,YAAY,AAAE,CAAC,AACrB,MAAM,CAAE,WAAW,AAAE,CAAC,AAEhB,UAAU,AAAE,CAAC,AACnB,MAAM,CAAE,SAAS,AAAE,CAAC,AAEd,UAAU,AAAE,CAAC,AACnB,MAAM,CAAE,SAAS,AAAE,CAAC,AAEd,YAAY,AAAE,CAAC,AACrB,MAAM,CAAE,WAAW,AAAE,CAAC,AAEhB,QAAQ,AAAE,CAAC,AACjB,MAAM,CAAE,OAAO,AAAE,CAAC,AAEZ,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,IAAI,AAAE,CAAC,AAET,YAAY,AAAE,CAAC,AACrB,MAAM,CAAE,WAAW,AAAE,CAAC,AAEhB,QAAQ,AAAE,CAAC,AACjB,MAAM,CAAE,OAAO,AAAE,CAAC,AAEZ,SAAS,AAAE,CAAC,AAClB,MAAM,CAAE,QAAQ,AAAE,CAAC,AAEb,WAAW,AAAE,CAAC,AACpB,MAAM,CAAE,UAAU,AAAE,CAAC,AAEf,SAAS,AAAE,CAAC,AAClB,MAAM,CAAE,QAAQ,AAAE,CAAC,AAEb,UAAU,AAAE,CAAC,AACnB,MAAM,CAAE,SAAS,AAAE,CAAC,AAEd,UAAU,AAAE,CAAC,AACnB,MAAM,CAAE,SAAS,AAAE,CAAC,AAEd,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,IAAI,AAAE,CAAC,AAET,IAAI,AAAE,CAAC,AACb,MAAM,CAAE,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,AAAE,CAAC,AAE1B,SAAS,AAAE,CAAC,AAClB,MAAM,CAAE,QAAQ,AAAE,CAAC,AAEb,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,IAAI,AAAE,CAAC,AAET,QAAQ,AAAE,CAAC,AACjB,MAAM,CAAE,OAAO,AAAE,CAAC,AAEZ,SAAS,AAAE,CAAC,AAClB,MAAM,CAAE,QAAQ,AAAE,CAAC,AAUb,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,OAAO,AAAE,CAAC,AAChB,WAAW,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,AAAE,CAAC,AAE5B,KAAK,AAAE,CAAC,AACd,MAAM,CAAE,KAAK,AAAE,CAAC;ACn4BlB,OAAO,cAAC,CAAC,AAEL,KAAK,CAAE,IAAI,CACX,UAAU,CAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAE7B,MAAM,CAAE,IAAI,CACZ,UAAU,CAAE,IAAI,CAChB,UAAU,CAAE,MAAM,AAEtB,CAAC,AAED,WAAW,cAAC,CAAC,AACT,UAAU,CAAE,UAAU,CACtB,YAAY,CAAE,IAAI,CAClB,WAAW,CAAE,IAAI,CACjB,YAAY,CAAE,IAAI,CAClB,aAAa,CAAE,IAAI,AACvB,CAAC,AAED,IAAI,cAAC,CAAC,AACF,MAAM,CAAE,EAAE,CAAC,CAAC,CACZ,QAAQ,CAAE,IAAI,CACd,OAAO,CAAE,IAAI,AACjB,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,UAAU,CAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,AAC3C,CAAC;AChDD,OAAO,eAAC,CAAC,AACL,MAAM,CAAE,IAAI,CACZ,UAAU,CAAE,IAAI,CAChB,UAAU,CAAE,MAAM,AACtB,CAAC,AAED,KAAK,eAAC,CAAC,AACH,OAAO,CAAE,IAAI,AACjB,CAAC,AAED,QAAQ,eAAC,CAAC,AACN,WAAW,CAAE,MAAM,CACnB,QAAQ,CAAE,MAAM,CAChB,aAAa,CAAE,QAAQ,AAC3B,CAAC,AAED,QAAQ,eAAC,CAAC,AACN,WAAW,CAAE,IAAI,CACjB,KAAK,CAAE,aAAa,AACxB,CAAC,AAED,KAAK,eAAC,CAAC,AACH,WAAW,CAAE,MAAM,CAAC,UAAU,CAC9B,KAAK,CAAE,OAAO,AAClB,CAAC,AAED,QAAQ,eAAC,CAAC,AACN,UAAU,CAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,AAC3C,CAAC,AAED,uBAAQ,MAAM,AAAC,CAAC,AACZ,gBAAgB,CAAE,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,AAE/C,CAAC,AAED,MAAM,eAAC,CAAC,AACJ,SAAS,CAAE,GAAG,AAClB,CAAC,AAED,WAAW,eAAC,CAAC,AACT,WAAW,CAAE,MAAM,CACnB,QAAQ,CAAE,MAAM,CAChB,aAAa,CAAE,QAAQ,AAC3B,CAAC;AClED,KAAK,cAAC,CAAC,AAEH,YAAY,CAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAC/B,gBAAgB,CAAE,OAAO,CACzB,MAAM,CAAE,KAAK,AACjB,CAAC" +} \ No newline at end of file diff --git a/dist/build/bundle.js b/dist/build/bundle.js new file mode 100644 index 0000000..65b4cfe --- /dev/null +++ b/dist/build/bundle.js @@ -0,0 +1 @@ +var app=function(){"use strict";function t(){}function e(t){return t()}function n(){return Object.create(null)}function o(t){t.forEach(e)}function r(t){return"function"==typeof t}function s(t,e){return t!=t?e==e:t!==e||t&&"object"==typeof t||"function"==typeof t}function a(e,n,o){e.$$.on_destroy.push(function(e,...n){if(null==e)return t;const o=e.subscribe(...n);return o.unsubscribe?()=>o.unsubscribe():o}(n,o))}function c(t,e){t.appendChild(e)}function l(t,e,n){t.insertBefore(e,n||null)}function i(t){t.parentNode.removeChild(t)}function u(t){return document.createElement(t)}function d(t){return document.createTextNode(t)}function f(){return d(" ")}function p(t,e,n,o){return t.addEventListener(e,n,o),()=>t.removeEventListener(e,n,o)}function h(t,e,n){null==n?t.removeAttribute(e):t.getAttribute(e)!==n&&t.setAttribute(e,n)}function m(t,e){e=""+e,t.data!==e&&(t.data=e)}let g;function b(t){g=t}const y=[],v=[],$=[],w=[],x=Promise.resolve();let k=!1;function J(t){$.push(t)}let _=!1;const A=new Set;function C(){if(!_){_=!0;do{for(let t=0;t{L.delete(t),o&&(n&&t.d(1),o())}),t.o(e)}}function N(t){t&&t.c()}function O(t,n,s){const{fragment:a,on_mount:c,on_destroy:l,after_update:i}=t.$$;a&&a.m(n,s),J(()=>{const n=c.map(e).filter(r);l?l.push(...n):o(n),t.$$.on_mount=[]}),i.forEach(J)}function T(t,e){const n=t.$$;null!==n.fragment&&(o(n.on_destroy),n.fragment&&n.fragment.d(e),n.on_destroy=n.fragment=null,n.ctx=[])}function R(t,e){-1===t.$$.dirty[0]&&(y.push(t),k||(k=!0,x.then(C)),t.$$.dirty.fill(0)),t.$$.dirty[e/31|0]|=1<{const r=o.length?o[0]:n;return p.ctx&&c(p.ctx[t],p.ctx[t]=r)&&(p.bound[t]&&p.bound[t](r),h&&R(e,t)),n}):[],p.update(),h=!0,o(p.before_update),p.fragment=!!a&&a(p.ctx),r.target){if(r.hydrate){const t=function(t){return Array.from(t.childNodes)}(r.target);p.fragment&&p.fragment.l(t),t.forEach(i)}else p.fragment&&p.fragment.c();r.intro&&S(e.$$.fragment),O(e,r.target,r.anchor),C()}b(d)}class D{$destroy(){T(this,1),this.$destroy=t}$on(t,e){const n=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return n.push(e),()=>{const t=n.indexOf(e);-1!==t&&n.splice(t,1)}}$set(){}}const P=[];function q(e,n=t){let o;const r=[];function a(t){if(s(e,t)&&(e=t,o)){const t=!P.length;for(let t=0;t{const t=r.indexOf(l);-1!==t&&r.splice(t,1),0===r.length&&(o(),o=null)}}}}var H=function t(e){function n(t){return function(e,n){return s(e,Object.assign({method:t},n))}}function o(t){return function(e,n,o){return s(e,Object.assign({method:t,data:n},o))}}function r(t,e,n){if(Array.isArray(t))return t.concat(e);if(e&&"object"==typeof e){var o,s={};if(t)for(o in t)s[n?o.toLowerCase():o]=t[o];for(o in e){var a=n?o.toLowerCase():o;"headers"===a&&(n=!0),s[a]=o in s?r(s[a],e[o],n):e[o]}return s}return e}function s(t,n){"string"!=typeof t&&(t=(n=t).url);var o=r(e,n||{}),s=o.data;if(o.transformRequest)for(var a=0;a{const e=JSON.parse(t.data);U.set(e)}).catch(t=>{console.error("getJobList",t)})},loadJobDetail(t){if(console.log("loadJobDetail: "+t),t){const e=`${I.jobs}/${t}`;H.get(e).then(t=>{const e=JSON.parse(t.data);V.set(e)}).catch(t=>{console.error("loadJobDetail",t)})}},async touchJob(t){if(console.log("touchJob: "+t),t){const e=`${I.jobs}/${t}`;await H.put(e).then(t=>{200!==t.status&&console.error(t)}).catch(t=>{console.error("touchJob",t)}),U.update(e=>(e.map(e=>{if(e._id===t)return e.read=(new Date).getTime(),e}),e))}},async appliedJob(t){console.log("touchJob: "+t);const e=(new Date).getTime();if(t){console.log("Applying..");const n=`${I.apply}/${t}`;await H.put(n).then(t=>{200!==t.status&&console.error(t)}).catch(t=>{console.error("appliedJob",t)});const o=parseInt(t,10);U.update(t=>(t.map(t=>{if(t._id===o)return t.a=e,t}),t)),V.update(t=>(t.a=e,t))}},async markAllRead(){console.log(">> markAllRead");const t=""+I.readall;await H.put(t).then(t=>{200!==t.status&&console.error(t)}).catch(t=>{console.error("markAllRead",t)}),this.getJobList()},killWord(t){console.log(">> killword:"+t);const e=`${I.kill}/${t}`;H.put(e).then(t=>{200!==t.status&&console.error(t)}).catch(t=>{console.error("killword",t)})},upVote(t){console.log(">> upvote:"+t);const e=`${I.upvote}/${t}`;H.put(e).then(t=>{200!==t.status&&console.error(t)}).catch(t=>{console.error("upvote",t)})},downVote(t){console.log(">> downvote:"+t);const e=`${I.downvote}/${t}`;H.put(e).then(t=>{200!==t.status&&console.error(t)}).catch(t=>{console.error("downvote",t)})}};function B(t,e,n){const o=t.slice();return o[4]=e[n],o[6]=n,o}function G(t){let e,n,o,r,s,a,g,b,y,v,$,w,x,k,J,_,A=t[4].title+"",C=t[4].site+"",j=Q(t[4].company)+"",L=t[4].postdate+"";return{c(){e=u("tr"),n=u("td"),o=d(A),r=f(),s=u("td"),a=d(C),g=f(),b=u("td"),y=d(j),v=f(),$=u("td"),w=d(L),x=f(),h(n,"class","noOverflow svelte-15500pg"),h(e,"class",k="dataRow "+X(t[4])+" "+Y(t[4])+" svelte-15500pg"),h(e,"data-id",J=t[4]._id)},m(i,u,d){var f;l(i,e,u),c(e,n),c(n,o),c(e,r),c(e,s),c(s,a),c(e,g),c(e,b),c(b,y),c(e,v),c(e,$),c($,w),c(e,x),d&&_(),_=p(e,"click",(f=t[1],function(t){return t.stopPropagation(),f.call(this,t)}))},p(t,n){1&n&&A!==(A=t[4].title+"")&&m(o,A),1&n&&C!==(C=t[4].site+"")&&m(a,C),1&n&&j!==(j=Q(t[4].company)+"")&&m(y,j),1&n&&L!==(L=t[4].postdate+"")&&m(w,L),1&n&&k!==(k="dataRow "+X(t[4])+" "+Y(t[4])+" svelte-15500pg")&&h(e,"class",k),1&n&&J!==(J=t[4]._id)&&h(e,"data-id",J)},d(t){t&&i(e),_()}}}function K(e){let n,o,r,s,a,d=e[0],p=[];for(let t=0;t\n Title\n \n \n Site\n \n \n Company\n \n \n Date\n ',s=f(),a=u("tbody");for(let t=0;tn(0,o=t)),[o,function(t){console.log("clickevent",t);const e=t.target.parentElement.dataset.id;W.loadJobDetail(e),W.touchJob(e)}]}class tt extends D{constructor(t){super(),E(this,t,Z,K,s,{})}}function et(t){let e,n,o,r,s,a,p,g,b,y,v,$,w,x,k,J,_,A,C,j,L,S,M,N,O,T,R,E,D,P,q,H,z,F,I,U,V,W,B,G,K=t[1].title+"",Q=t[1].company+"",X=t[1].location+"",Y=t[1].salary+"",Z=t[1].postdate+"",tt=t[1].site+"",et=t[1].summary+"";let rt=void 0,st=rt&&rt(t);function at(t,e){return t[1].applied?nt:ot}let ct=at(t),lt=ct(t);return{c(){e=u("div"),n=u("div"),o=u("div"),r=u("div"),s=d(K),a=f(),st&&st.c(),p=f(),g=u("div"),b=u("div"),y=d("Company: "),v=u("span"),$=d(Q),w=f(),x=u("div"),k=d("Location: "),J=u("span"),_=d(X),A=f(),C=u("div"),j=d("Applied:\n "),lt.c(),L=f(),S=u("div"),M=u("div"),N=d("Salary: "),O=u("span"),T=d(Y),R=f(),E=u("div"),D=d("Scraped: "),P=u("span"),q=d(Z),H=f(),z=u("div"),F=d("Site: "),I=u("a"),U=d(tt),B=f(),G=u("blockquote"),h(r,"class","col-12 text-primary lead"),h(o,"class","row svelte-r7f80i"),h(v,"class","text-primary"),h(b,"class","col-1-3rd"),h(J,"class","text-primary"),h(x,"class","col-1-3rd"),h(C,"class","col-1-3rd"),h(g,"class","row svelte-r7f80i"),h(O,"class","text-primary"),h(M,"class","col-1-3rd"),h(P,"class","text-primary"),h(E,"class","col-1-3rd"),h(I,"href",V=t[1].url),h(I,"class","text-capitalize"),h(I,"target","_blank"),h(z,"class","col-1-3rd"),h(S,"class","row svelte-r7f80i"),h(n,"class",W="detailHead bg-light svelte-r7f80i"),h(e,"class","detail svelte-r7f80i")},m(t,i){l(t,e,i),c(e,n),c(n,o),c(o,r),c(r,s),c(o,a),st&&st.m(o,null),c(n,p),c(n,g),c(g,b),c(b,y),c(b,v),c(v,$),c(g,w),c(g,x),c(x,k),c(x,J),c(J,_),c(g,A),c(g,C),c(C,j),lt.m(C,null),c(n,L),c(n,S),c(S,M),c(M,N),c(M,O),c(O,T),c(S,R),c(S,E),c(E,D),c(E,P),c(P,q),c(S,H),c(S,z),c(z,F),c(z,I),c(I,U),c(e,B),c(e,G),G.innerHTML=et},p(t,e){2&e&&K!==(K=t[1].title+"")&&m(s,K),2&e&&Q!==(Q=t[1].company+"")&&m($,Q),2&e&&X!==(X=t[1].location+"")&&m(_,X),ct===(ct=at(t))&<?lt.p(t,e):(lt.d(1),lt=ct(t),lt&&(lt.c(),lt.m(C,null))),2&e&&Y!==(Y=t[1].salary+"")&&m(T,Y),2&e&&Z!==(Z=t[1].postdate+"")&&m(q,Z),2&e&&tt!==(tt=t[1].site+"")&&m(U,tt),2&e&&V!==(V=t[1].url)&&h(I,"href",V),2&e&&et!==(et=t[1].summary+"")&&(G.innerHTML=et)},d(t){t&&i(e),st&&st.d(),lt.d()}}}function nt(e){let n;return{c(){n=d("Applied!")},m(t,e){l(t,n,e)},p:t,d(t){t&&i(n)}}}function ot(e){let n,o;return{c(){n=u("button"),n.textContent="Applied",h(n,"class","btn btn-sm btn-primary")},m(t,r,s){l(t,n,r),s&&o(),o=p(n,"click",e[2])},p:t,d(t){t&&i(n),o()}}}function rt(e){let n,o=e[0]&&et(e);return{c(){o&&o.c(),n=d("")},m(t,e){o&&o.m(t,e),l(t,n,e)},p(t,[e]){t[0]?o?o.p(t,e):(o=et(t),o.c(),o.m(n.parentNode,n)):o&&(o.d(1),o=null)},i:t,o:t,d(t){o&&o.d(t),t&&i(n)}}}function st(t){for(const e in t)if(t.hasOwnProperty(e))return!1;return!0}function at(t,e,n){let o;a(t,V,t=>n(1,o=t));let r=!1;return V.subscribe(t=>{n(0,r=!st(t)),console.log("JobDetails.subscribe",t)}),t.$$.update=()=>{2&t.$$.dirty&&st(o)},[r,o,function(){W.appliedJob(o._id)}]}class ct extends D{constructor(t){super(),E(this,t,at,rt,s,{})}}function lt(e){let n,o,r,s,a,d,m;return{c(){n=u("header"),o=u("section"),r=u("span"),r.textContent=""+it,s=f(),a=u("section"),d=u("button"),d.textContent="Mark All Read",h(r,"class","text-bold navbar-brand mx-1 text-uppercase"),h(o,"class","navbar-section"),h(d,"class","btn btn-sm btn-primary"),h(a,"class","navbar-section text-right"),h(n,"class","navbar bg-primary")},m(t,i,u){l(t,n,i),c(n,o),c(o,r),c(n,s),c(n,a),c(a,d),u&&m(),m=p(d,"click",e[0])},p:t,i:t,o:t,d(t){t&&i(n),m()}}}let it="Jobs";function ut(t){return[function(){W.markAllRead()}]}class dt extends D{constructor(t){super(),E(this,t,ut,lt,s,{})}}function ft(e){let n,o,r,s,a;const d=new dt({}),p=new tt({}),m=new ct({});return{c(){N(d.$$.fragment),n=f(),o=u("div"),r=u("div"),N(p.$$.fragment),s=f(),N(m.$$.fragment),h(r,"class","col-12"),h(o,"class","container fullWidth")},m(t,e){O(d,t,e),l(t,n,e),l(t,o,e),c(o,r),O(p,r,null),c(r,s),O(m,r,null),a=!0},p:t,i(t){a||(S(d.$$.fragment,t),S(p.$$.fragment,t),S(m.$$.fragment,t),a=!0)},o(t){M(d.$$.fragment,t),M(p.$$.fragment,t),M(m.$$.fragment,t),a=!1},d(t){T(d,t),t&&i(n),t&&i(o),T(p),T(m)}}}var pt=1e3,ht=6e4,mt=60*ht,gt=24*mt,bt=function(t,e){e=e||{};var n=typeof t;if("string"===n&&t.length>0)return function(t){if((t=String(t)).length>100)return;var e=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(t);if(!e)return;var n=parseFloat(e[1]);switch((e[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return 315576e5*n;case"weeks":case"week":case"w":return 6048e5*n;case"days":case"day":case"d":return n*gt;case"hours":case"hour":case"hrs":case"hr":case"h":return n*mt;case"minutes":case"minute":case"mins":case"min":case"m":return n*ht;case"seconds":case"second":case"secs":case"sec":case"s":return n*pt;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return n;default:return}}(t);if("number"===n&&isFinite(t))return e.long?function(t){var e=Math.abs(t);if(e>=gt)return yt(t,e,gt,"day");if(e>=mt)return yt(t,e,mt,"hour");if(e>=ht)return yt(t,e,ht,"minute");if(e>=pt)return yt(t,e,pt,"second");return t+" ms"}(t):function(t){var e=Math.abs(t);if(e>=gt)return Math.round(t/gt)+"d";if(e>=mt)return Math.round(t/mt)+"h";if(e>=ht)return Math.round(t/ht)+"m";if(e>=pt)return Math.round(t/pt)+"s";return t+"ms"}(t);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(t))};function yt(t,e,n,o){var r=e>=1.5*n;return Math.round(t/n)+" "+o+(r?"s":"")}let vt=0;const $t=new class extends D{constructor(t){super(),E(this,t,null,ft,s,{})}}({target:document.body});return function t(){W.getJobList();const e=bt("15m");console.log("Next:",e),vt=setTimeout(()=>{t()},e)}(),$t}(); diff --git a/dist/favicon.png b/dist/favicon.png new file mode 100644 index 0000000..f8b097c Binary files /dev/null and b/dist/favicon.png differ diff --git a/dist/gfx/star.svg b/dist/gfx/star.svg new file mode 100644 index 0000000..66c36c2 --- /dev/null +++ b/dist/gfx/star.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dist/global.css b/dist/global.css new file mode 100644 index 0000000..e69de29 diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 0000000..e744409 --- /dev/null +++ b/dist/index.html @@ -0,0 +1,18 @@ + + + + + + + Jobs Server + + + + + + + + + + + diff --git a/feeds.md b/feeds.md new file mode 100644 index 0000000..eac4f77 --- /dev/null +++ b/feeds.md @@ -0,0 +1,16 @@ +```text + + + +https://www.jobserve.com/MySearch/ED1708BF42EF3513.rss +https://www.jobserve.com/MySearch/6EFA569DF8008ED5.rss +https://www.jobserve.com/MySearch/ABB2BB88FFA9ECF5.rss +https://www.jobserve.com/MySearch/2BBB5238D7B0A92B.rss +https://www.jobserve.com/MySearch/7DC15EBEFCADFABC.rss +https://www.jobserve.com/MySearch/E84953EAAD79B7BB.rss +https://www.jobserve.com/MySearch/E84953EAAD79B7BB.rss +https://www.jobserve.com/MySearch/3ACE55BA06CD0783.rss +https://www.jobserve.com/MySearch/BAEBF3BDF82B8FEF.rss +https://www.jobserve.com/MySearch/70DBC913965F9F95.rss + +``` \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..dccb3db --- /dev/null +++ b/go.mod @@ -0,0 +1,44 @@ +module jobscraper + +go 1.22.0 + +require ( + github.com/gofiber/fiber/v2 v2.52.4 + github.com/gofiber/template/html/v2 v2.1.1 + github.com/jackc/pgx/v5 v5.5.5 + github.com/lib/pq v1.10.9 + github.com/mmcdole/gofeed v1.3.0 + github.com/robfig/cron/v3 v3.0.0 + github.com/shomali11/util v0.0.0-20220717175126-f0771b70947f +) + +require ( + github.com/PuerkitoBio/goquery v1.8.0 // indirect + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/andybalholm/cascadia v1.3.1 // indirect + github.com/gofiber/template v1.8.3 // indirect + github.com/gofiber/utils v1.1.0 // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/philhofer/fwd v1.1.2 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/tinylib/msgp v1.1.8 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.51.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b1767f4 --- /dev/null +++ b/go.sum @@ -0,0 +1,123 @@ +github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= +github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= +github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM= +github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc= +github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8= +github.com/gofiber/template/html/v2 v2.1.1 h1:QEy3O3EBkvwDthy5bXVGUseOyO6ldJoiDxlF4+MJiV8= +github.com/gofiber/template/html/v2 v2.1.1/go.mod h1:2G0GHHOUx70C1LDncoBpe4T6maQbNa4x1CVNFW0wju0= +github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM= +github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4= +github.com/mmcdole/gofeed v1.3.0/go.mod h1:9TGv2LcJhdXePDzxiuMnukhV2/zb6VtnZt1mS+SjkLE= +github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23 h1:Zr92CAlFhy2gL+V1F+EyIuzbQNbSgP4xhTODZtrXUtk= +github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23/go.mod h1:v+25+lT2ViuQ7mVxcncQ8ch1URund48oH+jhjiwEgS8= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E= +github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/shomali11/parallelizer v0.0.0-20220717173222-a6776fbf40a9/go.mod h1:QsLM53l8gzX0sQbOjVir85bzOUucuJEF8JgE39wD7w0= +github.com/shomali11/util v0.0.0-20220717175126-f0771b70947f h1:OM0LVaVycWC+/j5Qra7USyCg2sc+shg3KwygAA+pYvA= +github.com/shomali11/util v0.0.0-20220717175126-f0771b70947f/go.mod h1:9POpw/crb6YrseaYBOwraL0lAYy0aOW79eU3bvMxgbM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= +github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= +github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/grabber/grabber-structs.go b/grabber/grabber-structs.go new file mode 100644 index 0000000..3a56ae0 --- /dev/null +++ b/grabber/grabber-structs.go @@ -0,0 +1,15 @@ +package grabber + +import "time" + +type RssItem struct { + Title string + URL string + Date time.Time + Link string + Summary string + Id string + Company string + Location string + Salary string +} diff --git a/grabber/grabber.go b/grabber/grabber.go new file mode 100644 index 0000000..6f899fe --- /dev/null +++ b/grabber/grabber.go @@ -0,0 +1,234 @@ +package grabber + +import ( + "fmt" + "github.com/shomali11/util/xhashes" + + "io" + "log" + "net/http" + "reflect" + "regexp" + + "github.com/mmcdole/gofeed" +) + +var ( + Version string + Build string + locationRegex = regexp.MustCompile(`Location:<\/strong><\/td> <\/td>(.*?)(?:<\/td>)`) + rateRegex = regexp.MustCompile(`Rate:<\/strong><\/td> <\/td>(.*?)<\/td>`) + companyRegex = regexp.MustCompile(`Advertiser:<\/strong><\/td> <\/td>(.*?)<\/td>`) + // accept filter + acceptRegex = regexp.MustCompile(`(full\s?stack|front\s?end|html|html5|es6|react|angular|knockout|ember|vue|riotjs|css|javascript|typescript|golang|go|sql|node|backbone|git|gulp|jquery|express|£\dk|Data Warehouse Developer|iot|internet of things)\W`) + + // reject filters + pattRegex = regexp.MustCompile(`(Simply Education|Splunk|Coordinators?|Teachers?|Technical Writers?|Data Analyst|WebLogic|WebSphere|Data Scientist|Change Managers?|T24|Test Analyst|Insight Analyst|application tester|senior tester|Salesforce|QlikView|Navision|Murex|seo|django|drupal|SHAREPOINT|per annum|ServiceNow|Test Lead|User Researcher|Service Management|\(PERM\)|£\d.K|Remedy|ITSM|Symfony|Zend|Full Time|Technical Business Analyst|BUSINESS ANALYST|AUTOMATION TESTER|FIELD TECHNICIAN|websphere administrator|Research Data Scientist)`) + engineersRegex = regexp.MustCompile(`((Support|Devops|Planning|security|Postgresql|network|sccm|test|data|imac|firewall|vmware)\s+Engineer)`) + developersRegex = regexp.MustCompile(`((Big Data|Java Server Side|Java|PHP|Graduate|Access|Oracle ADF|SHAREPOINT|Ruby on Rails|Java Software|IOS|Qlikview|c#|c\+\+|\.net|bi|go lang|Python)+\s+Developer+)`) + // architectsRegex = regexp.MustCompile(`(Java|PHP|Microsoft)+(?:\s)(?=Architect)`) +) + +func Grab(url string) []RssItem { + log.Printf("Grabbing: %v", url) + + // url := "https://www.jobserve.com/MySearch/F3A56475D5FD4966.rss" + + content, err := readFeed(url) + if err != nil { + log.Printf("failed to poll feed <%s>: %v", url, err) + // continue + } + + items, err := extractItems(content) + if err != nil { + log.Printf("Failed to extract items from feed %s: %v", url, err) + // log.Printf("Failed to extract items from feed %s: %v", feed.url, err) + // continue + } + + log.Printf("Length %v\n", len(items)) + + items = rejectItems(items) + + log.Printf("Length %v\n", len(items)) + + items = acceptItems(items) + + log.Printf("Length %v\n", len(items)) + + return items + +} + +func readFeed(url string) (string, error) { + resp, err := http.Get(url) + if err != nil { + return "", fmt.Errorf("failed to request feed: %v", err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("failed to read feed: %v", err) + } + + return string(body), err +} + +func showStruct(item any) { + val := reflect.ValueOf(item) + typ := val.Type() + + for i := 0; i < val.NumField(); i++ { + field := val.Field(i) + fieldType := typ.Field(i) + + fmt.Printf("Field Name: %s, Field Value: %v\n", fieldType.Name, field.Interface()) + } +} + +func extractItems(content string) ([]RssItem, error) { + var items []RssItem + + fp := gofeed.NewParser() + + feed, err := fp.ParseString(content) + if err != nil { + return items, fmt.Errorf("Failed to parse feed: %v", err) + } + + for _, item := range feed.Items { + + var ri RssItem + ri.Title = item.Title + if item.PublishedParsed != nil { + ri.Date = *item.PublishedParsed + } + + if item.GUID != "" { + // ri.Id = item.GUID + ri.Id = xhashes.SHA1(item.GUID) + } + + if item.Description != "" { + ri.Summary = item.Description + } + ri.URL = item.Link + + ri = additionalProcessing(ri) + + items = append(items, ri) + } + + return items, nil +} + +func additionalProcessing(workItem RssItem) RssItem { + + if loc := locationRegex.FindStringSubmatch(workItem.Summary); loc != nil { + + workItem.Location = loc[1] + } + + if rate := rateRegex.FindStringSubmatch(workItem.Summary); rate != nil { + + workItem.Salary = rate[1] + } + + if company := companyRegex.FindStringSubmatch(workItem.Summary); company != nil { + workItem.Company = company[1] + } + + return workItem + +} + +func acceptItems(jobitems []RssItem) []RssItem { + + var items []RssItem + + for _, item := range jobitems { + + var accept []string + + acceptable := false + + if pat := acceptRegex.FindStringSubmatch(item.Title); pat != nil { + + accept = append(accept, pat[0]) + + acceptable = true + } + + if pat := acceptRegex.FindStringSubmatch(item.Summary); pat != nil { + + accept = append(accept, pat[0]) + acceptable = true + } + + log.Printf("%v :: Accept? %v -- %v", item.Title, acceptable, accept) + + if acceptable == true { + + items = append(items, item) + + } + + } + + return items +} + +func rejectItems(jobitems []RssItem) []RssItem { + + var items []RssItem + + for _, item := range jobitems { + + var rejected []string + + rejectable := false + + if pat := pattRegex.FindStringSubmatch(item.Title); pat != nil { + rejected = append(rejected, pat[0]) + rejectable = true + } + + if pat := pattRegex.FindStringSubmatch(item.Summary); pat != nil { + rejected = append(rejected, pat[0]) + rejectable = true + } + + if pat := engineersRegex.FindStringSubmatch(item.Title); pat != nil { + rejected = append(rejected, pat[0]) + rejectable = true + } + + if pat := engineersRegex.FindStringSubmatch(item.Summary); pat != nil { + rejected = append(rejected, pat[0]) + rejectable = true + } + + if pat := developersRegex.FindStringSubmatch(item.Title); pat != nil { + rejected = append(rejected, pat[0]) + rejectable = true + } + + if pat := developersRegex.FindStringSubmatch(item.Summary); pat != nil { + rejected = append(rejected, pat[0]) + rejectable = true + } + + log.Printf("%v :: Reject? %v -- %v", item.Title, rejectable, rejected) + + if rejectable == false { + + items = append(items, item) + + } + + } + + return items +} diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..f62c799 --- /dev/null +++ b/notes.md @@ -0,0 +1,89 @@ +# Notes + + + +- Test Link https://www.jobserve.com/MySearch/F3A56475D5FD4966.rss + + + +LocationRegex +```regexp + +[Ll]ocation:(?:<\/span>)?\n*(.*?)  +Location:<\/strong><\/td> <\/td>(.*?)<\/td> + +``` + +RateRegex +```goregexp + +[Rr]ate:(?:<\/span>)?\n*(.*?)  +Rate:<\/strong><\/td> <\/td>(.*?)<\/td> +``` + +Company Regex +```goregexp + +Advertiser:<\/strong><\/td> <\/td>(.*?)<\/td> + +``` + +Location regex +```goregexp + +Location:<\/strong><\/td> <\/td>(.*?)<\/td> + +``` + +Image regex +```goregexp + + +src="https:\/\/(.+?)" + +``` + + + + +```sqlite + +-- jobs definition + +CREATE TABLE "jobs" ( + "_id" INTEGER NOT NULL UNIQUE, + "title" TEXT, + "site" TEXT, + "url" TEXT, + "id" TEXT UNIQUE, + "summary" TEXT, + "company" TEXT, + "location" TEXT, + "postdate" TEXT, + "salary" TEXT, + "easyapply" INTEGER, + "timestamp" INTEGER, + PRIMARY KEY("_id" AUTOINCREMENT) +); + +``` + + +```postgresql + +CREATE TABLE jobs ( + _id SERIAL PRIMARY KEY, + title VARCHAR, + site VARCHAR, + url VARCHAR, + id VARCHAR UNIQUE, + summary TEXT, + company VARCHAR, + location VARCHAR, + postdate VARCHAR, + salary VARCHAR, + easyapply INTEGER, + "timestamp" INTEGER +); + +``` \ No newline at end of file diff --git a/server.go b/server.go new file mode 100644 index 0000000..5d60749 --- /dev/null +++ b/server.go @@ -0,0 +1,317 @@ +package main + +import ( + "context" + "database/sql" + "errors" + "fmt" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cache" + "github.com/gofiber/template/html/v2" + "github.com/jackc/pgx/v5/pgxpool" + _ "github.com/lib/pq" + "github.com/robfig/cron/v3" + "jobscraper/grabber" + "log" + "os" + "strconv" + "time" +) + +var ( + Version string + Build string +) + +const fileName = "./db/jobs.db" + +var ( + ErrDuplicate = errors.New("record already exists") + ErrNotExists = errors.New("row not exists") + ErrUpdateFailed = errors.New("update failed") + ErrDeleteFailed = errors.New("delete failed") +) + +type SQLConn struct { + db *sql.DB +} + +type JobEntries struct { + ID int64 `json:"_id"` + Title string `json:"title"` + Site string `json:"site"` + Url string `json:"url"` + Id string `json:"id"` + Summary string `json:"summary"` + Company string `json:"company"` + Location string `json:"location"` + Postdate string `json:"postdate"` + Salary string `json:"salary"` + Easyapply int64 `json:"easyapply"` + Timestamp int64 `json:"timestamp"` + Applied any `json:"applied,omitempty"` + Read any `json:"read,omitempty"` +} + +type Site struct { + SID int64 `json:"sid"` + Url string `json:"url"` +} + +func main() { + log.Printf("GO-JOBSCRAPER v%+v build %+v\n\n", Version, Build) + + connStr := os.Getenv("DBCONNECTION") + + if connStr == "" { + log.Println("DBCONNECTION not set") + log.Println("Should be something like:") + log.Println("postgresql://user:password@server:5432/database?sslmode=disable") + + log.Fatalln("Exiting...") + } + + /*db*/ + db, err := pgxpool.New(context.Background(), connStr) + + if err != nil { + log.Fatal(err) + } + + if err != nil { + log.Fatal(err) + } else { + log.Println("connected") + } + defer db.Close() + + // url := "https://www.jobserve.com/MySearch/F3A56475D5FD4966.rss" + + c := cron.New() + + c.AddFunc("*/15 * * * *", func() { JobWorker(db) }) + + c.Start() + + engine := html.New("./dist", ".html") + + app := fiber.New(fiber.Config{ + Views: engine, + }) + + // Caching.. + app.Use(cache.New(cache.Config{ + Next: func(c *fiber.Ctx) bool { + return c.Query("noCache") == "true" + }, + Expiration: 2 * time.Minute, + CacheControl: true, + })) + + app.Get("/", indexHandler) + + port := os.Getenv("PORT") + if port == "" { + port = "3600" + } + + app.Static("/", "./dist") + + app.Get("/jobs", func(c *fiber.Ctx) error { + return getJobs(c, db) + }) + app.Get("/jobs/:id", func(c *fiber.Ctx) error { + return getJobById(c, db) + }) + app.Put("/jobs/:id", func(c *fiber.Ctx) error { + return markJobAsReadById(c, db) + }) + + log.Fatalln(app.Listen(fmt.Sprintf(":%v", port))) + +} + +func indexHandler(c *fiber.Ctx) error { + + return c.Render("index", nil) +} + +func JobWorker(db *pgxpool.Pool) { + log.Println("JobWorker") + + sites, err := AllSites(db) + + if err != nil { + log.Fatal(err) + } + + log.Printf("%+v\n", sites) + // showstruct.Show(sites) + + for _, url := range sites { + entries := grabber.Grab(url.Url) + + InsertJobs(db, entries) + + } + + /*entries := grabber.Grab("https://www.jobserve.com/MySearch/F3A56475D5FD4966.rss") + + InsertJobs(db, entries)*/ + +} + +func AllSites(db *pgxpool.Pool) ([]Site, error) { + log.Println("ALL Sites") + // rows, err := r.db.Query(`SELECT * from jobs`) + + var sites []Site + + rows, err := db.Query(context.Background(), "SELECT * from sites") + + defer rows.Close() + + if err != nil { + log.Fatalln(err) + + return sites, err + } + + for rows.Next() { + var newsite Site + if err := rows.Scan(&newsite.SID, &newsite.Url); err != nil { + return nil, err + } + + sites = append(sites, newsite) + } + + if err = rows.Err(); err != nil { + log.Fatal(err) + + return sites, err + } + + return sites, nil +} + +func InsertJobs(db *pgxpool.Pool, jobs []grabber.RssItem) error { + + // Rollback is safe to call even if the tx is already closed, so if + // the tx commits successfully, this is a no-op + + t := time.Now() + + ms := strconv.Itoa(int(t.Unix())) + + for _, job := range jobs { + + // showstruct.Show(job) + + log.Println("Inserting") + + _, err := db.Exec(context.Background(), "insert into jobs(\"_id\", title, site, url, id, summary, company, \"location\", postdate, salary,easyapply, applied, approved, \"timestamp\") VALUES(nextval('jobs__id_seq'::regclass), $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)", job.Title, "Jobserve", job.URL, job.Id, job.Summary, job.Company, job.Location, job.Date.Format("2006-01-02 15:04:05"), job.Salary, 0, 0, 1, ms) + if err != nil { + log.Println(err) + continue + } + + } + + return nil +} + +func getJobs(c *fiber.Ctx, db *pgxpool.Pool) error { + log.Println("GetJobs") + + var jobs []JobEntries + + rows, err := db.Query(context.Background(), `SELECT jobs._id, jobs.title, jobs.site, jobs.company, jobs.timestamp, coalesce(applied.a, 0) as a, coalesce(read.d, 0) as d +FROM jobs +left join applied on applied.aid = jobs._id +left join read on read.rid = jobs._id order by jobs._id desc`) + + defer rows.Close() + + if err != nil { + log.Fatalln(err) + c.JSON("An error occured") + } + + for rows.Next() { + var job JobEntries + if err := rows.Scan(&job.ID, &job.Title, &job.Site, &job.Company, &job.Timestamp, &job.Applied, &job.Read); err != nil { + return err + } + + jobs = append(jobs, job) + } + + if err = rows.Err(); err != nil { + log.Fatal(err) + + return c.JSON(nil) + } + + return c.JSON(jobs) +} + +func getJobById(c *fiber.Ctx, db *pgxpool.Pool) error { + log.Println("GetJobById") + + var entry JobEntries + + id := c.Params("id") + log.Printf("-- %+v\n", id) + + if id == "" { + log.Println("no id supplied...") + + return c.SendString("{}") + } + + rows, err := db.Query(context.Background(), `SELECT jobs._id, jobs.title, jobs.site, jobs.url, jobs.id, jobs.summary, jobs.company, jobs.location, jobs.postdate, jobs.salary, jobs.easyapply, jobs."timestamp", coalesce(applied.a, 0) as a FROM jobs + left join applied on applied.aid = jobs._id WHERE jobs._id = $1`, id) + + defer rows.Close() + + if err = rows.Err(); err != nil { + log.Fatal(err) + + return c.JSON(nil) + } + + for rows.Next() { + var job JobEntries + if err := rows.Scan(&job.ID, &job.Title, &job.Site, &job.Url, &job.Id, &job.Summary, &job.Company, &job.Location, &job.Postdate, &job.Salary, &job.Easyapply, &job.Timestamp, &job.Applied); err != nil { + return err + } + + entry = job + } + + return c.JSON(entry) +} + +func markJobAsReadById(c *fiber.Ctx, db *pgxpool.Pool) error { + log.Println("markJobasReadById") + id := c.Params("id") + log.Printf("-- %+v\n", id) + + t := time.Now() + + if id != "" { + log.Println("Marking entry %v as read", id) + + r, err := db.Exec(context.Background(), `INSERT INTO public."read" ("_id", rid, d) VALUES(nextval('read__id_seq'::regclass), $1, $2);`, id, t.Unix()) + if err != nil { + log.Printf("An error occured while executing query: %v", err) + } + if r.RowsAffected() != 1 { + return errors.New("No row affected...") + } + log.Println("***") + } + + return c.SendStatus(200) +} diff --git a/showstruct/showstruct.go b/showstruct/showstruct.go new file mode 100644 index 0000000..1917014 --- /dev/null +++ b/showstruct/showstruct.go @@ -0,0 +1,18 @@ +package showstruct + +import ( + "fmt" + "reflect" +) + +func Show(item any) { + val := reflect.ValueOf(item) + typ := val.Type() + + for i := 0; i < val.NumField(); i++ { + field := val.Field(i) + fieldType := typ.Field(i) + + fmt.Printf("Field : %s, Value: %v\n", fieldType.Name, field.Interface()) + } +}