Compare commits
No commits in common. "d69a193e6deeba5597e64f3fbfa0a1a09a6c61ff" and "15d64b94b03b1599e85a8fedbf7c09375c4e3272" have entirely different histories.
d69a193e6d
...
15d64b94b0
4
Gemfile
4
Gemfile
@ -56,12 +56,10 @@ gem "ahoy_matey", "~> 5.2"
|
|||||||
gem "ruby-openai", "~> 7.3"
|
gem "ruby-openai", "~> 7.3"
|
||||||
gem "httparty", "~> 0.22.0"
|
gem "httparty", "~> 0.22.0"
|
||||||
gem "down", "~> 5.4"
|
gem "down", "~> 5.4"
|
||||||
gem "aws-sdk-s3", "~> 1.179"
|
gem "aws-sdk-s3", "~> 1.177"
|
||||||
gem "sidekiq", "~> 7.3"
|
gem "sidekiq", "~> 7.3"
|
||||||
gem "sidekiq-scheduler", "~> 5.0"
|
gem "sidekiq-scheduler", "~> 5.0"
|
||||||
|
|
||||||
gem "image_processing", "~> 1.13"
|
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
|
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
|
||||||
gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
|
gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
|
||||||
|
60
Gemfile.lock
60
Gemfile.lock
@ -93,17 +93,17 @@ GEM
|
|||||||
ruby2_keywords (>= 0.0.2)
|
ruby2_keywords (>= 0.0.2)
|
||||||
ast (2.4.2)
|
ast (2.4.2)
|
||||||
aws-eventstream (1.3.0)
|
aws-eventstream (1.3.0)
|
||||||
aws-partitions (1.1043.0)
|
aws-partitions (1.1035.0)
|
||||||
aws-sdk-core (3.217.0)
|
aws-sdk-core (3.215.0)
|
||||||
aws-eventstream (~> 1, >= 1.3.0)
|
aws-eventstream (~> 1, >= 1.3.0)
|
||||||
aws-partitions (~> 1, >= 1.992.0)
|
aws-partitions (~> 1, >= 1.992.0)
|
||||||
aws-sigv4 (~> 1.9)
|
aws-sigv4 (~> 1.9)
|
||||||
jmespath (~> 1, >= 1.6.1)
|
jmespath (~> 1, >= 1.6.1)
|
||||||
aws-sdk-kms (1.97.0)
|
aws-sdk-kms (1.96.0)
|
||||||
aws-sdk-core (~> 3, >= 3.216.0)
|
aws-sdk-core (~> 3, >= 3.210.0)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sigv4 (~> 1.5)
|
||||||
aws-sdk-s3 (1.179.0)
|
aws-sdk-s3 (1.177.0)
|
||||||
aws-sdk-core (~> 3, >= 3.216.0)
|
aws-sdk-core (~> 3, >= 3.210.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sigv4 (~> 1.5)
|
||||||
aws-sigv4 (1.11.0)
|
aws-sigv4 (1.11.0)
|
||||||
@ -164,14 +164,6 @@ GEM
|
|||||||
multipart-post (~> 2.0)
|
multipart-post (~> 2.0)
|
||||||
faraday-net_http (3.4.0)
|
faraday-net_http (3.4.0)
|
||||||
net-http (>= 0.5.0)
|
net-http (>= 0.5.0)
|
||||||
ffi (1.17.1-aarch64-linux-gnu)
|
|
||||||
ffi (1.17.1-aarch64-linux-musl)
|
|
||||||
ffi (1.17.1-arm-linux-gnu)
|
|
||||||
ffi (1.17.1-arm-linux-musl)
|
|
||||||
ffi (1.17.1-arm64-darwin)
|
|
||||||
ffi (1.17.1-x86_64-darwin)
|
|
||||||
ffi (1.17.1-x86_64-linux-gnu)
|
|
||||||
ffi (1.17.1-x86_64-linux-musl)
|
|
||||||
formtastic (5.0.0)
|
formtastic (5.0.0)
|
||||||
actionpack (>= 6.0.0)
|
actionpack (>= 6.0.0)
|
||||||
formtastic_i18n (0.7.0)
|
formtastic_i18n (0.7.0)
|
||||||
@ -189,19 +181,15 @@ GEM
|
|||||||
csv
|
csv
|
||||||
mini_mime (>= 1.0.0)
|
mini_mime (>= 1.0.0)
|
||||||
multi_xml (>= 0.5.2)
|
multi_xml (>= 0.5.2)
|
||||||
i18n (1.14.7)
|
i18n (1.14.6)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
image_processing (1.13.0)
|
|
||||||
mini_magick (>= 4.9.5, < 5)
|
|
||||||
ruby-vips (>= 2.0.17, < 3)
|
|
||||||
inherited_resources (1.14.0)
|
inherited_resources (1.14.0)
|
||||||
actionpack (>= 6.0)
|
actionpack (>= 6.0)
|
||||||
has_scope (>= 0.6)
|
has_scope (>= 0.6)
|
||||||
railties (>= 6.0)
|
railties (>= 6.0)
|
||||||
responders (>= 2)
|
responders (>= 2)
|
||||||
io-console (0.8.0)
|
io-console (0.8.0)
|
||||||
irb (1.15.1)
|
irb (1.14.3)
|
||||||
pp (>= 0.6.0)
|
|
||||||
rdoc (>= 4.0.0)
|
rdoc (>= 4.0.0)
|
||||||
reline (>= 0.4.2)
|
reline (>= 0.4.2)
|
||||||
jbuilder (2.13.0)
|
jbuilder (2.13.0)
|
||||||
@ -252,7 +240,6 @@ GEM
|
|||||||
matrix (0.4.2)
|
matrix (0.4.2)
|
||||||
meta-tags (2.22.1)
|
meta-tags (2.22.1)
|
||||||
actionpack (>= 6.0.0, < 8.1)
|
actionpack (>= 6.0.0, < 8.1)
|
||||||
mini_magick (4.13.2)
|
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
minitest (5.25.4)
|
minitest (5.25.4)
|
||||||
msgpack (1.7.5)
|
msgpack (1.7.5)
|
||||||
@ -276,21 +263,21 @@ GEM
|
|||||||
net-protocol
|
net-protocol
|
||||||
net-ssh (7.3.0)
|
net-ssh (7.3.0)
|
||||||
nio4r (2.7.4)
|
nio4r (2.7.4)
|
||||||
nokogiri (1.18.2-aarch64-linux-gnu)
|
nokogiri (1.18.1-aarch64-linux-gnu)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.2-aarch64-linux-musl)
|
nokogiri (1.18.1-aarch64-linux-musl)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.2-arm-linux-gnu)
|
nokogiri (1.18.1-arm-linux-gnu)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.2-arm-linux-musl)
|
nokogiri (1.18.1-arm-linux-musl)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.2-arm64-darwin)
|
nokogiri (1.18.1-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.2-x86_64-darwin)
|
nokogiri (1.18.1-x86_64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.2-x86_64-linux-gnu)
|
nokogiri (1.18.1-x86_64-linux-gnu)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.2-x86_64-linux-musl)
|
nokogiri (1.18.1-x86_64-linux-musl)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
ostruct (0.6.1)
|
ostruct (0.6.1)
|
||||||
@ -299,9 +286,6 @@ GEM
|
|||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
pg (1.5.9)
|
pg (1.5.9)
|
||||||
pp (0.6.2)
|
|
||||||
prettyprint
|
|
||||||
prettyprint (0.2.0)
|
|
||||||
propshaft (1.1.0)
|
propshaft (1.1.0)
|
||||||
actionpack (>= 7.0.0)
|
actionpack (>= 7.0.0)
|
||||||
activesupport (>= 7.0.0)
|
activesupport (>= 7.0.0)
|
||||||
@ -311,7 +295,7 @@ GEM
|
|||||||
date
|
date
|
||||||
stringio
|
stringio
|
||||||
public_suffix (6.0.1)
|
public_suffix (6.0.1)
|
||||||
puma (6.6.0)
|
puma (6.5.0)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
raabro (1.4.0)
|
raabro (1.4.0)
|
||||||
racc (1.8.1)
|
racc (1.8.1)
|
||||||
@ -402,9 +386,6 @@ GEM
|
|||||||
faraday (>= 1)
|
faraday (>= 1)
|
||||||
faraday-multipart (>= 1)
|
faraday-multipart (>= 1)
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.13.0)
|
||||||
ruby-vips (2.2.2)
|
|
||||||
ffi (~> 1.12)
|
|
||||||
logger
|
|
||||||
ruby2_keywords (0.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
rubyzip (2.4.1)
|
rubyzip (2.4.1)
|
||||||
rufus-scheduler (3.9.2)
|
rufus-scheduler (3.9.2)
|
||||||
@ -429,7 +410,7 @@ GEM
|
|||||||
tilt (>= 1.4.0, < 3)
|
tilt (>= 1.4.0, < 3)
|
||||||
sitemap_generator (6.3.0)
|
sitemap_generator (6.3.0)
|
||||||
builder (~> 3.0)
|
builder (~> 3.0)
|
||||||
solid_cable (3.0.7)
|
solid_cable (3.0.5)
|
||||||
actioncable (>= 7.2)
|
actioncable (>= 7.2)
|
||||||
activejob (>= 7.2)
|
activejob (>= 7.2)
|
||||||
activerecord (>= 7.2)
|
activerecord (>= 7.2)
|
||||||
@ -438,7 +419,7 @@ GEM
|
|||||||
activejob (>= 7.2)
|
activejob (>= 7.2)
|
||||||
activerecord (>= 7.2)
|
activerecord (>= 7.2)
|
||||||
railties (>= 7.2)
|
railties (>= 7.2)
|
||||||
solid_queue (1.1.3)
|
solid_queue (1.1.2)
|
||||||
activejob (>= 7.1)
|
activejob (>= 7.1)
|
||||||
activerecord (>= 7.1)
|
activerecord (>= 7.1)
|
||||||
concurrent-ruby (>= 1.3.1)
|
concurrent-ruby (>= 1.3.1)
|
||||||
@ -511,7 +492,7 @@ PLATFORMS
|
|||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
activeadmin (~> 3.2)
|
activeadmin (~> 3.2)
|
||||||
ahoy_matey (~> 5.2)
|
ahoy_matey (~> 5.2)
|
||||||
aws-sdk-s3 (~> 1.179)
|
aws-sdk-s3 (~> 1.177)
|
||||||
bootsnap
|
bootsnap
|
||||||
brakeman
|
brakeman
|
||||||
capybara
|
capybara
|
||||||
@ -521,7 +502,6 @@ DEPENDENCIES
|
|||||||
down (~> 5.4)
|
down (~> 5.4)
|
||||||
friendly_id (~> 5.5)
|
friendly_id (~> 5.5)
|
||||||
httparty (~> 0.22.0)
|
httparty (~> 0.22.0)
|
||||||
image_processing (~> 1.13)
|
|
||||||
jbuilder
|
jbuilder
|
||||||
jsbundling-rails
|
jsbundling-rails
|
||||||
kamal
|
kamal
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 5.9 KiB |
@ -1,4 +1,3 @@
|
|||||||
@import "photoswipe/dist/photoswipe.css";
|
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
class HomeController < ApplicationController
|
class HomeController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@latest_arts = WeatherArt.includes(:city).order(created_at: :desc).limit(20).shuffle.last(10)
|
@latest_arts = WeatherArt.includes(:city).order(created_at: :desc).limit(6)
|
||||||
@featured_arts = WeatherArt.includes(:city).order(created_at: :desc).limit(5)
|
@featured_arts = WeatherArt.includes(:city).order(created_at: :desc).limit(5)
|
||||||
set_meta_tags(
|
set_meta_tags(
|
||||||
title: "AI-Generated Weather Art",
|
title: "AI-Generated Weather Art",
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// Entry point for the build script in your package.json
|
// Entry point for the build script in your package.json
|
||||||
import "@hotwired/turbo-rails"
|
import "@hotwired/turbo-rails"
|
||||||
import "@hotwired/stimulus"
|
|
||||||
import "@fontsource/playfair-display/400.css";
|
import "@fontsource/playfair-display/400.css";
|
||||||
import "@fontsource/playfair-display/700.css";
|
import "@fontsource/playfair-display/700.css";
|
||||||
import "@fontsource/raleway/400.css";
|
import "@fontsource/raleway/400.css";
|
||||||
|
@ -6,9 +6,3 @@ import { application } from "./application"
|
|||||||
|
|
||||||
import HelloController from "./hello_controller"
|
import HelloController from "./hello_controller"
|
||||||
application.register("hello", HelloController)
|
application.register("hello", HelloController)
|
||||||
|
|
||||||
import PhotoSwipeLightBoxController from "./photo_swipe_lightbox_controller"
|
|
||||||
|
|
||||||
console.log("ready to register photo-swipe")
|
|
||||||
application.register("photo-swipe-lightbox", PhotoSwipeLightBoxController)
|
|
||||||
console.log("successful to register photo-swipe")
|
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
import { Controller } from "@hotwired/stimulus"
|
|
||||||
import PhotoSwipeLightbox from 'photoswipe/lightbox'
|
|
||||||
import PhotoSwipe from 'photoswipe'
|
|
||||||
import 'photoswipe/dist/photoswipe.css'
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
static targets = ['image', 'gallery']
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
this.initPhotoSwipeLightbox()
|
|
||||||
}
|
|
||||||
|
|
||||||
initPhotoSwipeLightbox() {
|
|
||||||
const lightbox = new PhotoSwipeLightbox({
|
|
||||||
gallery: this.galleryTarget,
|
|
||||||
children: 'a',
|
|
||||||
pswpModule: PhotoSwipe,
|
|
||||||
initialZoomInEndEvent: 'mousedown',
|
|
||||||
dataSource: (items) => {
|
|
||||||
return items.map((item) => ({
|
|
||||||
src: item.dataset.pswpSrc,
|
|
||||||
w: parseInt(item.dataset.pswpWidth, 10),
|
|
||||||
h: parseInt(item.dataset.pswpHeight, 10),
|
|
||||||
title: item.dataset.pswpCaption,
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
padding: { top: 0, bottom: 0, left: 0, right: 0 }, // 自定义图片与页面边界的填充
|
|
||||||
closeOnScroll: false,
|
|
||||||
zoom: true, // 启用缩放功能
|
|
||||||
bgOpacity: 0.9, // 背景透明度
|
|
||||||
pswpUIOptions: {
|
|
||||||
arrowPrev: true,
|
|
||||||
arrowNext: true,
|
|
||||||
zoom: true, // 添加缩放按钮
|
|
||||||
fullscreen: true, // 添加全屏按钮
|
|
||||||
counter: true, // 显示当前图片编号
|
|
||||||
}
|
|
||||||
})
|
|
||||||
lightbox.init()
|
|
||||||
// console.log('PhotoSwipeLightbox instance:', lightbox);
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,6 @@ class WeatherArt < ApplicationRecord
|
|||||||
|
|
||||||
belongs_to :city
|
belongs_to :city
|
||||||
has_one_attached :image
|
has_one_attached :image
|
||||||
has_one_attached :image_with_watermark
|
|
||||||
|
|
||||||
has_many :visits, class_name: "Ahoy::Visit", foreign_key: :weather_art_id
|
has_many :visits, class_name: "Ahoy::Visit", foreign_key: :weather_art_id
|
||||||
has_many :events, class_name: "Ahoy::Event", foreign_key: :weather_art_id
|
has_many :events, class_name: "Ahoy::Event", foreign_key: :weather_art_id
|
||||||
@ -51,8 +50,4 @@ class WeatherArt < ApplicationRecord
|
|||||||
Ahoy::Event.where("properties::jsonb->>'event_type' = 'weather_art_view' AND (properties::jsonb->>'weather_art_id')::integer = ?", self.id).count
|
Ahoy::Event.where("properties::jsonb->>'event_type' = 'weather_art_view' AND (properties::jsonb->>'weather_art_id')::integer = ?", self.id).count
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def image_url
|
|
||||||
image.attached? ? image.blob : nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -50,7 +50,7 @@ class AiService
|
|||||||
- Temperature: #{weather_data[:temperature]}°C
|
- Temperature: #{weather_data[:temperature]}°C
|
||||||
- Weather: #{weather_data[:description]}
|
- Weather: #{weather_data[:description]}
|
||||||
- Cloud cover: #{weather_data[:cloud]}%
|
- Cloud cover: #{weather_data[:cloud]}%
|
||||||
- Time: #{weather_data[:time]}
|
- Time: Early morning
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
- Feature iconic landmarks or architecture from #{city.name}
|
- Feature iconic landmarks or architecture from #{city.name}
|
||||||
|
@ -31,8 +31,7 @@ class WeatherService
|
|||||||
pressure: data["pressure"].to_f,
|
pressure: data["pressure"].to_f,
|
||||||
visibility: data["vis"].to_f,
|
visibility: data["vis"].to_f,
|
||||||
cloud: data["cloud"].to_f,
|
cloud: data["cloud"].to_f,
|
||||||
description: data["text"],
|
description: data["text"]
|
||||||
time: response["updateTime"]
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<!-- 最新天气艺术 -->
|
<!-- 最新天气艺术 -->
|
||||||
<section class="container mx-auto px-4 py-16 space-y-12">
|
<section class="container mx-auto px-4 py-16 space-y-12">
|
||||||
<h2 class="text-3xl font-display font-bold text-center">Shuffle Latest Weather Art</h2>
|
<h2 class="text-3xl font-display font-bold text-center">Latest Weather Art</h2>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
<% @latest_arts.each do |art| %>
|
<% @latest_arts.each do |art| %>
|
||||||
|
@ -33,22 +33,22 @@
|
|||||||
<%# Includes all stylesheet files in app/assets/stylesheets %>
|
<%# Includes all stylesheet files in app/assets/stylesheets %>
|
||||||
<%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
|
<%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
|
||||||
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
|
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
|
||||||
<!-- <script defer data-domain="todayaiweather.com" src="https://plausible.frytea.com/js/script.js"></script>-->
|
<script defer data-domain="todayaiweather.com" src="https://plausible.frytea.com/js/script.js"></script>
|
||||||
|
|
||||||
<!-- <script defer src="https://busuanzi.frytea.com/js"></script>-->
|
<script defer src="https://busuanzi.frytea.com/js"></script>
|
||||||
|
|
||||||
<!-- Google tag (gtag.js) -->
|
<!-- Google tag (gtag.js) -->
|
||||||
<!-- <script async src="https://www.googletagmanager.com/gtag/js?id=G-PX1C92V5L7"></script>-->
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-PX1C92V5L7"></script>
|
||||||
<!-- <script>-->
|
<script>
|
||||||
<!-- window.dataLayer = window.dataLayer || [];-->
|
window.dataLayer = window.dataLayer || [];
|
||||||
<!-- function gtag(){dataLayer.push(arguments);}-->
|
function gtag(){dataLayer.push(arguments);}
|
||||||
<!-- gtag('js', new Date());-->
|
gtag('js', new Date());
|
||||||
|
|
||||||
<!-- gtag('config', 'G-PX1C92V5L7');-->
|
gtag('config', 'G-PX1C92V5L7');
|
||||||
<!-- </script>-->
|
</script>
|
||||||
|
|
||||||
<!-- <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7296634171837358"-->
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7296634171837358"
|
||||||
<!-- crossorigin="anonymous"></script>-->
|
crossorigin="anonymous"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<div class="join shadow-lg">
|
<div class="join shadow-lg">
|
||||||
<!-- 首页 -->
|
<!-- 首页 -->
|
||||||
<%= link_to url_for(page: 1, region: params[:region], country: params[:country], sort: params[:sort]),
|
<%= link_to url_for(page: 1, region: params[:region], country: params[:country], sort: params[:sort]),
|
||||||
class: "join-item btn btn-xs #{collection.first_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
|
class: "join-item btn #{collection.first_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
|
||||||
</svg>
|
</svg>
|
||||||
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<!-- 上一页 -->
|
<!-- 上一页 -->
|
||||||
<%= link_to url_for(page: collection.prev_page || 1, region: params[:region], country: params[:country], sort: params[:sort]),
|
<%= link_to url_for(page: collection.prev_page || 1, region: params[:region], country: params[:country], sort: params[:sort]),
|
||||||
class: "join-item btn btn-xs #{collection.first_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
|
class: "join-item btn #{collection.first_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
||||||
</svg>
|
</svg>
|
||||||
@ -33,35 +33,35 @@
|
|||||||
|
|
||||||
<% if start_page > 1 %>
|
<% if start_page > 1 %>
|
||||||
<%= link_to 1, url_for(page: 1, region: params[:region], country: params[:country], sort: params[:sort]),
|
<%= link_to 1, url_for(page: 1, region: params[:region], country: params[:country], sort: params[:sort]),
|
||||||
class: "join-item btn btn-xs btn-ghost hover:bg-primary/5" %>
|
class: "join-item btn btn-ghost hover:bg-primary/5" %>
|
||||||
<% if start_page > 2 %>
|
<% if start_page > 2 %>
|
||||||
<button class="join-item btn btn-xs btn-ghost btn-disabled">...</button>
|
<button class="join-item btn btn-ghost btn-disabled">...</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% (start_page..end_page).each do |page| %>
|
<% (start_page..end_page).each do |page| %>
|
||||||
<% if page == collection.current_page %>
|
<% if page == collection.current_page %>
|
||||||
<button class="join-item btn btn-xs btn-ghost bg-primary/10 font-medium">
|
<button class="join-item btn btn-ghost bg-primary/10 font-medium">
|
||||||
<%= page %>
|
<%= page %>
|
||||||
</button>
|
</button>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= link_to page, url_for(page: page, region: params[:region], country: params[:country], sort: params[:sort]),
|
<%= link_to page, url_for(page: page, region: params[:region], country: params[:country], sort: params[:sort]),
|
||||||
class: "join-item btn btn-xs btn-ghost hover:bg-primary/5" %>
|
class: "join-item btn btn-ghost hover:bg-primary/5" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if end_page < collection.total_pages %>
|
<% if end_page < collection.total_pages %>
|
||||||
<% if end_page < collection.total_pages - 1 %>
|
<% if end_page < collection.total_pages - 1 %>
|
||||||
<button class="join-item btn btn-xs btn-ghost btn-disabled">...</button>
|
<button class="join-item btn btn-ghost btn-disabled">...</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= link_to collection.total_pages,
|
<%= link_to collection.total_pages,
|
||||||
url_for(page: collection.total_pages, region: params[:region], country: params[:country], sort: params[:sort]),
|
url_for(page: collection.total_pages, region: params[:region], country: params[:country], sort: params[:sort]),
|
||||||
class: "join-item btn btn-xs btn-ghost hover:bg-primary/5" %>
|
class: "join-item btn btn-ghost hover:bg-primary/5" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<!-- 下一页 -->
|
<!-- 下一页 -->
|
||||||
<%= link_to url_for(page: collection.next_page || collection.total_pages, region: params[:region], country: params[:country], sort: params[:sort]),
|
<%= link_to url_for(page: collection.next_page || collection.total_pages, region: params[:region], country: params[:country], sort: params[:sort]),
|
||||||
class: "join-item btn btn-xs #{collection.last_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
|
class: "join-item btn #{collection.last_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||||
</svg>
|
</svg>
|
||||||
@ -69,7 +69,7 @@
|
|||||||
|
|
||||||
<!-- 末页 -->
|
<!-- 末页 -->
|
||||||
<%= link_to url_for(page: collection.total_pages, region: params[:region], country: params[:country], sort: params[:sort]),
|
<%= link_to url_for(page: collection.total_pages, region: params[:region], country: params[:country], sort: params[:sort]),
|
||||||
class: "join-item btn btn-xs #{collection.last_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
|
class: "join-item btn #{collection.last_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
|
||||||
</svg>
|
</svg>
|
||||||
|
11
app/views/weather_arts/_weather_stat.html.erb
Normal file
11
app/views/weather_arts/_weather_stat.html.erb
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!-- app/views/weather_arts/_weather_stat.html.erb -->
|
||||||
|
<div class="stat bg-base-200/50 backdrop-blur-sm rounded-box hover:bg-base-300/50 transition-all duration-300">
|
||||||
|
<div class="flex items-center gap-2 mb-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="<%= icon %>" />
|
||||||
|
</svg>
|
||||||
|
<div class="stat-title font-medium"><%= title %></div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value text-2xl"><%= value %></div>
|
||||||
|
<div class="stat-desc mt-1"><%= desc %></div>
|
||||||
|
</div>
|
@ -1,37 +0,0 @@
|
|||||||
<%# Partial _weather_stats.html.erb %>
|
|
||||||
|
|
||||||
<div class="stat bg-gradient-to-br from-primary/10 to-primary/20 hover:from-primary hover:to-primary/30 p-4 rounded-lg">
|
|
||||||
<div class="stat-title font-medium text-base">Temperature</div>
|
|
||||||
<div class="stat-value text-3xl"><%= weather_art.temperature %>°C</div>
|
|
||||||
<div class="stat-desc">Feels like <%= weather_art.feeling_temp %>°C</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stat bg-gradient-to-br from-secondary/10 to-secondary/20 hover:from-secondary hover:to-secondary/30 p-4 rounded-lg">
|
|
||||||
<div class="stat-title font-medium text-base">Wind</div>
|
|
||||||
<div class="stat-value text-3xl"><%= weather_art.wind_scale %></div>
|
|
||||||
<div class="stat-desc"><%= weather_art.wind_speed %> km/h</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stat bg-base-300 hover:bg-base-400 p-4 rounded-lg">
|
|
||||||
<div class="stat-title font-medium text-base">Humidity</div>
|
|
||||||
<div class="stat-value text-3xl"><%= weather_art.humidity %>%</div>
|
|
||||||
<div class="stat-desc">Relative humidity</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stat bg-base-300 hover:bg-base-400 p-4 rounded-lg">
|
|
||||||
<div class="stat-title font-medium text-base">Visibility</div>
|
|
||||||
<div class="stat-value text-3xl"><%= weather_art.visibility %> km</div>
|
|
||||||
<div class="stat-desc">Clear view distance</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stat bg-accent/10 hover:bg-accent p-4 rounded-lg">
|
|
||||||
<div class="stat-title font-medium text-base">Pressure</div>
|
|
||||||
<div class="stat-value text-3xl"><%= weather_art.pressure %> hPa</div>
|
|
||||||
<div class="stat-desc">Atmospheric pressure</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stat bg-base-200 hover:bg-base-100 p-4 rounded-lg">
|
|
||||||
<div class="stat-title font-medium text-base">Cloud Cover</div>
|
|
||||||
<div class="stat-value text-3xl"><%= weather_art.cloud %>%</div>
|
|
||||||
<div class="stat-desc">Sky coverage</div>
|
|
||||||
</div>
|
|
@ -4,88 +4,155 @@
|
|||||||
</script>
|
</script>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="relative min-h-screen bg-white"> <!-- 使用更明快的背景颜色 -->
|
<div class="relative min-h-screen bg-base-200">
|
||||||
<div class="container mx-auto px-4 pt-12 pb-16">
|
<!-- 背景图片 -->
|
||||||
<div class="max-w-6xl mx-auto space-y-6">
|
<% if @weather_art.image.attached? %>
|
||||||
|
<div class="fixed inset-0 -z-10">
|
||||||
|
<%= image_tag @weather_art.image,
|
||||||
|
class: "absolute w-full h-full object-cover scale-110 filter blur-2xl opacity-25" %>
|
||||||
|
<div class="absolute inset-0 bg-gradient-to-b from-base-200/90 to-base-200/70 backdrop-blur-md"></div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<!-- 返回导航 -->
|
<!-- 主要内容 -->
|
||||||
<div class="flex items-center">
|
<div class="relative z-10">
|
||||||
<%= link_to city_path(@weather_art.city),
|
<!-- 返回导航 -->
|
||||||
class: "btn btn-ghost btn-md gap-2 bg-base-200 hover:bg-base-300 transition-all duration-300" do %>
|
<div class="container mx-auto px-4 py-6">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<%= link_to city_path(@weather_art.city),
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
class: "btn btn-ghost btn-lg gap-2 bg-base-100/50 backdrop-blur-sm hover:bg-base-100/70 transition-all duration-300" do %>
|
||||||
</svg>
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
Back to <%= @weather_art.city.name %>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
||||||
<% end %>
|
</svg>
|
||||||
</div>
|
Back to <%= @weather_art.city.name %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 主要内容 -->
|
<div class="container mx-auto px-4 pb-16">
|
||||||
<div class="card bg-base-200/80 backdrop-blur-md shadow-lg overflow-hidden"> <!-- 调整透明度和阴影 -->
|
<div class="max-w-6xl mx-auto">
|
||||||
<div class="grid lg:grid-cols-2 gap-6 items-center">
|
<!-- 头部信息 -->
|
||||||
|
<div class="text-center space-y-4 mb-12">
|
||||||
|
<div class="inline-flex items-center gap-2 text-sm font-medium px-4 py-2 rounded-full bg-base-100/50 backdrop-blur-sm">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
||||||
|
</svg>
|
||||||
|
<%= @weather_art.city.full_name %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 图片区域 -->
|
<h1 class="text-4xl md:text-6xl font-display font-bold">
|
||||||
<% if @weather_art.image.attached? %>
|
<span class="bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
|
||||||
<figure class="relative lg:h-[30rem] h-auto overflow-hidden rounded-lg"> <!-- 添加圆角 -->
|
Weather Art
|
||||||
<div class="gallery" data-controller="photo-swipe-lightbox">
|
</span>
|
||||||
<div data-photo-swipe-lightbox-target="gallery" class="h-full">
|
</h1>
|
||||||
<% blob = @weather_art.image_blob %>
|
|
||||||
<%= link_to rails_blob_path(blob),
|
|
||||||
data: {
|
|
||||||
pswp_src: rails_blob_url(blob),
|
|
||||||
pswp_caption: 'Weather Art',
|
|
||||||
pswp_width: 1792,
|
|
||||||
pswp_height: 1024
|
|
||||||
} do %>
|
|
||||||
<%= image_tag @weather_art.image,
|
|
||||||
class: "object-cover w-full h-full transition-transform transform hover:scale-105 ease-in-out" %> <!-- 改变缩放效果 -->
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</figure>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<!-- 信息区域 -->
|
<div class="flex flex-wrap justify-center items-center gap-3">
|
||||||
<div class="card-body p-8 lg:py-10 lg:px-12">
|
<div class="badge badge-lg badge-primary gap-2">
|
||||||
<div class="prose max-w-none">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<h1 class="font-display text-4xl md:text-5xl font-bold text-gradient mb-6">
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||||
Weather Art
|
</svg>
|
||||||
</h1>
|
<%= @weather_art.weather_date.strftime("%B %d, %Y") %>
|
||||||
|
</div>
|
||||||
|
<div class="badge badge-lg badge-secondary gap-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
<%= @weather_art.weather_date.strftime("%H:%M") %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-4 mb-6">
|
<!-- 主要卡片 -->
|
||||||
<div class="badge badge-lg badge-primary">
|
<div class="card lg:card-side bg-base-100/80 backdrop-blur-md shadow-2xl">
|
||||||
<%= @weather_art.weather_date.strftime("%B %d, %Y") %>
|
<figure class="lg:w-1/2 relative aspect-square lg:aspect-auto group">
|
||||||
</div>
|
<% if @weather_art.image.attached? %>
|
||||||
<div class="badge badge-lg badge-secondary">
|
<%= image_tag @weather_art.image,
|
||||||
<%= @weather_art.weather_date.strftime("%H:%M") %>
|
class: "w-full h-full object-cover transition-transform duration-500 group-hover:scale-105" %>
|
||||||
</div>
|
<div class="absolute inset-0 bg-gradient-to-t from-base-100/50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
</div>
|
<% end %>
|
||||||
|
</figure>
|
||||||
|
|
||||||
<h2 class="text-2xl font-semibold mb-4">
|
<div class="card-body lg:w-1/2">
|
||||||
|
<div class="prose max-w-none mb-8">
|
||||||
|
<h2 class="card-title font-display text-3xl mb-4 flex items-center gap-3">
|
||||||
<%= weather_description_icon(@weather_art.description) %>
|
<%= weather_description_icon(@weather_art.description) %>
|
||||||
<%= @weather_art.description %>
|
<%= @weather_art.description %>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<!-- 天气数据网格 -->
|
||||||
<%= render 'weather_stats', weather_art: @weather_art %> <!-- 使用局部渲染 -->
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div class="stat bg-base-200/50 backdrop-blur-sm rounded-box hover:bg-base-300/50 transition-all duration-300">
|
||||||
|
<div class="flex items-center gap-2 mb-2">
|
||||||
|
<%= weather_stat_icon("temperature") %>
|
||||||
|
<div class="stat-title font-medium">Temperature</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value text-2xl"><%= @weather_art.temperature %>°C</div>
|
||||||
|
<div class="stat-desc mt-1">Feels like <%= @weather_art.feeling_temp %>°C</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat bg-base-200/50 backdrop-blur-sm rounded-box hover:bg-base-300/50 transition-all duration-300">
|
||||||
|
<div class="flex items-center gap-2 mb-2">
|
||||||
|
<%= weather_stat_icon("wind") %>
|
||||||
|
<div class="stat-title font-medium">Wind</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value text-2xl"><%= @weather_art.wind_scale %></div>
|
||||||
|
<div class="stat-desc mt-1"><%= @weather_art.wind_speed %> km/h</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat bg-base-200/50 backdrop-blur-sm rounded-box hover:bg-base-300/50 transition-all duration-300">
|
||||||
|
<div class="flex items-center gap-2 mb-2">
|
||||||
|
<%= weather_stat_icon("humidity") %>
|
||||||
|
<div class="stat-title font-medium">Humidity</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value text-2xl"><%= @weather_art.humidity %>%</div>
|
||||||
|
<div class="stat-desc mt-1">Relative humidity</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat bg-base-200/50 backdrop-blur-sm rounded-box hover:bg-base-300/50 transition-all duration-300">
|
||||||
|
<div class="flex items-center gap-2 mb-2">
|
||||||
|
<%= weather_stat_icon("visibility") %>
|
||||||
|
<div class="stat-title font-medium">Visibility</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value text-2xl"><%= @weather_art.visibility %> km</div>
|
||||||
|
<div class="stat-desc mt-1">Clear view distance</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat bg-base-200/50 backdrop-blur-sm rounded-box hover:bg-base-300/50 transition-all duration-300">
|
||||||
|
<div class="flex items-center gap-2 mb-2">
|
||||||
|
<%= weather_stat_icon("pressure") %>
|
||||||
|
<div class="stat-title font-medium">Pressure</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value text-2xl"><%= @weather_art.pressure %> hPa</div>
|
||||||
|
<div class="stat-desc mt-1">Atmospheric pressure</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat bg-base-200/50 backdrop-blur-sm rounded-box hover:bg-base-300/50 transition-all duration-300">
|
||||||
|
<div class="flex items-center gap-2 mb-2">
|
||||||
|
<%= weather_stat_icon("cloud") %>
|
||||||
|
<div class="stat-title font-medium">Cloud Cover</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value text-2xl"><%= @weather_art.cloud %>%</div>
|
||||||
|
<div class="stat-desc mt-1">Sky coverage</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- AI Prompt -->
|
||||||
|
<div class="mt-8">
|
||||||
|
<div class="bg-base-200/50 backdrop-blur-sm p-6 rounded-box border border-base-300">
|
||||||
|
<div class="flex items-center gap-3 mb-4">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||||
|
</svg>
|
||||||
|
<h3 class="font-display font-bold text-lg">AI Prompt</h3>
|
||||||
|
</div>
|
||||||
|
<p class="text-base-content/70 leading-relaxed">
|
||||||
|
<%= @weather_art.prompt %>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- AI Prompt -->
|
|
||||||
<div class="bg-primary/10 backdrop-blur-md p-6 rounded-lg border border-primary/20">
|
|
||||||
<div class="flex items-center gap-3 mb-4">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
|
||||||
</svg>
|
|
||||||
<h3 class="font-display font-bold text-lg">AI Prompt</h3>
|
|
||||||
</div>
|
|
||||||
<p class="text-base-content/80 leading-relaxed">
|
|
||||||
<%= @weather_art.prompt %>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -1,53 +0,0 @@
|
|||||||
class AddWatermarkToWeatherArtWorker
|
|
||||||
include Sidekiq::Worker
|
|
||||||
|
|
||||||
def perform(weather_art_id)
|
|
||||||
@weather_art = WeatherArt.find_by(id: weather_art_id)
|
|
||||||
return unless @weather_art
|
|
||||||
|
|
||||||
add_watermark
|
|
||||||
rescue StandardError => e
|
|
||||||
Rails.logger.error "Error adding watermark to WeatherArt #{weather_art_id}: #{e.message}"
|
|
||||||
Rails.logger.error e.backtrace.join("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
attr_reader :weather_art
|
|
||||||
|
|
||||||
def add_watermark
|
|
||||||
return if weather_art.image_with_watermark.attached?
|
|
||||||
|
|
||||||
watermark_path = Rails.root.join("app/assets/images/today_ai_weather_copyright_watermark1.png")
|
|
||||||
return unless File.exist?(watermark_path)
|
|
||||||
|
|
||||||
image_tempfile = nil
|
|
||||||
watermark_tempfile = nil
|
|
||||||
begin
|
|
||||||
image_tempfile = weather_art.image.download
|
|
||||||
return unless image_dimensions_are_sufficient?(image_tempfile.path)
|
|
||||||
|
|
||||||
image = ImageProcessing::Vips.source(image_tempfile.path)
|
|
||||||
watermark = ImageProcessing::Vips.source(watermark_path)
|
|
||||||
watermarked_image = image.composite(watermark, "overlay")
|
|
||||||
watermark_tempfile = Tempfile.new([ "watermarked_image", ".png" ])
|
|
||||||
watermarked_image.write_to_file(watermark_tempfile.path)
|
|
||||||
weather_art.image_with_watermark.attach(
|
|
||||||
io: File.open(watermark_tempfile.path),
|
|
||||||
filename: "#{generate_filename("watermarked")}",
|
|
||||||
content_type: "image/png"
|
|
||||||
)
|
|
||||||
ensure
|
|
||||||
watermark_tempfile.unlink if watermark_tempfile
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def image_dimensions_are_sufficient?(image_path)
|
|
||||||
dimensions = ImageProcessing::Vips.source(image_path).sizes
|
|
||||||
dimensions.width >= 200 && dimensions.height >= 200
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate_filename(prefix)
|
|
||||||
"#{prefix}_#{weather_art.image.filename.base}"
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,16 +1,19 @@
|
|||||||
class BatchGenerateWeatherArtsWorker
|
class BatchGenerateWeatherArtsWorker
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
|
||||||
GENERATION_INTERVAL = 24.hours
|
GENERATION_INTERVAL = 24.hours
|
||||||
MAX_DURATION = 50.minutes
|
MAX_DURATION = 50.minutes
|
||||||
SLEEP_DURATION = 120.seconds
|
SLEEP_DURATION = 120.seconds
|
||||||
BATCH_SIZE = 20
|
|
||||||
|
|
||||||
def perform(*args)
|
def perform(*args)
|
||||||
start_time = Time.current
|
start_time = Time.current
|
||||||
cities_to_process = get_eligible_cities.shuffle.take(BATCH_SIZE)
|
|
||||||
|
cities_to_process = get_eligible_cities
|
||||||
|
|
||||||
cities_to_process.each do |city|
|
cities_to_process.each do |city|
|
||||||
break if Time.current - start_time > MAX_DURATION
|
break if Time.current - start_time > MAX_DURATION
|
||||||
Rails.logger.info "Generating weather art for #{city.name}"
|
Rails.logger.info "Generating weather art for #{city.name}"
|
||||||
|
|
||||||
GenerateWeatherArtWorker.perform_async(city.id)
|
GenerateWeatherArtWorker.perform_async(city.id)
|
||||||
sleep SLEEP_DURATION
|
sleep SLEEP_DURATION
|
||||||
end
|
end
|
||||||
@ -20,6 +23,7 @@ class BatchGenerateWeatherArtsWorker
|
|||||||
|
|
||||||
def get_eligible_cities
|
def get_eligible_cities
|
||||||
cutoff_time = Time.current - GENERATION_INTERVAL
|
cutoff_time = Time.current - GENERATION_INTERVAL
|
||||||
|
|
||||||
City.active
|
City.active
|
||||||
.joins("LEFT JOIN (
|
.joins("LEFT JOIN (
|
||||||
SELECT city_id, MAX(created_at) as last_generation_time
|
SELECT city_id, MAX(created_at) as last_generation_time
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"jquery": "^3.7.1",
|
"jquery": "^3.7.1",
|
||||||
"jquery-ui": "^1.14.1",
|
"jquery-ui": "^1.14.1",
|
||||||
"photoswipe": "^5.4.4",
|
|
||||||
"postcss": "^8.5.1",
|
"postcss": "^8.5.1",
|
||||||
"sass": "^1.83.4",
|
"sass": "^1.83.4",
|
||||||
"tailwindcss": "^3.4.17"
|
"tailwindcss": "^3.4.17"
|
||||||
|
16
yarn.lock
16
yarn.lock
@ -153,7 +153,7 @@
|
|||||||
|
|
||||||
"@hotwired/stimulus@^3.2.2":
|
"@hotwired/stimulus@^3.2.2":
|
||||||
version "3.2.2"
|
version "3.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.2.2.tgz#071aab59c600fed95b97939e605ff261a4251608"
|
resolved "https://registry.npmjs.org/@hotwired/stimulus/-/stimulus-3.2.2.tgz"
|
||||||
integrity sha512-eGeIqNOQpXoPAIP7tC1+1Yc1yl1xnwYqg+3mzqxyrbE5pg5YFBZcA6YoTiByJB6DKAEsiWtl6tjTJS4IYtbB7A==
|
integrity sha512-eGeIqNOQpXoPAIP7tC1+1Yc1yl1xnwYqg+3mzqxyrbE5pg5YFBZcA6YoTiByJB6DKAEsiWtl6tjTJS4IYtbB7A==
|
||||||
|
|
||||||
"@hotwired/turbo-rails@^8.0.12":
|
"@hotwired/turbo-rails@^8.0.12":
|
||||||
@ -722,13 +722,20 @@ jiti@^1.21.6:
|
|||||||
resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz"
|
resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz"
|
||||||
integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==
|
integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==
|
||||||
|
|
||||||
jquery-ui@^1.13.3, jquery-ui@^1.14.1:
|
jquery-ui@^1.13.3:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
resolved "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.14.1.tgz"
|
resolved "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.14.1.tgz"
|
||||||
integrity sha512-DhzsYH8VeIvOaxwi+B/2BCsFFT5EGjShdzOcm5DssWjtcpGWIMsn66rJciDA6jBruzNiLf1q0KvwMoX1uGNvnQ==
|
integrity sha512-DhzsYH8VeIvOaxwi+B/2BCsFFT5EGjShdzOcm5DssWjtcpGWIMsn66rJciDA6jBruzNiLf1q0KvwMoX1uGNvnQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
jquery ">=1.12.0 <5.0.0"
|
jquery ">=1.12.0 <5.0.0"
|
||||||
|
|
||||||
|
jquery-ui@^1.14.1:
|
||||||
|
version "1.14.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.14.1.tgz#ba342ea3ffff662b787595391f607d923313e040"
|
||||||
|
integrity sha512-DhzsYH8VeIvOaxwi+B/2BCsFFT5EGjShdzOcm5DssWjtcpGWIMsn66rJciDA6jBruzNiLf1q0KvwMoX1uGNvnQ==
|
||||||
|
dependencies:
|
||||||
|
jquery ">=1.12.0 <5.0.0"
|
||||||
|
|
||||||
jquery-ujs@^1.2.2:
|
jquery-ujs@^1.2.2:
|
||||||
version "1.2.3"
|
version "1.2.3"
|
||||||
resolved "https://registry.npmjs.org/jquery-ujs/-/jquery-ujs-1.2.3.tgz"
|
resolved "https://registry.npmjs.org/jquery-ujs/-/jquery-ujs-1.2.3.tgz"
|
||||||
@ -846,11 +853,6 @@ path-scurry@^1.11.1:
|
|||||||
lru-cache "^10.2.0"
|
lru-cache "^10.2.0"
|
||||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||||
|
|
||||||
photoswipe@^5.4.4:
|
|
||||||
version "5.4.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-5.4.4.tgz#e045dc036453493188d5c8665b0e8f1000ac4d6e"
|
|
||||||
integrity sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==
|
|
||||||
|
|
||||||
picocolors@^1, picocolors@^1.0.1, picocolors@^1.1.1:
|
picocolors@^1, picocolors@^1.0.1, picocolors@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz"
|
||||||
|
Loading…
Reference in New Issue
Block a user