Fix build.
[dcpomatic.git] / src / lib / analytics.cc
1 /*
2     Copyright (C) 2018 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 #include "analytics.h"
22 #include "exceptions.h"
23 #include "job.h"
24 #include <dcp/raw_convert.h>
25 #include <libcxml/cxml.h>
26 #include <libxml++/libxml++.h>
27 #include <boost/filesystem.hpp>
28 #include <boost/algorithm/string.hpp>
29 #include <boost/foreach.hpp>
30 #include <iostream>
31
32 #include "i18n.h"
33
34 using std::string;
35 using std::map;
36 using dcp::raw_convert;
37 using boost::algorithm::trim;
38 using boost::shared_ptr;
39
40 Analytics* Analytics::_instance;
41 int const Analytics::_current_version = 1;
42
43 Event::Event ()
44 {
45         gettimeofday (&_time, 0);
46 }
47
48 Event::Event (cxml::ConstNodePtr node)
49 {
50         _time.tv_sec = node->number_child<int64_t>("Time");
51         _time.tv_usec = 0;
52         BOOST_FOREACH (cxml::ConstNodePtr i, node->node_children()) {
53                 set(i->name(), i->content());
54         }
55 }
56
57 void
58 Event::set (string k, string v)
59 {
60         _data[k] = v;
61 }
62
63 void
64 Event::as_xml (xmlpp::Element* parent) const
65 {
66         /* It would be nice if this had timezone */
67         parent->add_child("Time")->add_child_text(raw_convert<string>(_time.tv_sec));
68         for (map<string, string>::const_iterator i = _data.begin(); i != _data.end(); ++i) {
69                 parent->add_child(i->first)->add_child_text(i->second);
70         }
71 }
72
73 string
74 Event::dump () const
75 {
76         string d;
77         d += raw_convert<string>(_time.tv_sec) + "\n";
78         for (map<string, string>::const_iterator i = _data.begin(); i != _data.end(); ++i) {
79                 d += i->first + ": " + i->second + "\n";
80         }
81         return d;
82 }
83
84 Analytics::Analytics ()
85 {
86
87 }
88
89 int
90 Analytics::successful_dcp_encodes () const
91 {
92         boost::mutex::scoped_lock lm (_mutex);
93         BOOST_FOREACH (Event e, _events) {
94                 std::cout << e.dump() << "\n";
95         }
96         return 0;
97 }
98
99 void
100 Analytics::job_state_changed (shared_ptr<Job> job)
101 {
102         Event ev;
103         ev.set ("type", "job-state");
104         ev.set ("json_name", job->json_name());
105         ev.set ("sub_name", job->sub_name());
106         ev.set ("error-summary", job->error_summary());
107         ev.set ("error-details", job->error_details());
108         ev.set ("status", job->json_status());
109
110         {
111                 boost::mutex::scoped_lock lm (_mutex);
112                 _events.push_back (ev);
113         }
114
115         write ();
116
117         if (successful_dcp_encodes() == 3) {
118                 emit (
119                         boost::bind(
120                                 boost::ref(Message),
121                                 _("Congratulations!"),
122                                 _(
123                                         "<h2>You have made 3 DCPs with DCP-o-matic!</h2>"
124                                         "<img width=\"20%\" src=\"memory:me.jpg\" align=\"center\">"
125                                         "<p>Hello. I'm Carl and I'm the "
126                                         "developer of DCP-o-matic. I work on it in my spare time (with the help "
127                                         "of a fine volunteer team of testers and translators) and I release it "
128                                         "as free software."
129
130                                         "<p>If you find DCP-o-matic useful, please consider a donation to the "
131                                         "project. Financial support will help me to spend more "
132                                         "time developing DCP-o-matic and making it better!"
133
134                                         "<p><ul>"
135                                         "<li><a href=\"https://dcpomatic.com/donate_amount?amount=40\">Go to Paypal to donate £40</a>"
136                                         "<li><a href=\"https://dcpomatic.com/donate_amount?amount=20\">Go to Paypal to donate £20</a>"
137                                         "<li><a href=\"https://dcpomatic.com/donate_amount?amount=10\">Go to Paypal to donate £10</a>"
138                                         "</ul>"
139
140                                         "<p>Thank you!"
141                                         )
142                                 )
143                         );
144         }
145 }
146
147 void
148 Analytics::write () const
149 {
150         xmlpp::Document doc;
151         xmlpp::Element* root = doc.create_root_node ("Analytics");
152
153         root->add_child("Version")->add_child_text(raw_convert<string>(_current_version));
154         boost::mutex::scoped_lock lm (_mutex);
155         BOOST_FOREACH (Event e, _events) {
156                 e.as_xml (root->add_child("Event"));
157         }
158
159         try {
160                 doc.write_to_file_formatted(path("analytics.xml").string());
161         } catch (xmlpp::exception& e) {
162                 string s = e.what ();
163                 trim (s);
164                 throw FileError (s, path("analytics.xml"));
165         }
166 }
167
168 void
169 Analytics::read ()
170 try
171 {
172         cxml::Document f ("Analytics");
173         f.read_file (path("analytics.xml"));
174         boost::mutex::scoped_lock lm (_mutex);
175         BOOST_FOREACH (cxml::ConstNodePtr i, f.node_children("Event")) {
176                 _events.push_back (Event(i));
177         }
178 } catch (...) {
179         /* Never mind */
180 }
181
182 Analytics*
183 Analytics::instance ()
184 {
185         if (!_instance) {
186                 _instance = new Analytics();
187                 _instance->read();
188         }
189
190         return _instance;
191 }