I am having dificulties to do the following:
Having a job that runs from time to time (Done)
Fetching some data from database inside that job (Done)
Next step would be use that data with a template that I already created (HTML) and then render it to PDF
Then send an email with that pdf in attachment
This is my current job:
def execute() {
println("Hi, I am a schedule Job triggering every half an hour from 7 AM to 22 PM that will never stop running until master jose tells me to :)")
EventService.updateOffersdwh()
EventService.getUncheckedOffers()
}
which is working properly :)
Those are my service's methods called in the previous job:
def updateOffersdwh(){
def sql = Sql.newInstance("jdbc:postgresql://"+hostname+":5432/xyz", username, password, "org.postgresql.Driver")
sql.rows(" Select From bird_admin.updt_offerorders('2018-03-07','2018-03-07','" + database + "') ");
println('Updating Order and Offers datawarehouse...');
}
def getUncheckedOffers(){
def sql = sql.newInstance("jdbc:postgresql://"+hostname+":5432/xyz", username, password, "org.postgresql.Driver")
def query = "select id, poo_id, b.name, b.address_line1, b.address_line2, b.zipcode, event_date::date, task_name, sku_id, sku_name, column_type, column_value, instance_name,checked FROM bird_admin.ooisdwh a LEFT JOIN sd_bel.cfg_point_of_operation b ON a.poo_id = b.id WHERE checked = false"
def offers = sql.rows(query)
println('DEBUG OFFERS FINAL:: '+offers)
}
Can anyone please advice or guide me on the next steps?
How can I use a template in html that I already have in my views\template folder?
What is the best way to use the info from the database and render the html with that info?
Should I use a controller? If so, how? I'm kinda lost now :|
I'm not 100% sure if you want to use the template for the body of the email or the attachment, but to use a template for the body of the email you can do the following.
First add the dependency for the Grails mail plugin:
build.gradle
dependencies {
compile "org.grails.plugins:mail:2.0.0"
...
}
Then in your service generate the body using the template and send like:
class YourService {
def groovyPageRenderer
def mailService
def getUncheckedOffers(){
def sql = sql.newInstance("jdbc:postgresql://"+hostname+":5432/xyz", username, password, "org.postgresql.Driver")
def query = "select id, poo_id, b.name, b.address_line1, b.address_line2, b.zipcode, event_date::date, task_name, sku_id, sku_name, column_type, column_value, instance_name,checked FROM bird_admin.ooisdwh a LEFT JOIN sd_bel.cfg_point_of_operation b ON a.poo_id = b.id WHERE checked = false"
def offers = sql.rows(query)
def content = groovyPageRenderer.render( view: "/aViewDirectory/mail",
model:[offers : offers ] )
mailService.sendMail {
to 'anEmailRecipient#somewhere.com'
subject "Email subject"
html( content )
}
}
There's a section in the email plugin docs about attachments.
You can follow the steps above to generate a PDF from a template and attach as per the docs.
Related
email user specific developer jobs at a given location
import requests # for api
import smtplib # for emails
import auth
class Jobs:
URL = "https://jobs.github.com/positions" # base url for API
def _jobs_api(self): # get json data (jobs)
location = input("Where would you like to become a developer? \U0001f607\n") # location parameter
description = input("What type of developer are you interested in becoming? \U0001f608\n") # search term
response = requests.get(Jobs.URL,
headers={"Accept": "application/json"},
params={"location": location, "description": description} # query params
)
data = response.json()
return data
def _job_links(self):
data = self._jobs_api()
if data:
for job in data:
links = job['url']
return links
else:
print(f"Sorry, I was not able to find any {self.description} jobs in {self.location}...")
def send_jobs(self): # email auth
links = self._job_links()
smpt_object = smtplib.SMTP('smtp.gmail.com', 587)
smpt_object.ehlo()
smpt_object.starttls()
# use own authentication data
email = auth.email()
password = auth.password()
smpt_object.login(email, password)
# next, send the message
from_address = email
to_address = email
subject = "Developer Jobs"
message = "Here are some jobs you are looking for: {}".format(links)
msg = "Subject: "+subject+"\n"+message
print("Check your email for an update! \U0001f601")
smpt_object.sendmail(from_address, to_address, msg)
smpt_object.quit()
user = Jobs()
user.send_jobs()
I'm trying to use the line Here are some jobs you are looking for: {}".format(links) (or an f string preferably) to send the links found from the API. But, when I check my email, it doesn't show the message with the links.
You can try to send a html structured mail in python to send the url you want.
See this post to find out
I'm a complete novice with CRON jobs but I think I have that set up correctly.
Ultimately what I'm trying to do is send an email every day at 8:00 am to users (and a couple others) that have not logged in within the last 3 days, have not received the email, AND are marked as active OR temp as a status.
So from querying the db in console I know that I can do:
first = User.where(status: 'active').or(User.where(status: 'temp'))
second = first.where("last_login_at < ? ", Time.now-3.days)
third = second.where(notified: false)
That's not certainly clean but I was struggling with finding a contained query that grabbed all that data. Is there a cleaner way to do this query?
I believe I have my cron job set up correctly using a runner. I have whenever installed and in my schedule.rb I have:
every 1.day, at: '8:00 am' do
runner 'ReminderMailer.agent_mailer.deliver'
end
So under app > mailer I created ReminderMailer
class ReminderMailer < ApplicationMailer
helper ReminderHelper
def agent_reminder(user)
#user = user
mail(to: email_recipients(user), subject: 'This is your reminder')
end
def email_recipients(agent)
email_address = ''
email_addresses += agent.notification_emails + ',' if agent.notification_emails
email_addresses += agent.manager
email_address += agent.email
end
end
Where I'm actually struggling is where I should put my queries to send to the mailer, which is why I built a ReminderHelper.
module ReminderHelper
def applicable_agents(user)
agent = []
first = User.where(status: 'active').or(User.where(status: 'temp'))
second = first.where("last_login_at < ? ", Time.now-3.days)
third = second.where(notified: false)
agent << third
return agent
end
end
EDIT: So I know I could in theory do a chain of where queries. There's gotta be a better way right?
So what I need help on is: do I have the right structure in place? Is there a cleaner way to query this data in ActiveRecord for the CRON job? Is there a way to test this?
Try combining them together as if understand the conditions correct
Have not logged in within the last 3 days,
Have not received the email
Are marked as active OR temp as a status
User.where("last_login_at < ? ", 3.days.ago).
where(notified: false).
where(status: ['active', temp])
module ReminderHelper
def applicable_agents(user)
User.where("last_login_at < ? ", 3.days.ago).
where(notified: false).
where(status: ['active', temp])
end
end
You don't need to add/ assign them to array. Because this relation is already like an array. You can use .to_a if you need array. If you just want to iterate over them then users.each should work fine.
Update
class User
scope :not_notified, -> { where(notified: false) }
scope :active_or_temp, -> { where(status: ['active', 'temmp']) }
scope :last_login_in, -> (default_days = 3) { where("last_login_at < ?", default_days.days.ago) }
end
and then use
User.not_notified.active_or_temp.last_login_in(3)
Instead of Time.now-3.days it's better to use 3.days.ago because it keeps time zone also in consideration and avoids unnecessary troubles and failing test cases.
Additionally you can create small small scopes and combine them. More read on scopes https://guides.rubyonrails.org/active_record_querying.html
I am trying to change Spree 3.0 from_email
I added this line to my spree initialiser, but it does not work:
Spree::Store.current.mail_from_address = “x#x.com"
Do you know of any reason why not?
I also put it directly in my mailer decorator:
Spree::OrderMailer.class_eval do
def confirm_email_to_store(order, resend = false)
Spree::Store.current.mail_from_address = "x#x.com"
#order = order.respond_to?(:id) ? order : Spree::Order.find(order)
subject = (resend ? "[#{Spree.t(:resend).upcase}] " : '')
subject += "#{'Will Call' if #order.will_call} #{'Just to See' if #order.just_to_see} ##{#order.number}"
mail(to: ENV['STORE_EMAIL'], from: from_address, subject: subject)
end
end
This also did not work
Check you might have created multiple stores via checking Spree::Store.all
Also, spree use current store as store which updated last so you have to check that also
You can simply change the from email address in the Admin Panel under Configuration -> General settings:
Got it to work this way:
Spree::Store.current.update(mail_from_address: ENV["STORE_EMAIL"])
Looking here http://www.davidverhasselt.com/set-attributes-in-activerecord/ you can see that:
user.name = "Rob"
This regular assignment is the most common and easiest to use. It is
the default write accessor generated by Rails. The name attribute will
be marked as dirty and the change will not be sent to the database
yet.
Of course, spree initializers claims to do save in the databse, but it did not:
If a preference is set here it will be stored within the cache & database upon initialization.
Finally calling Spree::Store.current will pull from the database, so any unsaved changes will be lost:
scope :by_url, lambda { |url| where("url like ?", "%#{url}%") }
def self.current(domain = nil)
current_store = domain ? Store.by_url(domain).first : nil
current_store || Store.default
end
Fixing this bug in Spree would be prefrable, this is sort of a workaround
I want to exclude some fields in my inline based on my request user.
I know somehow I can handle this with methods like 'get_formsets', 'add_view', 'change_view', but I'm not sure what the syntax is.
Any suggestions?
I achieved what I needed with the next code in my inline class:
def get_formset(self, request, obj=None, **kwargs):
if request.user.groups.all().count() > 0:
if request.user.groups.all()[0].name == 'User Group Name':
kwargs['exclude'] = ['field_to_exclude',]
return super(MyInline, self).get_formset(request, obj, **kwargs)
The answer to this question gave me the hints: different fields for add and change pages in admin
There's also the get_exclude hook:
class FoodInline(TabularInline):
model = Food
def get_exclude(self, request, obj=None):
group = request.user.groups.first()
if group and group.name == 'User Group Name':
return ['field_to_exclude', ]
return self.exclude
I have 2 domain classes: Project and User.
Project hasMany on User via a SortedSet called allowedUsers. User does not belong to Project.
I want to find all Projects that a particular user is allowed to see. So trying syntax like:
Project.findAll{ it.allowedUsers.contains( userA ) }
Project.findAll{ userA in it.allowedUsers }
These dont work. And the find notation doesn't appear to support something like a ThatContains operator.
How can I achieve my aim?
Criteria should work, take a look at the "querying associations" section in http://grails.org/doc/latest/guide/GORM.html#criteria. Can you try the following:
def c = Project.createCriteria()
def results = c.list {
allowedUsers{
eq('id', userA.id)
}
}
You can also try where queries
def query = Project.where{
allowedUsers{id == userA.id}
}
def results = query.list()
or HQL
def query = """
select p from Project as p
inner join p.allowedUsers as user
where user.id = :user
"""
def results = Project.executeQuery(query, [user: userA.id])